页面适配

  • 页面适配
    • 高清适配
    • 布局适配
      • 自适应缩放
        • 场景:视觉设计对元素位置的相对关系依赖较强,如移动端页面
        • 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) ); 
}
其他文章
交流区
加载中...