关于移动端的适配方案,现在其实已经有很多了,什么百分比、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); } }
结语
上面只是粗略的代码,可优化的地方还很多,最主要是这种懒适配的思想,当然,因为这种方法使用的较少,应该还存在很多的问题,欢迎指出。