页面适配
- 页面适配
- 高清适配
- 布局适配
- 自适应缩放
- 场景:视觉设计对元素位置的相对关系依赖较强,如移动端页面
- rem + vw 适配方案
- 弹性结构
- flex
- grid
- 响应式布局
- 媒体查询 + 断点
- 容器查询 + 断点
- 自适应缩放
- 大屏适配
高清适配
设备独立像素 vs 物理像素
设备独立像素,也就是无任何设备无关、逻辑上的像素,可以认为是计算机坐标系统中得一个点,这个点代表一个可以由程序使用的虚拟像素(比如: css 像素),然后由相关系统转换为物理像素。
在同一尺寸但物理像素不同的屏幕上(普通屏幕 vs retina屏幕),css 像素所呈现的大小(物理尺寸)是一致的,不同的是1个css像素所对应的物理像素个数是不一致的:

所以说,物理像素和设备独立像素之间存在着一定的对应关系,也就是设备像素比(dpr)。在 javascript 中,可以通过 window.devicePixelRatio 获取到当前设备的 dpr 。
设备像素比 = 物理像素 / 设备独立像素 // 在某一方向上,x方向或者y方向
例如通过得知设备像素比,比如为 devicePixelRatio = 2,我们可以知道该设备 1px == 4 个物理像素点。
位图像素
一个位图像素是栅格图像(如:png, jpg, gif等)最小的数据单元。每一个位图像素都包含着一些自身的显示信息(如:显示位置,颜色值,透明度等)。
谈到这里,就得说一下,retina下图片的展示情况?
理论上,1个位图像素对应于1个物理像素,图片才能得到完美清晰的展示。
在普通屏幕下是没有问题的,但是在retina屏幕下就会出现位图像素点不够,从而导致图片模糊的情况。
用一张图来表示:

如上图:对于dpr=2的retina屏幕而言,1个位图像素对应于4个物理像素,由于单个位图像素不可以再进一步分割,所以只能就近取色,从而导致图片模糊(注意上述的几个颜色值)。
所以,对于图片高清问题,比较好的方案就是两倍图片(@2x)。
如:200×300(css pixel)img标签,就需要提供 400×600 的图片。
如此一来,位图像素点个数就是原来的 4 倍,在 retina 屏幕下,位图像素点个数就可以跟物理像素点个数形成 1 : 1 的比例,图片自然就清晰了。
开发规范
总结来说:页面的显示原理本质上就是一张位图。了解设备独立像素和物理像素后,作为开发者,我们要面对的是逻辑像素,但对于 retina 屏幕(如: dpr=2),为了达到高清效果,视觉稿的画布大小会是基准的 2 倍,也就是说像素点个数是原来的 4 倍(对iphone6 而言:原先的 375×667,就会变成 750×1334),也就是以 1px == 1 个物理像素为基准开发 呢。
如何使 1px == 1pt?
- 视觉视口,也就是手机屏幕可看到的大小范围。
window.innerWidth/Height返回视觉视口的尺寸 - 布局视口,也就是网页页面展示的大小范围。
document.documentElement.clientWidth/Height返回布局视口的尺寸
在设计开发上,对 iphone6 而言,视觉视口大小为 375×667 px,dpr 为 2,为了达到高清效果(1px == 1pt),我们会以 750x1334 px为基准。这样效果即是:

我们通过 meta 标签的 viwport 属性来控制缩放布局视图大小来达到 750x1334 px 的页面容进 750x1334 pt 的屏幕,这样 1px == 1pt。
viewport 标签只对移动端浏览器有效,对 PC 端浏览器是无效的。 对于设置初始或最大比例的页面,这意味着width属性实际上转换为最小视口宽度,浏览器将扩大视口(而不是放大)以适应屏幕
对不同尺寸移动设备做到动态缩放设置:
// 控制 viewport 属性
let dpr = window.devicePixelRatio;
let meta = document.querySelector('meta[name=viewport]') || document.createElement('meta');
let scale = 1/dpr;
meta.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
if (document.querySelector('meta[name=viewport]')) {
document.getElementsByTagName('head')[0].appendChild(meta);
}
到目前为止,以上方案可以解决:
- 高清适配问题
- retina下,border: 1px 问题
rem + vw 适配方案
以 750×1334 设计稿为例,设置 viewport=device-width,html 元素的 font-size 使用 vw 的形式:
<meta name="viewport" content="width=device-width; initial-scale=1; maximum-scale=1; minimum-scale=1; user-scalable=no;">
<style>
html{
font-size: 10vw;
}
</style>
元素布局上使用 rem 单位,使用 postcss-px-to-viewport 将设计稿的长度单位换算为 vw 单位,配合 vw 单位的适配方案,实现了适配不同屏幕尺寸的效果
// postcss.config.js
module.exports = {
plugins: [
require('postcss-pxtorem')({
rootValue: 75,
propList: ['*'],
})
]
};
当页面超过限定宽度时,根据 media query 设置 font-size:
html {
font-size: 10vw;
// 同时,通过Media Queries 限制根元素最大最小值
@media screen and (max-width: 320px) {
font-size: 64px;
}
@media screen and (min-width: 540px) {
font-size: 108px;
}
}
// body 也增加最大最小宽度限制,避免默认100%宽度的 block 元素跟随 body 而过大过小
body {
max-width: 540px;
min-width: 320px;
}
vw + calc + clamp
这是一种全 css 方案:使用 vw 单位,配合 calc 函数和 clamp 函数,实现了适配不同屏幕尺寸的效果:
:root {
/* 理想视窗宽度,就是设计稿宽度 */
--ideal-viewport-width: 750;
/* 当前视窗宽度 100vw */
--current-viewport-width: 100vw;
/* 最小视窗宽度 */
--min-viewport-width: 320px;
/* 最大视窗宽度 */
--max-viewport-width: 1920px;
/**
* clamp() 接受三个参数值,MIN、VAL 和 MAX,即 clamp(MIN, VAL, MAX)
* MIN:最小值,对应的是最小视窗宽度,即 --min-viewport-width
* VAL:首选值,对应的是100vw,即 --current-viewport-width
* MAX:最大值,对应的是最大视窗宽度,即 --max-viewport-width
**/
--clamped-viewport-width: clamp( var(--min-viewport-width), var(--current-viewport-width), var(--max-viewport-width) )
}
.card {
--ideal-border-radius: 38; /* 750px 设计中圆角值 */
border-radius: calc( var(--ideal-border-radius) * var(--clamped-viewport-width) / var(--ideal-viewport-width) );
}
.card__media {
--ideal-border-radius: 24; /* 750px 设计中圆角值 */
border-radius: calc( var(--ideal-border-radius) * var(--clamped-viewport-width) / var(--ideal-viewport-width) );
}
.card__media img {
--ideal-border-radius: 24; /* 750px 设计中圆角值 */
border-radius: calc( var(--ideal-border-radius) * var(--clamped-viewport-width) / var(--ideal-viewport-width) );
}