2. 移动端

144 阅读14分钟

基本概念

  • 物理像素(设备像素dp)
    • 分辨率:1792 X 828,表示纵向有1792个点,横向有828个点
    • 一个一个的点都是一个一个的物理像素(也可以叫做设备像素【dp】)
    • 实际开发不会以物理像素为准
  • css像素(逻辑像素 / 设备独立像素dip)
    • 实际开发中使用到的像素
    • 不管在标清屏下还是在高清屏下,css像素代表的大小是不变的,只不过用来显示css像素的物理点变多了,显示的也就更精细了
    • 只用知道要描述的东西是多少个像素,设备会自动转化成多少个物理像素点,不需要额外关心
    • 标清屏下:1css像素 == 1物理像素
    • 高清屏下:1css像素 == 4物理像素
    • 缩放改变的是css像素的大小
  • 设备像素比【dpr】
    • dpr = 设备像素 / css像素(缩放比是1的情况下)---指的是同一方向上的
    • dpr来区分标清屏和高清屏
    • 标清屏: dpr = 1
    • 高清屏: dpr > 1(通常dpr = 2)
    • 获取dpr
      • window.devicePixelRatio
  • 缩放
    • 改变的css像素的大小
  • PPI
    • 每英寸的物理像素点,或者叫dpi,可以理解为像素密度
  • 视口
    • 正常pc端页面的视口宽度都会大于手机屏幕的宽度,通常浏览器不会允许出现横向滚动条,所以当页面出现在手机端的时候,会先按照原视口宽度显示出页面,然后将视口缩放到手机上的可视视口宽度内,所以pc页面上的内容都会被缩小
    • 获取视口宽度
      • window.innerWidth
      • document.documentElement.clientWidth
      • document.documentElement.getBoundingClientRect().width

hybrid模式

  • 将开发的h5页面嵌入到native app的webview中运行(所谓的webview可以简单理解为一个浏览器,也是webkit内核)

  • h5和native app如何实现交互和通信

    • jsBridge: 在webview中注入一个对象,对象中包含我们需要调取的方法。那么在h5的js中,就可以调取webview中提供的方法。一般来说需要给方法传递一个callback,这样app就会在具体的某个阶段把callback执行从而实现对应的通信效果
    • 伪装的URL或者伪装的协议
      • h5开发人员和app人员协商一套协议或者伪装的url地址
      • app开发者要实现的是:
        • 具体业务操作的功能:如调取摄像头进行拍照
        • 劫持h5中所有发送的url地址,然后把符合事先规定规则url拦截下来,通过解析url后面的参数调取对应的功能
function fn(img){         
    //->打开摄像头拍照后执行的后续操作 img是拍下来的照片     
}     
window.location.href="zhufeng://phone?callback=fn";     
//->zhufeng:// 就是我们事先制定的一个假协议,所有这种协议的都代表需要调取App的某个功能     
//->phone 这个标识就是事先制定的需要调取拍照功能     
//->callback=fn 把自己JS中的某一个方法传递给App,App可以在拍照完成后执行这个方法,并且把保存的照片传递给这个方法(类似于JSONP) 

移动端常用meta标签

  • 声明viewport视口
  • 忽略数字自动识别为电话号码
    • 在IOS Safari(其他浏览器和Android均不会)上会对那些看起来像电话号码的数字处理为电话链接。比如:7位数字(1234567);带括号以及加号的数字,如(+86)123456789;双连接线的数字,如00-00-00111;11位数字,如12800128000可能还有其他类型的数字也会被识别,但在具体的业务场景下有些时候这是不必须的,所以可以关闭电话自动识别,然后再需要拨号的地方开启电话呼出和短信功能
    • 关闭电话自动识别 <meta name="format-detection" content="telephone=no"/>
    • 对于看起来像邮箱的也忽略自动识别 <meta name="format-detection" content="telephone=no,email=no"/> 但是邮箱一般都不忽略
    • 开启拨打电话/发送短信/发送短信功能
      • <a href="tel:123456">立即拨打电话</>
      • <a href="sms:123456">立即发送短信</a>
      • <a href="mailto:haha@163.com">发邮件</a>
  • 把页面增加到桌面主屏幕
    • 苹果safari浏览器中访问一个页面,用户可以通过“添加到桌面”这一操作把网页保存到自己的主屏幕桌面上(就像安装一个app,在主屏幕上就会有一个操作图标)这样下一次可以直接点击图标打开页面(只对ios有效)
    • webapp全屏模式
      • 当将一个网页添加到主屏幕,会希望他像app一样的表现,没有地址栏和状态栏全屏显示<meta name="apple-mobile-web-app-capable" content="yes"/>
    • 设置状态栏颜色
      • 只有在webapp开启全屏模式下才能有效果。content值为default(状态栏将为正常的,即白色,网页从状态栏以下开始显示)|black(状态栏为黑色,网页从状态栏以下开始显示)|black-translucent(状态栏为灰色半透明,网页将充满整个屏幕,状态栏会盖在网页之上)
      • <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"/>
    • 添加到主屏幕后的图标
      • <link href="图片的地址" sizes="114x114" rel="apple-touch-icon-precomposed"/>
      • ios系统中对ICON有一套规范,就是在ios设备的图标统一是:四边圆角 高光处理。至于图标阴影是ios系统统一为所有桌面元素增加的,所以不作为图标单独处理的样式。rel="apple-touch-icon-precomposed"是设定按照设计稿原图的图标显示;rel="apple-touch-icon"是设定在原图的基础上增加一些高光光亮的效果。一般来说icon的尺寸是114x114
    • 添加到主屏幕后的标题
      • <meta name='apple-mobile-web-app-title' content='标题'/>

其他一些移动端偶尔使用的meta标签

  • 添加智能App广告条Smart App Banner
    • <meta name="apple-itunes-app" content="app-id=myAppStoreID,affiliate-data="myAffiliate"/>
  • qq浏览器(x5内核)独有 的meta
    • <meta name="x5-orientation" content="portrait|landscape"/> 设置屏幕方向
    • <meta name="x5-fullscreen" content="true"/> 设置全屏
  • uc浏览器独有的meta
    • <meta name="screen-orientation" content="portrait|landscape"/>设置屏幕方向
    • <meta name="full-screen" content="true"/> 设置全屏
    • <meta name="viewport" content="uc-fitscreen=no|yes"/> 缩放不出现滚动条
    • <neta name="noghtmode" content="enable|disable"/> 夜间模式
    • 强制图片显示
      • uc浏览器为了节省流量,为用户提供了无图模式,但是如果页面的图片是必不可少的如验证码,需要强制浏览器显示图片。可以设置imagemode。通过meta设置图片加载方式会作用于整个页面。
      • <meta name="imagemode" content="force"/>
      • 如果希望对单个图片进行设置 可以使用<img src="" show="force"/>

响应式布局

  • 搭建一个h5页面,需要在head中添加一个meta标签
  • 要是做的网页不仅要在pc端运行,而且要为移动端所用,那么就要加上以下 标签:<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no">
  • js动态设置viewport
var oMeta = document.createElement('meta');
oMeta.name = 'viewport';
oMeta.content = 'width=device-width,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,user-scalable=no';
document.head.appenChild(oMeta);
  • viewport: 视口
    • width=device-width
      • 设置视口的宽度为设备的宽度 如果不设置 默认视口宽度为980px
      • 通常理解视口宽:当前的html页面浏览器会按照多大宽度来进行渲染
      • 也就是当前手机有多宽,浏览器就多宽
    • user-scalable=no
      • 禁止用户手动缩放
    • initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0
      • 设置屏幕的最大最小和默认缩放比例
      • 1.0 不进行任何缩放
  • 高清屏
    • 对于2倍高清屏,在手机上看到的100100的图片 其实是用200200的图片来渲染的。因为如果真实图片本身才100*100,最后呈现出来的是被拉伸后变模糊的效果
    • DPI适配思想
      • 做页面的时候 最好每一张素材图准备两套或者三套,如
      • logo.png 100*100
      • logo@2x.png 200*200
      • logo@3x.png 300*300
  • 媒体查询 @media
    • 媒体设备:all 所有设备 screen 所有屏幕设备 pc+移动端 print打印机设备...
    • 媒体条件: 指定在什么样的条件下执行对应的样式
      • @media all and (max-width:319px) 宽度小于320px
      • @media all and (min-width:320px) and (max-width: 359px) >=320 <360
.box {
    margin: 0 10px;
    height: 150px;
    background: url('banner.jpg') norepeat;
    background-size: cover;
}
@media all and (-webkit-device-pixel-ratio:2){
    .box{
        background: url('banner@2x.jpg') norepeat;
        background-size: cover;
    }
}
@media all and (-webkit-device-pixel-ratio:3){
    .box{
        background: url('banner@3x.jpg') norepeat;
        background-size: cover;
    }
}

苹果手机尺寸: 5s及以下都是320px 6是375px 6plus 414px

常用安卓机尺寸: 320 / 360/ 480/ 640/ 720 ...

真实项目中,设计稿一般是 640960 / 640 * 1136 / 7501334

响应式布局的解决方案

    1. 流式布局法
    • 容器或者盒子的宽度一般不写固定的值,而是使用百分比(相对于视口区域的百分比)
    • 其余的样式: 字体/高度/margin/padding等等都按照设计稿上标注尺寸的一半来设置
    • 对于有些屏幕尺寸下我们设置的固定值看起来不是特别好看的话,使用@media进行微调整
    • 特殊情况: 设计稿时640px,素材图就也是640px,这样的话6plus展示的时候,图片不够大,会变虚,对于这种情况需要单独找设计师要一张更大的图(本身尺寸*3)
      • @media all and (-webkit-min-device-pixel-ratio:2) and (min-width:321px) 这种情况下才显示更大的图
      • @media all and (min-width:641px) 屏幕本身宽度已经超过640了 也显示更大图(针对于安卓/平板)
  • rem响应式布局

    • rem: 当前页面中元素的rem单位的样式值都是针对于html元素的fontsize的值进行动态计算的
    • 应用:做的H5页面只在移动端访问(rem不兼容低版本的浏览器)
    • 1.设计稿640*1136
    • 2.在样式中给HTML设定一个fontsize,一般设置一个方便后面计算的值,例如:10px/100px... 之所以不用10px主要是浏览器最小的字体大小都是12px,用10px比例计算的结果和真实ui设计稿会存在一点偏差
    • 3.完全按照设计稿的尺寸来写样式,样式的值时像素值除以根元素fontsize计算出的rem。真是项目中外层盒子的宽度还是不写固定rem值,延用流式布局的思想
// 根据当前屏幕宽度和设计稿宽度的比例,动态设置当前宽度下的fontsize,如果fontsize值改变了,之前设定的所有rem单位的值自动会跟着放大或者缩小
~function(){
    var desW = 750; // 使用的设计稿宽度
    var winW = document.documentElement.clientWidth;
    var ratio = winW / desW;
    document.documentElement.style.fontSize = ratio*100 + 'px';
}()
(function(){
    setRemUnit();
    window.addEventListener('resize', serRemUnit);
    
    function setRemUnit(){
        var docEl = document.documentElement;
        var ratio = 18.75; // 750设计稿 1rem = 40px 750 / 40 = 18.75
        var viewWidth = docEl.getBoundingClientRect().width || window.innerWidth;
        docEl.fontSize = viewWidth / ratio + 'px'
    }
})()
通用适配方案
(function(){
     var docEl = document.documentElement,
         viewportEl = document.querySelector(‘meta[name=‘viewport']') ,
         dpr = window.devicePixelRatio || 1,
         maxWidth = 540,
         minWidth = 320;
    dpr = dpr >= 3? 3 : (dpr >= 2? 2 : 1) ;
    docEl.setAttribute(‘data-dpr’,dpr);
    docEl.setAttribute(‘max-width’, maxWidth);
    docEl.setAttribute(‘min-width’, minWidth);
    var scale = 1 / dpr,
        content = ‘width=device-width,initial-scale=‘ + scale + ‘maximum-scale=‘ + scale + ‘minimum-scale=‘ + scale + ‘user-scalable=no’;
    If(viewportEl){
        viewportEl.serAttribute(‘content’, content)
    }else{
        viewportEl = document.createElement(‘meta’);
        viewportEl.setAttribute(’name’, ‘viewport’);
        viewportEl.setAttribute(‘content’, content);
        Document.head.appendChild(viewportEl);
    }
    setRemUnit();
    window.addEventListener(‘resize’, setRemUnit);
    function setRemUnit(){
        var ratio =18.75;
        var viewWidth = docEl.getBoundingClientRect().width || window.innerWidth;
        If(maxWidth && (viewWidth /dpr > maxWidth)){
            viewWidth = maxWidth * dpr;
        }else if(minWidth && (viewWidth / dpr < minWidth)){
            viewWidth = minWidth * dpr;
        }        
        docEl.style.fontSize = viewWidth / ratio + ‘px’; 
    } 
})()
  • 当屏幕宽度大于设计稿宽度时,就让显示的宽度等于640,不再等比例缩放,剩余的部分留白
把写的内容全部放到section标签当中
<section className="main">
    <div className="box"></div>
</section>

~function(){
    var desW = 750; // 使用的设计稿宽度
    var winW = document.documentElement.clientWidth;
    var ratio = winW / desW;
    var oMain = document.fetElementById('main');
    if(winW > desW){
        oMain.style.width = desW + 'px';
        oMain.style.margin = '0 auto';
        return;
    }
    document.documentElement.style.fontSize = ratio*100 + 'px';
}()
  • 另一种做法是:检测当前的浏览器是pc端还是移动端
    • 如果当前浏览器是pc端,但是访问的页面是移动端页面,让其跳转到pc端的页面
    • 如果当前浏览器是移动端的,但是我们访问的是pc端的页面,让其跳转到移动端页面
    • 跳转: window.location.href = 'xxx';
    • navigator.userAgent
~function(){
    var reg1 = /APPLEWebKit.*Mobile/i;
    var reg2 = /MIDP|SymbianOS|NOKIA|SAMSUNG|LG|NEC|TCL|Alcatel|BIRD|DBTEL|Dopod|PHILIPS|HAIER|LENOVO|MOT-|Nokia|SonyEricsson|SIE-|Amoi|ZTE/;
    // 条件成立说明当前页面运行在移动设备中
    if(reg1.test(navigator.userAgent) || reg2.test(navigator.userAgent)){
        // 如果当前页面是pc端项目 需要跳转到移动端项目
        if(window.location.href.indexOf('www.zhuzhu.cn')>=0){
            window.location.href = 'http://phone.zhuzhu.cn'
        }
        return;
    }
    // 反之说明当前页面运行在pc端设备上,如果访问的地址是移动端的,需要跳转到pc端的地址
    if(window.location.href.indexOf('phone.zhuzhu.cn')>=0){
        window.location.href = 'http://www.zhuzhu.cn'
    }
}()

移动端事件

  • TOUCH事件模型 处理单手指操作
    • touchstart touchmove touchend touchcancle
  • GESTURE事件模型 处理多手指操作
    • gesturestart geturechange gestureend
  • 移动端其他事件: click,load, scroll, blur, focus,change, input(代替keydown/keyup)
  • click
    • 在移动端click属于单击事件,不是点击事件;在移动端的项目中经常会区分单击做什么和双击做什么,所以移动端的浏览器在识别click的时候,只有确定是单击才会执行。
    • 在移动端使用click会存在300ms的延迟:浏览器在第一次点击结束后,还需要等到300ma看是否触发了第二次点击,如果触发了第二次点击就不属于click了,没有触发第二次点击才属于click
    • 使用touch事件模型实现点击操作(单击/双击),解决click在移动端的300ms延迟问题
      • changedTouches和touches都是手指信息的集合(TouchList),touches获取到值的必备条件只有手指还在屏幕上才可以获取,所以在touchend事件中如果想获取离开的瞬间坐标只能使用changedTouches获取
      • 在touchstart事件中记录起始的手指坐标
    • fastclick的原理就是这样
      • fastClick.attach(document.body)
      • 利用事件委托,给document.body绑定了rouchstart/touchmove/touchend绑定事件
function on(dom, name, fn) {
    dom.addEventListener(name, fn);
}
on(oBox, 'touchstart', function(ev){
    // this指向dom元素
    this['strX'] = ev.touches[0].clientX;
    this['strY'] = ev.touches[0].clientY;
    this['isMove'] = false;
})

on(oBox, 'touchmove', function(ev){
    var point = ev.touches[0];
    var newX = point.clientX;
    var newY = point.clientY;
    // 判断是否发生滑动 需要判断偏移的值是否在30px以内
    if(Math.abs(newX - this['strX'])>30 || Math.abs(newY - this['strY']) > 30){
        this['isMove'] = true;
    }
})
on(oBox, 'touchend', function(ev){
    if(this['isMove'] === false){
        // 点击
        // 执行点击的相关操作
        this.style.webkitTransitionDuration = '1s';
        this.style.webkitTransform = 'rotate(360deg)'
        var delayTimer = window.setTimeout(function(){
            this.style.webkitTransformDuration = '0s'; // 转回去不要动画效果
            this.style.webkitTransform = 'rotate(0deg)';
        }.bind(this), 1000);
    }else{
        // 滑动
    }
})
  • 点击、单击、双击、长按、滑动、左右上下滑动...

    • 单击和双击 300ms
    • 点击和长按 750ms
    • 点击和滑动 x/y轴偏移的距离是否在30px以内 超过30px就是滑动
    • 左右上下滑动 比较x和y的偏移量大小
  • 移动端常用的事件库

    • fastclick 解决click300ms的延迟
    • TOUCH.JS 百度云手势事件库
    • HAMMER.JS
    • Zepto.js:移动端的小型jq, jq由于是在pc端使用的,所以代码包含了大量的对于ie低版本浏览器的兼容处理,而Zepto应用于移动端开发,在jq的基础上没有对低版本ie进行支持
      • jq中提供了很多的选择器类型和dom操作方法,但是zepto中只是实现了部分常用的选择器和方法

      • zepto专门准备了移动端常用的事件操作:tap,singleTap,doubleTap,longTap,swipe,swipeUp,swipeDown,swipeLeft,swipeRight...

  • 移动端android/ios低版本不兼容position:fixed

    • 尤其是在定位的区域还需要输入内容调取虚拟键盘的时候会出现很多问题,所以不能使用固定定位,而使用局部滚动处理(iscroll)
    • 给局部区域滚动,首先要给区域固定高度
    • 在数据绑定完成之后实现局部滚动

    iscroll的滚动效果都是通过css3动画来实现的

    var myScroll(实例) = new IScroll(需要局部滚动的区域,{参数配置})

    初始化成功后,只对当前容器中的第一个子元素实现了滚动操作 参数

    scrollbars:true 显示滚动条,默认是不显示的(开启后,iscroll会默认的向content区域增加一个iScrollVerticalScrollbarDIV,这个div控制的是滚动条,它的样式是position:absolute; 所以如果只想让滚动条在容器中,需要给当前的滚动条区域容器(content)增加position:relative;)

    mouseWheel:true 设置支持鼠标滚轮滚动

    bounce: false 禁止运动到边界后反弹

    fadeScrollbars:true 滚动的时候才显示滚动条 否则不显示,如果想改变滚动条的样式,可以自定义 .iScrollIndicator{opacity: 0.2}

    click:true ISCROLL为了防止滑动过程中的误操作,默认是把click禁止的,如果容器里面的容器需要点击,则开启click即可 方法

    myScroll.refresh() 当滚动区域的内容发生变化的时候,让当前的实例刷新一下,滚动区域的相关值都会跟着重新计算

    myScroll.scrollToElement([ele], [time]) 用多长时间滚动到具体的某一个元素位置

    myScroll.scrollTo(x,y,time) 滚动到具体的坐标位置

    滚动到底部:var li = $content.find('li'); myScroll.scrollToElement(li[li.length-1], 300);

  • 1px边框