美文网首页
移动端页面(待更新)

移动端页面(待更新)

作者: 赵任QAQ | 来源:发表于2018-08-14 10:19 被阅读0次

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

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为单位

相关文章

  • 移动端页面(待更新)

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

  • 判断PC端或者移动端 跳转对应页面

    移动端页面写法 PC端页面写法

  • 移动端页面

    手机和PC端交互方式不一样,移动端多数没有hover,没有resize,没有滚动条,有touch 1、媒体查询 在...

  • 移动端页面

    媒体查询 第一种方式: 直接在head里写style 比如: 第二种方式:引入CSS文件 上面这行代码表示 : ...

  • 移动端页面

    1、viewport声明的意义 在head中添加以下标签,让手机浏览器以device-width的宽度为初始包含块...

  • 移动端页面

  • 移动端页面

    开发移动端的网页,需要提前准备一些东西 在index.html文件中添加meta标签做移动端适配 引入reset....

  • 移动端web页面适配

    移动端Web页面,即常说的H5页面、手机页面、webview页面等。 手机设备屏幕尺寸不一,在做移动端的Web页面...

  • React-Native实践总结

    React-Native分析 RN的优点: 页面热更新 RN页面不需要移动端发版本 纯web思维,开发速度快,且体...

  • 移动端

    app Web App Hybird App 移动端页面 设计图 测试 移动端的设置 移动端的布局 弹性 fl...

网友评论

      本文标题:移动端页面(待更新)

      本文链接:https://www.haomeiwen.com/subject/lvrmbftx.html