大家好,我是编程小6,很高兴遇见你,有问题可以及时留言哦。
本文主要针对初学移动端web开发的读者,笔者也是初学者,文中有众多用词不当之处望读者指正。
从开始做web app开发到现在,一直对移动端的尺寸适配有一种模糊的概念。能说得上来‘媒体查询’,‘栅格化布局’,‘流式布局’等若干技术名词和实现方式,但是每次自己做web app开发的时候,做出来的产物总是不尽人意,比如在iPhone5
上出现文字溢出,调整好的布局位置在一些小尺寸手机上发现位置非常不对,或是遮盖了其他元素,或是换行了。
如果是之前,我是这样的做法:
不断写媒体查询做兼容,直到PM或者QA满意为止。
这样的方法,存在以下几个问题:
我想了想,为什么会出现治标不治本的情况:
一劳永逸
,全尺寸支持
,不用动脑子算
的移动端尺寸适配方案呢?**答案当然是有的。**笔者结合了自己所看的几篇热门的博客,总结了其中比较有用的几个知识点,希望能让读者更快的掌握并使用这个'一劳永逸的方法'。能偷懒的事情绝对要偷懒。🐷(热门博客题目如图,含flexible的github repo)
230px
, 通过函数将其转为rem
而不用人工计算。一句话概括: 假如<html>
标签上设置了样式font-size:16px
,那么 1rem = 16px
。 所以:
首先,需要和UI小姐姐说一句话:
"标注元素的时候请按照
750px * 1334px
为准。"
那么,你将会拿到一张如下的标注图:
到这一步,我们仍然没有解决核心问题:
接下来,就是最为核心的环节了,笔者通过步骤图向大家还原计算的过程。
第一步:假设有三款不同长宽的手机。
第二步:把手机的宽分为10份,那么上述三款手机的每份宽度是35px/36px/37px。并且将<html>
标签添加不同的font-size
设置。
即:一份分别为35px/36px/37px
第三步:根据UI的px标注图计算出相应的rem:
第四步:rem将转化成不同的px尺寸在不同的手机上呈现:(ps:图中的除法结果算错了)
通过这样的方式,即可以在不同尺寸的手机上有相同的展示效果。而最cool的地方,是上述整个过程时自动适配的。开发者只需根据UI标注图无脑写就行了,再也不用挤眉弄眼地对着Chrome Devtools 疯狂调试了。
把手机的宽分为10份,那么上述三款手机的每份宽度是35px/36px/37px。并且将
<html>
标签添加不同的font-size
设置。
通过JavaScript动态计算出当前的屏幕宽度,切割为10份并将<html>
的fontSize
设置为1份单位宽度
。
document.addEventListener('DOMContentLoaded', function(e) {
document.getElementsByTagName('html')[0].style.fontSize = window.innerWidth / 10 + 'px';
}, false);
当初始的 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,而无需等待样式表、图像和子框架的完成加载。另一个不同的事件 load 应该仅用于检测一个完全加载的页面。 MDN::DOMContentLoaded
根据UI的px标注图计算出相应的rem
这一步需要使用Sass
来定义一个px2rem
的工具函数:
// utils.scss
@function px2rem($px){
$rem : 75px; // '750/10':分成10份
@return ($px/$rem) + rem;
}
// foo.scss
.box1 {
width: px2rem(320px); // '(320/750) * 10 = 4.266rem'
}
这样,我们在styleSheet
中实际生效的是height: 4.266rem
,而1rem
对应多少px
是上述JavaScript代码根据不同的window.innerWidth
提前计算好的。这样就实现了自动适配。
如果你嫌写
px2rem()
也麻烦,那么可以把函数名定义简单一些。
在写这篇博客的开始,我曾试图绕开阐述viewport
和dpr
这个抽象的概念,因为上述的内容已经可以从一个维度解决大多数问题了。但是,如果想做得更完美,就必须从另一个维度出发,而这个维度,就是dpr。
首先,要区分两个概念:
有这样一个场景
一位前端工程师敲出了
.box {
width: 100px;
height:100px;
}
那么此时,他的意思是box
在我们的屏幕中占的实际长宽是100px
,在他脑中是这样的画面:
项目上线之后,有一个用户'不怀好意'地使用了放大镜功能将长宽放大了两倍,现在就变成了:
你会发现,设备花了200px的长宽来渲染CSS里面定义的100px的长宽,而设备pixels和样式pixels的比值,就是dpr,即Device Pixel Ratio,如果对这个概念仍有问题,请查看viewport剖析。
我们大家都知道Retina屏(视网膜屏),之所以看起来这么高清,就是因为苹果设备花两个像素来渲染一个像素的物体,那么看起来肯定更为精致。
所以,如果我们针对dpr=1
的书写了rem2px(100px)
,那么在dpr=2
的设备看起来将会是被放大了2倍的元素。
那么,如果我们能够查询出当前设备的dpr,并且做相应的缩放就可以解决这个问题。
举个例子:某些安卓机的dpr=1
,但是UI做标注图的时候是根据dpr=2
来做的,就像我们上文的750px * 1334px
。直接按照750px * 1334px
写出来的元素将会被放大两倍,那么我们就使页面缩小两倍,如何控制呢?
用viewport
简言之,在这里我们使用viewport
是为了控制屏幕的缩放。
var dpr = window.devicePixelRatio;
meta.setAttribute('content', 'initial-scale=' + 1/dpr + ', maximum-scale=' + 1/dpr + ', minimum-scale=' + 1/dpr + ', user-scalable=no');
// 帮助理解 如果dpr=2,说明写的100px渲染成了200px,所以需要缩小至1/2,即1/dpr
另外值得一提的是,UI一般会以750px * 1334px
的标准进行设计,因为这样使得设计稿更加精细。 比如我们写了rem2px(375px)
,那么会经过下列的过程换算到设备pixels宽度为390px且dpr=3的手机。
rem2px(375px)
----> 5rem
5rem
-----> 195px (样式pixels)
样式195px
------> 此时看起来(指的就是设备pixels)有195*3 = 585px
的长度设置dpr=1/3
------->此时看起来只有195px
这样,我们完成了从dpr
维度的适配。
<script> var dpr = window.devicePixelRatio; var meta = document.createElement('meta'); // dpr meta.setAttribute('content', 'initial-scale=' + 1/dpr + ', maximum-scale=' + 1/dpr + ', minimum-scale=' + 1/dpr + ', user-scalable=no'); document.getElementsByTagName('head')[0].appendChild(meta); // rem document.addEventListener('DOMContentLoaded', function (e) { document.getElementsByTagName('html')[0].style.fontSize = window.innerWidth / 10 + 'px'; }, false); </script>
为了防止全局变量污染或者覆盖他人的变量,请封装成模块再使用。
在写这篇博客的过程中,曾纠结过这样的问题:rem布局和百分比布局感觉差距不大啊,因为在写rem的时候是基于把宽度切为10份后再写的,就像是1rem = 10% = 10vw
一样。这让我一度觉得可以用百分比布局。后来发现,如果出现盒子嵌套(这种场景太多了),那么百分比布局就出现问题了,因为其百分比的参考系选择的是父元素,所以我们如果在子盒子里面定义10%
的宽度,指的是针对父盒子
的而不是我们想要的针对整个window.innerWidth
的10%
。而vw
的代码可维护性不如上述的这套方案,且兼容性也没有rem
好(这一点差距不是太大)。
如果想了解更多关于PC端or移动端布局,请看参考资源&鸣谢
板块。
移动端页面适配方案
使用Flexible实现手淘H5页面的终端适配
六种布局+rem布局的简介
DOMContentLoaded与load的区别
rem是如何实现的
AlloyTeam 移动web适配利器
viewport剖析
lib-flexible
上一篇
已是最后文章