始终保持视觉稿比例的移动端布局
布局要求:
由于手机的屏幕宽度大小不一,于是在布局复杂的移动端页面开发中我们就存在这样一个需求:
自适应不同宽度的设备,最大程度还原视觉稿比例。
演变过程 :
假如一个类似淘宝手机端首页的页面布局需要你去实现,你需要适配不同尺寸的手机终端
解决方案1:百分比布局
可能你首先想到的会是百分比布局
问题:百分比参照的容器的宽度是不同的,需要一个统一的参照,布局比较复杂的情况不太适用,这种方法也不利于后续布局变更的维护。
解决方案2:rem + 媒体查询
也是我最早接触移动端开发的时候使用的方法,使用rem单位。
html{
font-size:50px;
}
.div1{
width:.5rem;
}
.div2{
width:1rem;
}复制代码
以上只是实现了参照的统一,并没有达到不同终端适配的效果,于是我们加上了这样子的一些媒体查询。
...
@media screen and (min-width: 361px) and (max-width: 375px) {
html {
font-size: 50px;
}
}
@media screen and (min-width: 376px) and (max-width: 393px) {
html {
font-size: 52.4px;
}
}
@media screen and (min-width: 394px) and (max-width: 412px) {
html {
font-size: 54.93px;
}
}
...
.div1{
width:.5rem;
}
.div2{
width:1rem;
}复制代码
仍然,我们的实现效果是 trace1 所示
但是理想的情况是 trace 0
解决方案3:手淘 2015 年出的方案 flexible.js
作者在2019年1月更新了这样一段话,说明现在已经不推荐大家来采用这个方案了,并提供一篇文章说明现在推荐采用的新方案,这个我们后续会讲到。
这个方案的原理是通过 js 来动态计算根节点的 font-size,然后结合 rem 来实现。这里附上 GitHub 仓库的地址。
除此之外,flexible.js 中另外一个比较重要的想法是实现了页面中物理像素和 css 像素的数量一致,它根据设备的 dpr (设备像素比)对 viewport 进行了缩放(当然仅限于安卓设备)。解决了 Retina 屏幕下1px border的问题,更准确的说是 dpr 为 2 的设备中如何实现0.5px border的问题,因为设备能展示的最小单元是一个物理像素,所以dpr 为 2 的设备能展现的最小单位是0.5px。有相关的文章说它也解决了 Retina 屏幕下图片模糊的问题,物理像素值与css像素值相等,导致屏幕不会因为 css 像素值 < 物理像素值而从周围的点取色值。
更详细的对该方案的介绍和相关物理像素,css像素,dpr 的背景知识的介绍,可以阅读这两篇文章:
理解flexible.js所需的viewport知识
关于移动端适配,你必须要知道的
值得注意的是,由于缩放了viewport,会导致我们在 css 中如果使用 px 单位,在不同 dpr 设备中所展现的效果是不一样的,需要根据 dpr 值做一些额外的处理。例如:
@mixin font-dpr($font-size){
font-size: $font-size;
[data-dpr="2"] & {
font-size: $font-size * 2;
}
[data-dpr="3"] & {
font-size: $font-size * 3;
}
}
@include font-dpr(16px);复制代码
如果你想要使用媒体查询,在这个方案中也是不太友好的。
解决方案4:视口单位的使用
在 CSS 规范中,有4种类型的可用视口单位:
vw
--- 1vw 等于视口宽度的 1%vh
--- 1vh 等于视口高度的 1%vmin
--- vw 和 vh 中的较小值vmax
--- vw 和 vh 中的较大值
视口,即浏览器屏幕大小,1vw 等于浏览器宽度的 1%,100vw 即整个浏览器的宽度。
目前浏览器对视口单位的兼容性覆盖率已经90% +,我们可以使用 vw 来实现适配。 viewport 单位的浏览器兼容性 。
其余3个单位常用的场景如下:
vh : vh 比较常用的情况是100 vh
, 使得高度撑满整个屏幕。
vmin,vmax: 解决移动端中横屏显示的问题
应用场景1: 实现固顶的导航栏
应用场景2: 固定宽度或高度的div
使用 vw 推荐采用的解决方案
1.网易新闻的解决办法
地址:3g.163.com/touch/news/
首先我们需要将我们的视口变为理想视口
复制代码
PC 端中默认视口的宽度就等于设备的宽度,但是在移动端的浏览器中不是。为了让未适配手机视图的PC端网页在移动端中也能比较完整的展示,手机端的浏览器做了相应的处理,默认的 viewport 宽度会大于真实设备的宽度(在 IOS 中默认的 html 的宽度是 980px)。
width=device-width 的作用其实就是让我们在css 代码里面写的 px 对应真实设备上的 px 值。
现在我们的视口宽度就等于设备的宽度了,在设备宽度为375 px 的 IPhone6 中
html{ width: 13.33333333vw}复制代码
就等同于设置了 font-size: 50px。网易新闻采用的方案如下:
结合了媒体查询来处理 vw 的兼容性问题,如果不支持 vw 单位,可以降级为使用固定的 px。
···
@media screen and (min-width: 361px) and (max-width: 375px) {
html {
font-size: 50px;
font-size: -webkit-calc(13.33333333vw);
font-size: calc(13.33333333vw);
}
}
@media screen and (min-width: 376px) and (max-width: 393px) {
html {
font-size: 52.4px;
font-size: -webkit-calc(13.33333333vw);
font-size: calc(13.33333333vw);
}
}
@media screen and (min-width: 394px) and (max-width: 412px) {
html {
font-size: 54.93px;
font-size: -webkit-calc(13.33333333vw);
font-size: calc(13.33333333vw);
}
}
···复制代码
然后在实际的布局中使用 rem
可以使用 vscode 插件 px2rem
来进行转化,也可全局转化。
2.flexible.js 作者新提出的解决方案
不推荐使用flexible.js
之后,作者大漠在如何在Vue项目中使用vw实现移动端适配中有提到使用 Postcss 插件结合 vw 解决方案。作者在文章最末尾给出了实现的 demo 与下载的链接。
"dependencies": {
"cssnano": "^3.10.0", // 压缩与优化css
"postcss-aspect-ratio-mini": "0.0.2", // 处理元素容器宽高比
"postcss-cssnext": "^3.1.0", // css的polyfill
"postcss-px-to-viewport": "0.0.3", // 编译时将px转化为视口单位
"postcss-viewport-units": "^0.1.3", // 处理视口单位的兼容性问题
"postcss-write-svg": "^3.0.1" // 用来解决1px的border的问题
}复制代码
cssnano
与 postcss-cssnext
的补充知识了解。
cssnano
: github.com/iuap-design…
postcss-cssnext
:github.com/jiayisheji/…
然后在.postcssrc.js
文件对新安装的PostCSS插件进行配置。
module.exports = {
"plugins": {
"postcss-import": {},
"postcss-url": {},
"postcss-aspect-ratio-mini": {},
"postcss-write-svg": {
utf8: false
},
"postcss-cssnext": {},
"postcss-px-to-viewport": {
viewportWidth: 750, // (Number) The width of the viewport.
viewportHeight: 1334, // (Number) The height of the viewport.
unitPrecision: 3, // (Number) The decimal numbers to allow the REM units to grow to.
viewportUnit: 'vw', // (String) Expected units.
selectorBlackList: ['.ignore', '.hairlines'], // (Array) The selectors to ignore and leave as px.
minPixelValue: 1, // (Number) Set the minimum pixel value to replace.
mediaQuery: false // (Boolean) Allow px to be converted in media queries.
},
"postcss-viewport-units":{},
"cssnano": {
preset: "advanced",
autoprefixer: false,
"postcss-zindex": false
}
}
}复制代码
文中还提及了对视口单位兼容性问题的解决方案:
采用 postcss-viewport-units
与 viewport-units-buggyfill
结合的方法
postcss-viewport-units
在编译时给视口单位增加 content
属性,例如:
然后viewport-units-buggyfill
通过这个content
属性来做兼容性处理。
最后附上作者 demo 的 Github地址。
多种终端适配的方案
前面讨论的都是纯移动端中的问题,接下来我们把讨论的范围扩大一下,如果我们需要实现一个多终端适配的网站,应该要如何实现?
我选择了一些比较典型的网页,在浏览器中将窗口大小缩放,观察效果。
方案1:出现横向滚动条, 忽略IPad视图
最小的PC端屏幕宽度是1280px,许多网站会将主体内容的宽度定在1200px,设置一个min-width:1200px,当窗口大小小于这个置时,出现横向的滚动条。
示例网站1
百度FEX
小于 1200px 时,横向出现滚动条,在 IPAD 下是出现横向滚动条的,小于 640px 时,变到了手机端的视图。
示例网站2
海康威视AI开放平台,和示例网站1很相似,手机视图变化的断点定在了 768px。
方案2:全适配
示例网站1
腾讯ISUX
页面内容比较简单,布局多采用块状,能比较方便的采用百分比, flex 或者是 grid 布局结合媒体查询来实现全适配。
示例网站2
css-tricks.com/
相比起网站1,页面内容就比较复杂了,需要做多个媒体查询的处理来实现效果。
网站的全局适配有很多种解决方法,不同视图变化的断点也不是固定的,你可以根据你网站的内容来自己定义如何来变化。
当然,这些单凭前端来完成是不足够的,设计师在出视觉稿的时候也需要考虑网页在PC端,移动端,IPAD尺寸下视图应当如何展现,并定义好各个视图转变的断点值是多少,这样才能保证我们的页面在不同屏幕尺寸下有良好的展现。
字体大小的适配
如果你选取了采用方案1,那某种程度上是可以避免掉字体大小适配的问题的,只需要保持在PC端一个字体规范,移动端一个字体规范,界面最终的显示并不会有问题。
如果采用了方案2,那你可能还需要考虑字体大小需要随着屏幕大小而变化的问题。具体内容可参考掘金翻译计划中的这篇文章:Web 流式文字排版的现状。
文章大致的内容是作者发现,
视口单位被引入更多是作为绝对单位网页布局的一种扩展
1.抛出了一个问题:为什么很少有网站把它应用到字体大小当中?
2.总结出了一个 rem 和 vw 结合来控制字体大小的最佳实践。( 带 CSS 锁的流式文字排版 )。
作者推导出这个公式
body {
font-size: calc([minimum size] + ([maximum size] - [minimum size]) * ((100vw - [minimum viewport width]) / ([maximum viewport width] - [minimum viewport width])));
}复制代码
我们用一个张鑫旭的博客中提到的实际的例子来帮助理解一下
假设我们希望浏览器宽度在 600px ~ 1000px 变化的时候,根元素的 font-size
大小是18px~22px之间对应变化的,默认根结点的字体大小是16px,那我们可以这样子做
html { font-size: calc(112.5% + 4 * (100vw - 600px) / 400); }复制代码
3.最后作者又回过头来推翻了自己的论点,考虑到使用vw来实现字体的大小变化会引起无障碍访问功能的问题,所以他并不提倡在实际的网站中采用这种做法。
比如使用 vw 来实现 div 的宽度,那么在网页缩放的时候所占当前页面的比例还是一致的,同理如果用 vw 来实现font-size,缩放页面大小也是不会改变的,对于一些需要使用无障碍功能将页面字体放大来方便查看的人来说,这个显示是不太合适的。
总结
未来视口单位的兼容性肯定会越来越好,在移动端的适配中,对于元素的布局可以直接采用 vw,使用Postcss
插件来让我们的开发更加方便与自动化。
多终端的适配有多种不同的解决方案,需要根据你当前网页所要展现的内容确定最适合自己的解决方案,当然需要与视觉实现沟通设计好不同屏幕尺寸下的展示效果。
对于字体的适配,在有需要的情况下如果不考虑无障碍访问功能的话,可以使用这个带 CSS 锁的字体大小适配方案配合 rem 单位一起使用。如果要考虑无障碍访问功能,那就优雅的降级采用 media query + rem 来实现。
参考文章
基于视口单位的网页排版
浅说移动前端中 Viewport 和 Viewport units
使用Flexible实现手淘H5页面的终端适配
lib-flexible仓库地址
理解flexible.js所需的viewport知识
关于移动端适配,你必须要知道的
基于vw等viewport视区单位配合rem响应式排版和布局
如何在Vue项目中使用vw实现移动端适配
Web 流式文字排版的现状