移动端适配之懒适配

关于移动端的适配方案,现在其实已经有很多了,什么百分比、font-size+rem、视窗单位(vw、vh)等等,在介绍懒适配之前,先说说我常用的百分比吧。

百分比布局

  元素的size:页面上的元素的width都使用百分比来实现,比如一行三列,每列就是33.33%,高度可以基于padding-bottom来实现,也可以让内部元素来支撑,这个主要看需求。

  字体大小:这个一般使用px,根据设计图来进行修改,最小12px

  整体页面:设置一个max-width,然后居中显示

  上面差不多就是百分比布局的一些要点,这其中有关size的都需要根据设计图来缩放,计算量还是挺大的,主要是太麻烦。。

灵感来源

  懒适配的灵感来源是看了淘宝的适配方案,使用viewport来对页面进行缩放,但淘宝的适配不仅仅依赖于此,在这里就不展开了。

viewport

  <meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no,target-densitydpi=device-dpi" />

  解释一下上面这个viewport的作用吧,width=device-width,视窗的width为设备的width。initial-scale=1,初始化的缩放为1(相当于什么都不干)。user-scalable=no,禁止用户手动去缩放(在某些安卓上没有作业)。target-densitydpi=device-dpi,去兼容安卓低版本不支持缩放的问题。

思路

  使用viewport来进行缩放,缩放的计算公式,设备的可视宽度/设计图的宽度,还有一些兼容和web的处理移动端需要使用meta来缩放,web端需要使用zoom来缩放

  移动端使用meta来进行缩放,因为考虑到需要适配web端,所以必须使用zoom来兼容

  页面所有元素都需要按照设计图来固定size,因为缩放也是安装设计图来进行缩放的

  在开发的过程中遇到android4.4以下和魅族的某些手机使用meta缩放有一些问题,所以这些情况也使用zoom配合target-densitydpi=device-dpi来进行缩放

  对ios设备在手机触发orientationchange事件,screen获取差异进行处理。

  在orientationchange的回调里增加一个定时器,解决某些低端安卓机获取可视区信息延迟的问题

代码

不解释了,直接上代码和注释吧

/*这种设计模式下,移动端需要使用meta来缩放,web端需要使用zoom来缩放
                  页面所有元素都必须按照设计图的尺寸来固定
              web端的缩放比例,预计的显示宽度/设计图的宽度
              android 4.4以下对viewport缩放支持不太好,使用zoom配合target-densitydpi=device-dpi来缩放
                  魅族手机使用viewport缩放有点问题,需要使用上面的方法处理
                  
                  手机旋转时,android系浏览器和safari获取视窗的不同
                  
                  假如android和safari的size都是320*640
                  
                  在竖屏模式下   window.screen.width  window.screen.height
                  android        320                  640
                  safari         320                  640
                  
                  在横屏模式下   window.screen.width  window.screen.height
                  android        640                  320
                  safari         320                  640
                  在orientationchange的回调里增加一个定时器,解决某些低端安卓机获取可视区信息延迟的问题
             **/
function scale(deWidth,webWidth){//deWidth设计图的宽度,webWidth兼容web页面的宽度,项目在web需要显示的宽度(不能超过deWidth)
    var changeTimer = null,sWidth = window.screen.width;
    if(/iPhone|iPad|iPod/.test(navigator.userAgent)&&typeof window.orientation!=="undefined"&&window.orientation !== 0){
        sWidth = window.screen.height;
    }
    if(navigator.userAgent.indexOf("Mobile")!==-1){
        window.onorientationchange = function(){
            changeTimer&&clearTimeout(changeTimer);
            changeTimer = setTimeout(function(){
                if(/iPhone|iPad|iPod/.test(navigator.userAgent)&&typeof window.orientation!=="undefined"&&window.orientation !== 0){
                    sWidth = window.screen.height;
                }else{
                    sWidth = window.screen.width
                }
                if(sWidth/deWidth<=1){
                    if (navigator.userAgent.match(/Android (d+.d+)/)){
                        if(parseFloat(navigator.userAgent.match(/Android (d+.d+)/)[1])<4.4||navigator.userAgent.indexOf("MZ-")!==-1){
                            
                            document.documentElement.style.zoom = sWidth/deWidth;
                            return false;
                        }
                    }
                    var meta = document.createElement("meta");
                    meta.setAttribute("name","viewport");
                    meta.setAttribute("content","width=device-width,initial-scale="+(sWidth/deWidth).toFixed(2)+",user-scalable=no,target-densitydpi=device-dpi");
                    
                    document.querySelector("[name=viewport]").remove();
                    document.head.appendChild(meta);
                }
            },500)
        }
        if(sWidth/deWidth<=1){
            if (navigator.userAgent.match(/Android (d+.d+)/)){
                
                if(parseFloat(navigator.userAgent.match(/Android (d+.d+)/)[1])<4.4||navigator.userAgent.indexOf("MZ-")!==-1){
                    document.documentElement.style.zoom = sWidth/deWidth;
                    return false;
                }
            }
            
            var meta = document.createElement("meta");
                meta.setAttribute("name","viewport");
                meta.setAttribute("content","width=device-width,initial-scale="+(sWidth/deWidth).toFixed(2)+",user-scalable=no,target-densitydpi=device-dpi");
                document.querySelector("[name=viewport]").remove();
                document.head.appendChild(meta);
        }
    }else{
        document.documentElement.style.zoom = webWidth/deWidth;
    }
}

可以在这里看看效果 demo

2017-07-03 update

解决window.screen在某些情况下,获取到的并不是可视区的问题,使用document.documentElement.clientWidth代替

在resize的时候还原zoom或者meta 重新去获取正确的视窗信息(因为缩放的原因,拿到的size是被放大了,之所以不记录缩放系数,因为有可能导致精度问题)

解决firefox不支持zoom,使用scale兼容

缩放规则更改
在安卓设备(4.4以上)上统一使用zoom来缩放(meta,target-densitydpi=device-dpi在某些内核下会自带缩放,即使没有设置initial-scale)

安卓设备(4.4以下)使用zoom + target-densitydpi=device-dpi

非安卓的使用<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no" />来缩放

var Scale = {
    init:function(deWidth,webWidth){
        var that = this,changeTimer = null;
        that.sWidth = document.documentElement.clientWidth;
        that.deWidth = deWidth;
        that.webWidth = webWidth;
        if(navigator.userAgent.match(/Android (d+.d+)/)&&(parseFloat(navigator.userAgent.match(/Android (d+.d+)/)[1])<4.4)){
                        var meta = document.createElement("meta");
                            meta.setAttribute("name","viewport");
                            meta.setAttribute("content","width=device-width,initial-scale=1,user-scalable=no,target-densitydpi=device-dpi");
                            
                            document.querySelector("[name=viewport]").remove();
                            document.head.appendChild(meta);
        }
        if(navigator.userAgent.indexOf("Mobile")!==-1){
            window.onorientationchange = function(){
                that.resetScale();
                changeTimer&&clearTimeout(changeTimer);
                changeTimer = setTimeout(function(){
                    that.sWidth = document.documentElement.clientWidth;
                    taht.setScale();
                },500)
            }
            that.setScale();
        }else{
            if(navigator.userAgent.indexOf("Firefox")!==-1){
                document.documentElement.style.transform = "scale("+that.webWidth/that.deWidth+")";
            }else{
                document.documentElement.style.zoom = that.webWidth/that.deWidth;
            }
        }
    },
    setScale:function(){
        var that = this;
        if(that.sWidth/that.deWidth<=1){
            if(navigator.userAgent.match(/Android (d+.d+)/)){
                document.documentElement.style.zoom = that.sWidth/that.deWidth;
            }else{
                var meta = document.createElement("meta");
                meta.setAttribute("name","viewport");
                meta.setAttribute("content","width=device-width,initial-scale="+(that.sWidth/that.deWidth)+",user-scalable=no");
                document.querySelector("[name=viewport]").remove();
                document.head.appendChild(meta);
            }
            
            
        }
    },
    resetScale:function(){
        document.documentElement.style.zoom = 1;
        var meta = document.createElement("meta");
        meta.setAttribute("name","viewport");
        meta.setAttribute("content","width=device-width,initial-scale=1,user-scalable=no");
        document.querySelector("[name=viewport]").remove();
        document.head.appendChild(meta);
                    
    }
}

结语

上面只是粗略的代码,可优化的地方还很多,最主要是这种懒适配的思想,当然,因为这种方法使用的较少,应该还存在很多的问题,欢迎指出。

原文地址:https://www.cnblogs.com/jesse007/p/7054209.html