因为手机的屏幕宽度大小不一,因而在布局复杂的移动端页面开发中咱们就存在这样一个需求:css
自适应不一样宽度的设备,最大程度还原视觉稿比例。html
假如一个相似淘宝手机端首页的页面布局须要你去实现,你须要适配不一样尺寸的手机终端前端
解决方案1:百分比布局
git
可能你首先想到的会是百分比布局github
问题:百分比参照的容器的宽度是不一样的,须要一个统一的参照,布局比较复杂的状况不太适用,这种方法也不利于后续布局变动的维护。web
解决方案2:rem + 媒体查询c#
也是我最先接触移动端开发的时候使用的方法,使用rem单位。浏览器
html{
font-size:50px;
}
.div1{
width:.5rem;
}
.div2{
width:1rem;
}复制代码
以上只是实现了参照的统一,并无达到不一样终端适配的效果,因而咱们加上了这样子的一些媒体查询。bash
...
@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 所示app
可是理想的状况是 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 的背景知识的介绍,能够阅读这两篇文章:
值得注意的是,因为缩放了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
首先咱们须要将咱们的视口变为理想视口
<meta name="viewport" content="width=device-width,initial-scale=1">复制代码
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
来进行转化,也可全局转化。
不推荐使用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地址。
前面讨论的都是纯移动端中的问题,接下来咱们把讨论的范围扩大一下,若是咱们须要实现一个多终端适配的网站,应该要如何实现?
我选择了一些比较典型的网页,在浏览器中将窗口大小缩放,观察效果。
最小的PC端屏幕宽度是1280px,许多网站会将主体内容的宽度定在1200px,设置一个min-width:1200px,当窗口大小小于这个置时,出现横向的滚动条。
示例网站1
小于 1200px 时,横向出现滚动条,在 IPAD 下是出现横向滚动条的,小于 640px 时,变到了手机端的视图。
示例网站2
海康威视AI开放平台,和示例网站1很类似,手机视图变化的断点定在了 768px。
示例网站1
页面内容比较简单,布局多采用块状,能比较方便的采用百分比, flex 或者是 grid 布局结合媒体查询来实现全适配。
示例网站2
相比起网站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