问题的由来
手机屏幕的分辨率差异很大。
iphone4:320×480
iphone6:375×667
H5 网页必须自动适应这些屏幕,包含以下三部分的工作。
布局的缩放
字体的大小
图片的适配
有些设备是高清屏(retina),物理像素在水平方向和垂直方向各压缩 50% 显示。
这时,CSS 里面的 1 个像素会对应 4 个物理像素。
width: 20px;height: 20px;
retina 设备中,一张 40 x 40 像素的图片,会占据 20 x 20 的屏幕空间。如果图像比 40 x 40 像素小,就会显得比较模糊。
所以,图片往往需要准备非 retina 和 retina 两个版本。比如,200×300 的图片,还要准备 400×600 的版本。
SASS 的写法。
@mixinimg-dpr(){background-image: url(image.jpg);//默认[data-dpr="2"]&{background-image: url(image@2x.jpg);//两倍高清} [data-dpr="3"] & {background-image: url(image@3x.jpg);//三倍高清}}.content{@includeimg-dpr(); }
高清屏的物理像素与实际像素的关系,可以用下面的公式表示。
设备像素比 = 物理像素 / 设备独立像素
“设备像素比”(device-pixel-ratio)简称为dpr。
iPhone4~iPhone6:dpr = 2
iPhone 6+:dpr = 3
对于 dpr 等于 2 的设备,视觉稿的画布水平/垂直大小会是基准的2倍,也就是说像素点个数是原来的4倍。比如,对于 iphone6 而言来说,屏幕大小是 375×667,视觉稿就是 750×1334。
脚本中可以使用window.devicePixelRatio得到dpr。
console.log(window.devicePixelRatio)// 2
使用 CSS 的 Media Query,可以针对不同的屏幕设置不同的 CSS 规则。
@media(-webkit-min-device-pixel-ratio:2), (min-resolution:2dppx), (min-resolution:192dpi) {/* 高清屏的 CSS 规则 */}
区分 retina 屏幕和屏幕宽度的 CSS 设置,完整写法如下。
@mediaonly screen and (min-width:320px) {/* 非 retina 的小屏幕 */}@mediaonly screen and (-webkit-min-device-pixel-ratio:2) and (min-width:320px),only screen and ( min--moz-device-pixel-ratio:2) and (min-width:320px),only screen and ( -o-min-device-pixel-ratio:2/1) and (min-width:320px),only screen and ( min-device-pixel-ratio:2) and (min-width:320px),only screen and ( min-resolution:192dpi) and (min-width:320px),only screen and ( min-resolution:2dppx) and (min-width:320px) {/* 小屏幕的 retina 屏 */}@mediaonly screen and (min-width:700px) {/* 中等的非 retina 屏 */}@mediaonly screen and (-webkit-min-device-pixel-ratio:2) and (min-width:700px),only screen and ( min--moz-device-pixel-ratio:2) and (min-width:700px),only screen and ( -o-min-device-pixel-ratio:2/1) and (min-width:700px),only screen and ( min-device-pixel-ratio:2) and (min-width:700px),only screen and ( min-resolution:192dpi) and (min-width:700px),only screen and ( min-resolution:2dppx) and (min-width:700px) {/* 中等屏幕的 retina 屏 */}@mediaonly screen and (min-width:1300px) {/* 大屏幕的非 retina 屏 */}@mediaonly screen and (-webkit-min-device-pixel-ratio:2) and (min-width:1300px),only screen and ( min--moz-device-pixel-ratio:2) and (min-width:1300px),only screen and ( -o-min-device-pixel-ratio:2/1) and (min-width:1300px),only screen and ( min-device-pixel-ratio:2) and (min-width:1300px),only screen and ( min-resolution:192dpi) and (min-width:1300px),only screen and ( min-resolution:2dppx) and (min-width:1300px) {/* 大屏幕的 retina 设备 */}
页面如何缩放,才能适应屏幕的大小?
宽度自适应
rem 方法
网页元素的宽度设成百分比或vw单位,或者采用 flex 布局。
视口自动缩放,使得宽度自动适应网页大小。
width=device-width,
initial-scale=1.0,
minimum-scale=1.0,
maximum-scale=1.0,
user-scalable=no
">
缺点:宽度可以自适应,但是没法规定高度,容器会有一定程度的变形。
另一种方法是使用rem单位,所有网页元素的宽度和高度都设成rem单位,因此不会有变形问题。
视口不用缩放,只需要指定根元素的font-size大小。然后,根据不同网页的宽度,动态改变根元素的font-size,来调节容器的大小。
比如,设计稿的默认宽度是 750 px,为了便于计算,对应的font-size指定为1rem = 100px。如果设备的宽度是 375 px,就用脚本将font-size改为1rem = 50px。那么,某个网页元素的宽度是0.5rem,在 750px 设备下是50px,在 375px 设备下是25px。
下面就是脚本改写font-size的例子。
(function(doc, win){vardocEl = doc.documentElement;varresizeEvt ='orientationchange'inwindow?'orientationchange':'resize';varrecalc =function(){varclientWidth = docEl.clientWidth;if(!clientWidth)return; docEl.style.fontSize =100* (clientWidth /750) +'px'; }; win.addEventListener(resizeEvt, recalc,false); doc.addEventListener('DOMContentLoaded', recalc,false);})(document,window);
不使用 JS,单纯使用 CSS 也可以设置font-size的大小。
@media screen and (min-width:320px) {html {font-size:100px;}}@media screen and (min-width:360px) {html {font-size:112.5px;}}@media screen and (min-width:400px) {html {font-size:125px;}}@media screen and (min-width:440px) {html {font-size:137.5px;}}@media screen and (min-width:480px) {html {font-size:150px;}}
为了解决1px的 border 的问题(见后文),可以指定页面宽度,然后将页面放大dpr倍,再缩小为1/dpr,脚本代码如下。
vardpr, rem, scale;vardocEl =document.documentElement;varfontEl =document.createElement('style');varmetaEl =document.querySelector('meta[name="viewport"]');dpr =window.devicePixelRatio ||1;rem = docEl.clientWidth * dpr /10;scale =1/ dpr;// 设置viewport,进行缩放,达到高清效果metaEl.setAttribute('content','width='+ dpr * docEl.clientWidth +',initial-scale='+ scale +',maximum-scale='+ scale +', minimum-scale='+ scale +',user-scalable=no');
比如,对于iphone5(dpr=2),meta标签如下。
然后,结合屏幕宽度和 dpr 后算出 font-size (基准为 32px)。
320px 屏幕, dpr=1 ,font-size=32px
320px 屏幕, dpr=2 ,font-size=64px
375px 屏幕, dpr=2 ,font-size=75px
414px 屏幕, dpr=3, font-size=124.2px
计算规则:(屏幕宽度 * dpr ) / 10
“屏幕宽度”可以用document.documentElement.clientWidth得到。乘以dpr,是因为页面有可能为了实现1pxborder页面会缩放(scale)1/dpr倍(如果没有,则dpr=1)。
使用了rem单位之后,页面元素会随着屏幕的增大而等比例放大,但是某些内容我们不愿意被放大,例如正文段落,而是为显示更多的文字,这时文字不使用rem作为单位,而是用px。
font-size:16px;[data-dpr="2"] input { font-size:32px;}
上面代码中,默认字体大小是 16px,retina 设备就是 32px。
一个针对 iphone6 的高清视觉稿 750×1334
在psd文件中量出:宽高 750×300px 的div,那么如何转换成rem单位呢?
公式如下:
rem = px / 基准值;
对于一个iphone6的视觉稿,它的基准值就是75(之前有提到)。
转成 HTML 就是如下。
width: 10rem; // -> 750px
height: 4rem; // -> 300px
最后因为dpr为2,页面scale了0.5,所以在手机屏幕上显示的真实宽高应该是375×150px,就刚刚好。
高清屏下,border: 1px会显得太宽,但是安卓设备的border又不能设成 0.5 px,可以参考下面的链接。