移动端页面(待更新)

超出部分隐藏,横向滑动的菜单栏

overflow-x: auto; //如果溢出框,则应该提供滚动机制。
white-space: nowrap; //规定段落中的文本不进行换行。

==========================================================

切换筛选菜单


筛选菜单1.png

筛选菜单2.png
<template>
<div id="sort-and-zone">
    <div id="sort" @click="sortOrZone(0)"><span>{{sortSpan}}</span><div class="nav-triangle"></div></div>
    <div id="zone" @click="sortOrZone(1)"><span>{{zoneSpan}}</span><div class="nav-triangle zone-triangle"></div>
</div>
<div v-show="isShadow" id="sort-and-zone-shadow" @click="hideShadow">
    <div id="sort-options" v-show="isSort">
        <ul class="sorts" v-for="item in sorts" :key="item.index">
             <li><span>{{item.name}}</span></li>
        </ul>
    </div>
    <div id="zone-options" v-show="!isSort">
        <ul class="zones" v-for="item in zones" :key="item.index">
             <li><span>{{item.name}}</span></li>
        </ul>
    </div>
</div>
</template>

<script>
export default {
    data() {
        return {
            sortSpan: "最新帖子",
            zoneSpan: "全部分区",
            isShadow: false,
            sortOrZoneFlag: -1,
            isSort: true,
            sorts: [
                {index: 6, name: "最新帖子", param: "last"},
                {index: 7, name: "最热帖子", param: "hot"},
                {index: 8, name: "最热帖子", param: "hot"},
                {index: 9, name: "最热帖子", param: "hot"}
            ],
            zones: [
                {index: 0, name: "全部分区", param: "all"},
                {index: 1, name: "沿途风光", param: "scenery"},
                {index: 2, name: "沿途风光", param: "scenery"},
                {index: 3, name: "沿途风光", param: "scenery"},
                {index: 4, name: "沿途风光", param: "scenery"},
                {index: 5, name: "沿途风光", param: "scenery"},
                {index: 15, name: "沿途风光", param: "scenery"},
                {index: 25, name: "沿途风光", param: "scenery"},
                {index: 35, name: "沿途风光", param: "scenery"},
                {index: 45, name: "沿途风光", param: "scenery"},
                {index: 55, name: "沿途风光", param: "scenery"},
                {index: 65, name: "沿途风光", param: "scenery"}
            ]
        }
    },
    methods: {
         sortOrZone: function(flag) {
            var _this = this;
            if(_this.sortOrZoneFlag == -1) { //初始状态,没有展开菜单
                _this.sortOrZoneFlag = flag; //记录当前菜单对应的按钮
                _this.isShadow = !_this.isShadow; //展开菜单,展示相应的菜单内容
                if(flag == 0) {
                    _this.isSort = true;
                }else {
                    _this.isSort = false;
                }
                $("html").css("overflow", "hidden");//PC端这样设置可以使得页面在遮罩层出现时禁止滚动,但是移动端无效
                $("body").css("overflow", "hidden");
                $('#sort-and-zone-shadow').bind("touchmove",function(e){//该方法为遮罩层添加滑动时的事件
                    e.preventDefault();//preventDefault()取消默认事件,来达到遮罩层出现时禁止滑动的效果
                });
            }else if(_this.sortOrZoneFlag == flag) { //该按钮展开了菜单,现在应该收起菜单
                _this.isShadow = !_this.isShadow; //收起菜单
                _this.sortOrZoneFlag = -1; //回到初始状态
                $("html").css("overflow", "visible");
                $("body").css("overflow", "visible");
            }else { //另一个按钮被点击,应保持菜单展开状态,更换相应的菜单内容
                _this.sortOrZoneFlag = flag; //更改当前菜单对应的按钮
                _this.isSort = !_this.isSort
                $("html").css("overflow", "hidden");
                $("body").css("overflow", "hidden");
                $('#sort-and-zone-shadow').bind("touchmove",function(e){
                    e.preventDefault();
                });
            }
         },
         hideShadow: function() {
             var _this = this;
             _this.isShadow = false; //收起菜单
            _this.sortOrZoneFlag = -1; //回到初始状态
            $("html").css("overflow", "visible");
            $("body").css("overflow", "visible");
         }
    }
}
</script>

==========================================================

类似的小标签通过给<span>添加背景颜色实现,或者给<span>添加边框样式


帖子小标签.png

==========================================================

两种盒模型的区别

1.box-sizing: content-box:
W3C标准盒模型
盒子所占空间的宽度 = content.width+padding×2+border×2+margin×2
盒子的实际宽度 = content.width+padding×2+border×2

2.box-sizing: border-box:
IE的盒模型
盒子所占空间的宽度 = content.width+margin×2
盒子的实际宽度 = content.width

3.区别:
在使用W3C标准盒模型时,定义宽度只是对content的定义,对padding、border、margin的定义是额外加上去的,它们都会改变盒子的实际大小以及所占空间。在使用IE盒模型时,定义的宽度是包含了padding和border的,对padding、border、margin的定义都不会改变盒子的实际大小,但是对margin的定义会改变盒子所占空间的大小,而对padding和border的定义不会。
==========================================================

在有遮罩层出现时,禁止对遮罩层以下的内容进行操作(PC端滚动,移动端滑动)
1.PC端
将超出的部分隐藏,设置overflow为hidden
2.移动端
阻止遮罩层的默认事件

$("遮罩层").bind("touchmove", function(e) {
    e.preventDefault();
})

==========================================================

下拉刷新
jQuery插件(pulltorefresh.js)

var ptr =  PullToRefresh.init({
    mainElement: "选择下拉的element",
    onRefresh: function() {
        window.location.reload();
    }
});

==========================================================

返回上一页
在index.js中使用keepAlive使组件保留状态,避免重新渲染。
在二级页面使用history.go(-1)或者history.back()来返回上一个页面。
history.go(-1)会使得要返回的页面刷新,而history.back()不会刷新。
以上为理论,下面是实际使用的结果:
我在index.js中为index.vue组件设置了

{
    path: '/',
    name: 'index',
    component: 'index',
    meta: {
        keepAlive: true
    }
}

之后,再使用history.back()和history.go(-1)返回index页面,index组件都没有重新渲染。而在不设
置keepAlive属性时,返回index页面,组件都重新渲染了,页面被刷新了。
==========================================================

vue组件或者元素的过渡
使用transition来实现元素或组件的过渡。
一个通用的例子,实现简单地过渡。

<transition name="fade">
    <div v-show="isIsSignBoardShow" id="is-sign-board">今天已签到</div>
</transition>
.fade-enter-active, .fade-leave-active {
  transition: opacity .5s;
}
.fade-enter, .fade-leave-to /* .fade-leave-active below version 2.1.8   #5086A5      */ {
  opacity: 0;
}

==========================================================

vue路由设置页面不刷新
在router.js中把需要缓存的页面设置为:

{
    path: "/",
    name: "index",
    component: index,
    meta: {
        keepAlive: true
    }
}

在App.vue中,需要缓存的页面这样写:

<keep-alive>
    <router-view v-if="$route.meta.keepAlive" v-on:isMenuNeed="isMenuNeed" v-on:needReload="reload"></router-view>
</keep-alive>
<router-view v-if="!$route.meta.keepAlive" v-on:isMenuNeed="isMenuNeed" v-on:needReload="reload"></router-view>

在需要返回的页面,用history.back()返回。(返回的两种方式之前写过了)

==========================================================

返回时定位到之前离开的位置
本次使用的是根据滚动条的高度来对页面进行定位。
在跳转页面的额函数中:

var scrollH = $("滚动的元素").scrollTop();//获取当前滚动条到屏幕上方的距离
sessionStorage.setItem("h", scrollH);//将当前滚动条到屏幕上方的距离存入sessionStorage
//在返回的页面中的activated函数中:(activated函数是在keep-alive 组件激活时调用的)
var h = sessionStorage.getItem("h");//从sessionStorage中取出离开时的滚动条到屏幕上方的距离
$("#test-wrap").scrollTop(h);//设置滚动条到屏幕上方的距离来定位页面位置
//在返回的页面中的created函数中:
sessionStorage.setItem("h", 0);//将sessionStorage存储的滚动条到屏幕上方的距离改为0,这样在页面刷新的时候页面就会回到最上方。

还有一种利用锚点来定位,个人感觉在本次的页面中不太好利用,因此没有使用。

==========================================================

复杂的布局和定位灵活运用position属性:
relative属性的元素初始位置是一直被占据的,而absolute属性的元素不会。
relative属性的元素定位是相对初始位置定位的,而absolute属性的元素是相对它父元素中拥有position属性的元素定位的,如果父元素中没有就相对body定位。
在复杂的布局定位中可以给父元素设置position: relative,子元素设置position: absolute,这样子元素就可以被设置到想要的想对于父元素的任意位置了。

==========================================================

下拉刷新和上滑加载更多
需要引用刷新的组件(父组件)

<template>
    <v-load id="test-wrap" :on-refresh="onRefresh" :on-infinite="onInfinite" :tPage="page">//onRefresh是下拉刷新,onInfinite是上滑加载更多,可根据需求取舍。把当前的页数传到子组件。
        <card v-for="card in cards" :key="card.id"></card>
    </v-load>
</template>
<script>
    import card from "components/card.vue";
    import load from "components/load.vue";//引入组件
    export default {
        data() {
            return {
                page: 0
            }
        }
    components: {
        vLoad: load//注册组件
    },
    methods: {
        // 下拉刷新
        onRefresh(done) {
        window.location.reload();
        done(); //加载完成,防止重复执行
        },
        // 上滑加载更多
        onInfinite(done) {
            // window.location.reload();
        var _this = this;
        if(_this.page == 6) {
            done();
        }else {
            setTimeout(function() {
            _this.addMore();
            _this.page += 1;
            done();
            }, 1000);//设置一秒钟之后是为了方便观察效果
        }
        }
    }
    }
</script>
load.vue
<template>
  <div class="yo-scroll"
    :class="{'down':(state===0),'up':(state==1),refresh:(state===2),touch:touching}"
    @touchstart="touchStart($event)"
    @touchmove="touchMove($event)"
    @touchend="touchEnd($event)"
    @scroll="(onInfinite || infiniteLoading) ? onScroll($event) : undefined">
    <section class="inner" :style="{ transform: 'translate3d(0, ' + top + 'px, 0)' }">
      <header class="pull-refresh">
        <slot name="pull-refresh">
           <span class="down-tip">下拉更新</span>
           <span class="up-tip">松开更新</span>
           <span class="refresh-tip">更新中</span>
        </slot>
      </header>
      <slot></slot>
      <footer class="load-more">
        <slot name="load-more">
          <span v-show="!isAll">加载中……</span>
          <span v-show="isAll">加载完毕</span>
        </slot>
      </footer>
    </section>
  </div>
</template>
<script>
export default {
  props: {
    offset: {
      type: Number,
      default: 40
    },
    enableInfinite: {
      type: Boolean,
      default: true
    },
    enableRefresh: {
      type: Boolean,
      default: true
    },
    onRefresh: {
      type: Function,
      default: undefined,
      required: false
    },
    onInfinite: {
      type: Function,
      default: undefined,
      require: false
    },
    tPage: {
      type: Number//接收到当前页数
    }
  },
  data() {
    return {
      top: 0,
      state: 0,
      startY: 0,
      touching: false,
      infiniteLoading: false,
      isAll: false
    }
  },
  methods: {
    touchStart(e) {
      this.startY = e.targetTouches[0].pageY
      this.startScroll = this.$el.scrollTop || 0
      this.touching = true
    },
    touchMove(e) {
      if (!this.enableRefresh || this.$el.scrollTop > 0 || !this.touching) {
        return
      }
      let diff = e.targetTouches[0].pageY - this.startY - this.startScroll
      if (diff > 0) e.preventDefault()
      this.top = Math.pow(diff, 0.8) + (this.state === 2 ? this.offset : 0)

      if (this.state === 2) { // in refreshing
        return
      }
      if (this.top >= this.offset) {
        this.state = 1
      } else {
        this.state = 0
      }
    },
    touchEnd(e) {
      if (!this.enableRefresh) return
      this.touching = false
      if (this.state === 2) { // in refreshing
        this.state = 2
        this.top = this.offset
        return
      }
      if (this.top >= this.offset) { // do refresh
        this.refresh()
      } else { // cancel refresh
        this.state = 0
        this.top = 0
      }
    },
    refresh() {
      this.state = 2
      this.top = this.offset
      this.onRefresh(this.refreshDone)
    },
    refreshDone() {
      this.state = 0
      this.top = 0
    },

    infinite() {
      this.infiniteLoading = true
      this.onInfinite(this.infiniteDone)
    },

    infiniteDone() {
      this.infiniteLoading = false
    },

    onScroll(e) {
      if (!this.enableInfinite || this.infiniteLoading) {
        return
      }
      let outerHeight = this.$el.clientHeight
      let innerHeight = this.$el.querySelector('.inner').clientHeight
      let scrollTop = this.$el.scrollTop
      let ptrHeight = this.onRefresh ? this.$el.querySelector('.pull-refresh').clientHeight : 0
      let infiniteHeight = this.$el.querySelector('.load-more').clientHeight
      let bottom = innerHeight - outerHeight - scrollTop - ptrHeight
      if (bottom < -80) this.infinite()
    }
  },
  //当数据发生变化时执行(vue生命周期中的钩子函数updated)
  updated: function() {//这里把总页数设置为6,判断是否加载完毕
    if(this.tPage == 6) {
      this.isAll = true;
    }
  }
}
</script>
<style scoped>
.yo-scroll {
  position: absolute;
  top: 2.5rem;
  right: 0;
  bottom: 0;
  left: 0;
  overflow: auto;
  -webkit-overflow-scrolling: touch;
  background-color: #f9f9f9;
}
.yo-scroll .inner {
  position: absolute;
  top: -2rem;
  width: 100%;
  margin-bottom: 55px;
  margin-top: 50px;
  transition-duration: 300ms;
  /* background-color: rgb(228, 29, 29); */
}
.yo-scroll .pull-refresh {
  position: relative;
  left: 0;
  top: 0;
  width: 100%;
  height: 2rem;
  display: flex;
  align-items: center;
  justify-content: center;
}
.yo-scroll.touch .inner {
  transition-duration: 0ms;
}
.yo-scroll.down .down-tip {
  display: block;
}
.yo-scroll.up .up-tip {
  display: block;
}
.yo-scroll.refresh .refresh-tip {
  display: block;
}
.yo-scroll .down-tip,
.yo-scroll .refresh-tip,
.yo-scroll .up-tip {
  display: none;
}
.yo-scroll .load-more {
  height: 3rem;
  display: flex;
  align-items: center;
  justify-content: center;
}  
</style>

==========================================================

H5页面适配

解决div在不同尺寸屏幕上等比缩放的问题:

//将viewport的宽度设置为设备屏幕的宽度,同时不允许用户手动缩放
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
var deviceWidth = document.documentElement.clientWidth;//获取设备屏幕宽度
document.documentElement.style.fontSize = deviceWidth+"px";//设置HTML的fontSize
@function calc(\$val) {
    @return $val/1080;
}
.logo {
    width: calc(180rem);
}

解决1px的问题:
先将整个页面缩小dpr倍,再把跟字体大小放大dpr倍。(页面整体需要采用rem单位)

解决横屏问题:
横屏时让width=height的效果是最好的。

var deviceWidth = document.documentElement.clientWidth;
var deviceHeight = document.documentElement.clientHeight;
if(window.orientation === 90 || window.orientation == -90) {
    deviceWidth = deviceHeight;
}
document.documentElement.style.fontSize = deviceWidth + 'px';
判断手机横竖屏的方法:
if (window.orientation === 180 || window.orientation === 0) { 
    console.log('竖屏状态!');
}
if (window.orientation === 90 || window.orientation === -90 ){ 
    console.log('横屏状态!');
}

在同一个CSS中两套样式:

@media screen and (orientation: portrait) {
  /*竖屏 css*/    
} 
@media screen and (orientation: landscape) {
  /*横屏 css*/
}

在link中筛选样式

<!-- 竖屏 -->
<link rel="stylesheet" media="all and (orientation:portrait)" href="portrait.css">
<!-- 竖屏 -->
<link rel="stylesheet" media="all and (orientation:landscape)" href="landscape.css">

H5适配方案:

html:
<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width">
</head>
<body>
    <!-- 正文 -->
</body>
</html>
js:
/**
 * @DESCRIPTION 移动端页面适配解决方案 v1.0
 * @AUTHOR      Night
 * @DATE        2018年08月01日
 */
(function(window, document){
    var docEle = document.documentElement,
        dpr    = window.devicePixelRatio || 1,
        scale  = 1 / dpr;
    
    var fontSizeRadio = 1, //手机字体正常比例
        isLandscape   = false;//是否横屏
    
    ///////////////////////// viewport start //////////////////////////////////
    
    //设置页面缩放比例并禁止用户手动缩放
    document.getElementsByName('viewport')[0].setAttribute('content','width=device-width,initial-scale='+scale+',maximum-scale='+scale+',minimum-scale='+scale+',user-scalable=no');
    
    ///////////////////////// viewport end //////////////////////////////////
    
    //横屏状态检测
    if (window.orientation === 90 || window.orientation === -90) {
        isLandscape = true;
    };

    ///////////////////// system font-size check start //////////////////////
    
    //试探字体大小,用于检测系统字体是否正常
    var setFz = '100px';

    //给head增加一个隐藏元素
    var headEle = document.getElementsByTagName('head')[0],
        spanEle = document.createElement('span');
        spanEle.style.fontSize = setFz;
        spanEle.style.display = 'none';
        headEle.appendChild(spanEle);

    //判断元素真实的字体大小是否setFz
    //如果不相等则获取真实的字体换算比例
    var realFz = getComputedStyle(headEle).getPropertyValue('font-size');

    if(setFz !== 'realFz'){
        //去掉单位px,下面要参与计算
        setFz = parseFloat(setFz);
        realFz = parseFloat(realFz);

        //获取字体换算比例
        fontSizeRadio = setFz / realFz;
    };
    
    ///////////////////// system font-size check end //////////////////////
    
    var setBaseFontSize = function(){
        var deviceWidth = docEle.clientWidth,
            deviceHeight= docEle.clientHeight;
        
        if(isLandscape){
            deviceWidth = deviceHeight;
        };
        
        docEle.style.fontSize = deviceWidth * fontSizeRadio + 'px';
    };
    setBaseFontSize();
    
    //页面发生变化时重置font-size
    //防止多个事件重复执行,增加延迟300ms操作(防抖)
    var tid;
    window.addEventListener('resize', function() {
        clearTimeout(tid);
        tid = setTimeout(setBaseFontSize, 300);
    }, false);
    window.addEventListener('pageshow', function(e) {
        if (e.persisted) {
            clearTimeout(tid);
            tid = setTimeout(setBaseFontSize, 300);
        };
    }, false);
    
})(window, document);
scss:
//设计稿尺寸大小,假如设计稿宽度750
$baseDesignWidth = 750;

@function calc($val){
    @return $val / $baseDesignWidth;
}

//适配元素采用rem,假如设计稿中元素宽度180
.logo{
    width : calc(180rem);
}

//边框采用px,假如设计稿边框宽度1px
.box{
    border : 1px solid #ddd;
}

作者:vivo丨sunmaobin
链接:https://juejin.im/post/5b6503dee51d45191e0d30d2
来源:掘金

关于字体大小问题:
PC端文字的最小尺寸为12px
移动端文字最小尺寸为8px
如果文字的尺寸小于最小尺寸则显示为最小尺寸
文字大小用最好以px为单位

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,133评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,682评论 3 390
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,784评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,508评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,603评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,607评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,604评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,359评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,805评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,121评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,280评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,959评论 5 339
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,588评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,206评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,442评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,193评论 2 367
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,144评论 2 352

推荐阅读更多精彩内容

  • 问答题47 /72 常见浏览器兼容性问题与解决方案? 参考答案 (1)浏览器兼容问题一:不同浏览器的标签默认的外补...
    _Yfling阅读 13,747评论 1 92
  • 移动开发基本知识点 一.使用rem作为单位 html { font-size: 100px; } @media(m...
    横冲直撞666阅读 3,462评论 0 6
  • 1. 前言 前端圈有个“梗”:在面试时,问个css的position属性能刷掉一半人,其中不乏工作四五年的同学。在...
    YjWorld阅读 4,438评论 5 15
  • H5移动端知识点总结 阅读目录 移动开发基本知识点 calc基本用法 box-sizing的理解及使用 理解dis...
    Mx勇阅读 4,477评论 0 26
  • 无基础
    WhaleFallsL阅读 166评论 0 0