一、前言
由于手机的屏幕尺寸、分辨率不同,还要考虑横竖屏问题,为了使得 web 页面在不同移动设备上适配,需要在开发过程中使用合理的适配方案。
本文特地将响应式和自适应搅为一谈,因为实际场景没必要分太细。就像函数式和面向对象,js 和 ts,react 和 vue,实际只要能解决具体需求,搞技术对立或抓着细节咬文嚼字没啥意义
早期网页设计采用静态布局,通过<meta>
标签中的applicable-device
设备标识识别移动设备,即<meta name = 'applicable-device' content = 'mobile'>
,在<meta>
标签中的viewport
标签中设置width
,通过js
动态修改标签的initial-scale
使得页面等比缩放刚好占满屏幕。静态布局各个元素采用px
为单位,这种方案实现简单,不存在兼容性问题,但用户体验很不友好。
后面出现流式布局,使用百分比%
定义宽度,高度使用px
固定,根据可视区域大小实时进行尺寸调整,通常使用max-width/min-width
控制尺寸范围过大或者过小。这种方案实现比较简单,但在大屏手机或横竖屏切换场景下可能会导致页面元素被拉伸变形,字体大小无法随屏幕大小发生变化。
为适应不同页面字体大小展现问题,出现了弹性布局。这种布局方案下,包裹文字的元素的尺寸采用em/rem
为单位,页面主要划分区域依据情况使用px
、百分数或者em/rem
。如一些高校的网站,页面的主要划分区域使用px
和百分比,包裹文字的元素和文字采用em
。
上面几种方案,页面元素的大小按照屏幕分辨率进行适配调整,但整体布局不变,对于响应式web设计,网页布局会随着访问它的视口及设备的不同从而呈现不同的样式,在实现上会以上多种方案相结合,搭配媒体查询,使得一个页面在多个终端 (PC, mobile, pad) 呈现满意效果。
二、基本概念
2.1 像素
px (pixel)
像素,(物理像素/设备像素),是分辨率的尺寸单位,用来显示图像的基本单元。不同设备像素不同,不同分辨率下相同长度的px
显示会不一样,因为像素点的个数相同情况下,不同分辨率下每个像素点对应像素宽度不同。同样是14px
大小的字,在1366×768
显示屏下会显示小些,在1024×768
显示屏下会相对大。
分辨率
分辨率:屏幕上纵横的像素点。
CSS 像素
逻辑像素(逻辑分辨率) = 设备独立像素(DIP) = CSS像素:是非真实存在的,代码中用来衡量页面内容的大小。不同屏幕上,css
像素呈现的物理尺寸一致,但css
像素对应的物理像素具数不同。标准的显示密度下,1
个css
像素对应一个物理像素,缩放时,1
个css
像素对应的物理像素会减增。
物理像素
物理像素(物理分辨率) = 设备像素(DP) : 设备屏幕的显示单位,设计图一般给的是物理分辨率
PPI (pixel per inch)
屏幕像素密度(ppi):屏幕每英寸可显示像素点的数量,与之类似的还有dpi。值越高,显示画面细节越丰富。计算公式为:
其中 W 和 H 是分辨率的宽高,S 是屏幕尺寸。
DPI (dot per inch)
打印设备每英寸印刷出来的点有多少个,值越高,图片越细腻。
DPR (devicePixelRatio)
设备像素比,即:
指用多少个物理像素来渲染一个css像素。js中通过window.devicePixelRatio
获取,css中通过-webkit-device-pixel-ratio
,-webkit-min-device-pixel-ratio
,-webkit-max-device-pixel-ratio
进行媒体查询。
dpr 的出现是苹果在 iphone4 产品中推出了所谓的 retina 视网膜屏幕。因为屏幕的 ppi 太高,达到了人类视网膜的分辨极限。它在保持屏幕大小尺寸不变的情况下将它物理分辨率提高了一倍,在同样大小的屏幕上,像素多了一倍。
em
大小取决于当前对象的 font-size,未设置的话则 1 em 默认为 16 px,此时 10 px = 0.625 em。
换算:
1em = 16px => 62.5%em = 10px
需要在 css 中的 body 选择器中声明 font-size = 62.5% ,使 1 em = 10 px,再把之前的 px 数值除以 10,然后换上 em 作为单位。
特点:
- em 的值并不是固定的,em 会继承父级元素的字体大小
- 比如要设置 p 标签内的文本为 12 px,但包裹 p 标签的 #content 中已经声明了 font-size = 1.2 em,在声明 p 的字体大小时就只能是 1 em
rem (root em)
CSS3 新增的一个单位,相对于 HTML 根元素 font-size 。
特点:
- 修改根元素大小比例地调整所有字体大小,避免了字体大小的逐层复合。
- 对于 IE8 及更早版本不兼容。
VM
CSS3 引入与视口有关的新单位:vw 表示相对于视口的宽度,vh 表示相对于视口高度。
平时拿到的设计图是基于 px ,怎么将 px 转为 vw 呢?
vw 单位换算:视口宽度为 100 vw 占满整个视口区域,1 vw 相当于占整个视口宽度的 1%,所以 375 px = 100 vw => 1 px = 1/375*100 vw
所有页面元素都可换算成 vw 单位,但和百分比方案计算类似,会比较麻烦。
用插件 postcss-px-to-viewport,来预处理 CSS,将 px 转换为 vw,需要进行一些相关 Webpack 配置:
{
loader: 'postcss-loader',
options: {
plugins: ()=>[
require('autoprefixer')({
browsers: ['last 5 versions']
}),
require('postcss-px-to-viewport')({
viewportWidth: 375,
viewportHeight: 1334,
unitPrecision: 3,
viewportUnit: 'vw',
selectorBlackList: ['.ignore', '.hairlines'],
minPixelValue: 1,
mediaQuery: false
})
]
}
优点:
- vw \ vh相对于视口的宽高,通过 postcss-px-to-viewport 插件进行单位转换比较方便。
缺点:
- 直接进行单位换算时百分比可能出现小数。
- 兼容性 - ie11和少数低版本手机系统 ios8、android4.4 以下不支持。
2.2 视口
标签中定义了一些元数据信息,通过设置,提供有关 视口初始大小 的信息,供移动设备 使用。属性值为:属性 | 属性值 | 描述 |
---|---|---|
width |
数值 / device-width |
视口宽度 |
height |
数值 / device-height |
视口高度 |
initial-scale |
0.0 ~ 10.0 | 设备宽度与视口大小之间的缩放比率 |
maximum-scale |
0.0 ~ 10.0 | 缩放最大值 |
minimum-scale |
0.0 ~ 10.0 | 缩放最小值 |
user-scalable |
布尔值 | 默认yes ,为no 时用户不能缩放网页 |
移动端涉及布局视口(Layout Viewport)、视觉视口(Visual ViewPort)和理想视口(Ideal ViewPort)。
- 布局视口指用视口元标签(viewport meta)来进行布局视口设置,
css
布局是相对于布局视口计算。 - 视觉视口指用户当前看到的区域。
- 理想视口指屏幕分辨率的值,通过设置
<meta name = "viewport" content = "width = device-width, initial-scale = 1.0">
实现。
2.3 手机屏幕特性
像素分辨率
硬件所支持的,屏幕每行的像素*
每列的像素点数,单位是px
。
逻辑分辨率
设备独立的,软件可以达到的,个人理解是使得软件/页面在不同屏幕上显示出来的效果一致。
倍率
像素分辨率÷
逻辑分辨率等于倍率,如@3x
表示分辨率的3
倍。一个已知物理像素大小的元素,如果在普通屏中其设备像素等于css
像素,但在一些高清屏中,如 Retina 显示屏,一个css像素对应2
或3
个设备像素,这时显示出来的元素会变小。为了让元素如期待显示,需要传入原始设计稿尺寸×
倍率的设计稿,根据 DPR 的定义,这样加载后能够达到同样的效果。
尺寸
手机屏幕对角线长度换算成英寸的大小
三、适配方案
3.1 百分比方案
原理
使用 百分比% 定义 宽度,高度 用**
px
**固定,根据可视区域实时尺寸进行调整,尽可能适应各种分辨率,通常使用max-width
/min-width
控制尺寸范围过大或者过小。下表是子元素不同属性设置百分比的依据
属性 | 设置参考 |
---|---|
height /width |
基于子元素的直接父元素,width 相对于父元素的width ,height 相对于父元素的height |
top /bottom 和left /right |
相对于直接非static 定位的父元素的height /width |
padding /margin |
不论是垂直方向或者是水平方向,都相对于直接父亲元素的width ,与父元素的height 无关。 |
border-radius |
相对于自身的宽度 |
优势
简单,不存在兼容问题
不足
- 如果屏幕尺度跨度太大,相对设计稿过大或者过小的屏幕不能正常显示,在大屏手机或横竖屏切换场景下可能会导致页面元素被拉伸变形,字体大小无法随屏幕大小发生变化。
- 设置盒模型的不同属性时,其百分比设置的参考元素不唯一,容易使布局问题变得复杂
3.2 rem 方案
原理
rem 是相对长度单位,rem方案中的样式设计为相对于 根元素
font-size
计算值的倍数。根据 屏幕宽度 设置html
标签的font-size
,在布局时使用 rem 单位布局,达到自适应的目的,是 弹性布局 的一种实现方式。
实现过程: 首先获取文档根元素和设备
dpr
,设置 rem,在html
文档加载和解析完成后调整body
字体大小; 在页面缩放 / 回退 / 前进的时候, 获取元素的内部宽度 (不包括垂直滚动条,边框和外边距),重新调整 rem 大小。
实现方法:用 css 处理器或 npm 包将页面 css 样式中的
px
自动转换成 rem。在整个 flexible 适配方案中,文本使用px
作为单位,使用[data-dpr]
属性来区分不同dpr
下的文本字号。由于手机浏览器对字体显示最小是8px
,因此对于小尺寸文字需要采用px
为单位,防止通过 rem 转化后出现显示问题。手机淘宝中的字体使用px
为单位,腾讯新闻中的字体使用rem
为单位。
贴上源码分析:
(function(win, lib) {
var doc = win.document; //当前文档对象
var docEl = doc.documentElement; //文档对象根元素的只读属性
var metaEl = doc.querySelector('meta[name="viewport"]');
var flexibleEl = doc.querySelector('meta[name="flexible"]');
var dpr = 0;
var scale = 0;
var tid;
var flexible = lib.flexible || (lib.flexible = {});
if (metaEl) {
//当meta中viewport的标签设置了scale时,将根据scale手动设置dpr
console.warn('将根据已有的meta标签来设置缩放比例');
var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/);
if (match) {
scale = parseFloat(match[1]);
dpr = parseInt(1 / scale);
}
} else if (flexibleEl) {
//当meta中flexible的标签存在时,据此设置dpr
var content = flexibleEl.getAttribute('content');
if (content) {
var initialDpr = content.match(/initial\-dpr=([\d\.]+)/);
var maximumDpr = content.match(/maximum\-dpr=([\d\.]+)/);
if (initialDpr) {
dpr = parseFloat(initialDpr[1]);
scale = parseFloat((1 / dpr).toFixed(2));
}
if (maximumDpr) {
dpr = parseFloat(maximumDpr[1]);
scale = parseFloat((1 / dpr).toFixed(2));
}
}
}
if (!dpr && !scale) {
//根据js获取到的devicePixelRatio设置dpr及scale,scale是dpr的倒数
var isAndroid = win.navigator.appVersion.match(/android/gi);
var isIPhone = win.navigator.appVersion.match(/iphone/gi);
var devicePixelRatio = win.devicePixelRatio;
if (isIPhone) {
// iOS下,对于2和3的屏,分别用2和3倍方案
if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {
dpr = 3;
} else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)){
dpr = 2;
} else {
dpr = 1;
}
} else {
// 其他设备下,仍旧使用1倍的方案
dpr = 1;
}
scale = 1 / dpr;
}
docEl.setAttribute('data-dpr', dpr);
//文本字号不建议使用rem,flexible适配方案中,文本使用px作为单位,使用[data-dpr]属性来区分不同dpr下的文本字号
if (!metaEl) {
//添加meta标签,设置name为viewport,content根据scale设置缩放比(默认、最大、最小缩放比)
metaEl = doc.createElement('meta');
metaEl.setAttribute('name', 'viewport');
metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
if (docEl.firstElementChild) {
docEl.firstElementChild.appendChild(metaEl);
} else {
var wrap = doc.createElement('div');
wrap.appendChild(metaEl);
doc.write(wrap.innerHTML);
}
}
function refreshRem(){
//更新rem值
var width = docEl.getBoundingClientRect().width;
if (width / dpr > 540) {
width = 540 * dpr;
}
var rem = width / 10; //1rem = viewWidth / 10
docEl.style.fontSize = rem + 'px';
flexible.rem = win.rem = rem;
}
//resize与pageshow延时300ms触发refreshRem(),使用防抖函数,防止事件被高频触发可能引起性能问题
win.addEventListener('resize', function() {
clearTimeout(tid);
tid = setTimeout(refreshRem, 300);
}, false);
win.addEventListener('pageshow', function(e) {
//当一条会话历史纪录被执行的时候触发事件,包括后退/前进按钮,同时会在onload页面触发后初始化页面时触发
if (e.persisted) {//表示网页是否来自缓存
clearTimeout(tid);
tid = setTimeout(refreshRem, 300);
}
}, false);
//在html文档加载和解析完成后设置body元素字体大小
if (doc.readyState === 'complete') {
doc.body.style.fontSize = 12 * dpr + 'px';
} else {
doc.addEventListener('DOMContentLoaded', function(e) {
doc.body.style.fontSize = 12 * dpr + 'px';
}, false);
}
//浏览器有最小字体限制,css在pc上font-size是12px(移动端最小是8px), 也就是css像素是12,其DPR为1,在移动端dpr有可能为2和3,为了保证字体不变小,需要用12*dpr进行换算。
refreshRem();
//实现rem与px相互转换
flexible.dpr = win.dpr = dpr;
flexible.refreshRem = refreshRem;
flexible.rem2px = function(d) {
var val = parseFloat(d) * this.rem;
if (typeof d === 'string' && d.match(/rem$/)) {
val += 'px';
}
return val;
}
flexible.px2rem = function(d) {
var val = parseFloat(d) / this.rem;
if (typeof d === 'string' && d.match(/px$/)) {
val += 'rem';
}
return val;
}
})(window, window['lib'] || (window['lib'] = {}));
优势
兼容性好
ios: 6.1系统以上都支持
android: 2.1系统以上都支持
大部分主流浏览器都支持
- 相较于之前的静态布局和百分比方案,页面不会因为伸缩发生变形,自适应效果更佳。
不足
- 不是纯css移动适配方案,需要引入js脚本 在头部内嵌一段
js
脚本 监听分辨率的变化来动态改变根元素的字体大小,css
样式和js
代码有一定 耦合性,并且必须将改变font-size
的代码放在 css 样式之前。- 小数像素问题:通过 rem 计算后可能会出现小数像素,浏览器会对这部分小数四舍五入,按照整数渲染。浏览器在渲染时所做的摄入处理只是应用在元素的尺寸渲染上,其真实占据的空间依旧是原始大小。也就是说如果一个元素尺寸是
0.625px
,那么其渲染尺寸应该是1px
,空出的0.375px
空间由其临近的元素填充;同样道理,如果一个元素尺寸是0.375px
,其渲染尺寸就应该是0
,但是其会占据临近元素0.375px
的空间。会导致:缩放到低于1px
的元素时隐时现(解决办法:指定最小转换像素,对于比较小的像素,不转换为 rem 或 vw);两个同样宽度的元素因为各自周围的元素宽度不同,导致两元素相差1px
;宽高相同的正方形,长宽不等了;border-radius: 50%
画的圆不圆。- Android 浏览器下 line-height 垂直居中偏离的问题。常用的垂直居中方式就是使用line-height,这种方法在Android设备下并不能完全居中。
- cursor: pointer 元素点击背景变色的问题,对添加了 cursor:pointer 属性的元素,在移动端点击时,背景会高亮。为元素添加
tag-highlight-color:transparent
属性可以隐藏背景高亮。
3.3 vh / vw 方案
原理
视口是浏览器中用于呈现网页的区域,移动端的视口通常指的是 布局视口
- vw : 1vw 等于 视口宽度 的 1%
- vh : 1vh 等于 视口高度 的 **1% **
- vmin : 选取 vw 和 vh 中 最小 的那个
- vmax : 选取 vw 和 vh 中 最大 的那个
使用 css 预处理器把设计稿尺寸转换为 vw 单位,包括 文本,布局高宽,间距 等,使得这些元素能够随视口大小自适应调整。以1080px
设计稿为基准,转化的计算表示为:
// 以1080px作为设计稿基准
$vw_base: 1080
@function vw($px) {
@return($px / 1080) * 100vw
}
优势
- 纯 css 移动端适配方案,不存在脚本依赖问题。
- 相对于 rem 以根元素字体大小的倍数 定义 元素大小,逻辑清晰简单,视口单位依赖于视口的尺寸
"1vw = 1/100 viewport width"
,根据 视口尺寸的百分比 来定义 元素宽度。
不足
- 存在一些兼容性问题,Android4.4以下不支持
3.4 rem + vw / vh 方案
原理
vw / vh 方案能够实现宽度和高度的自适应,并且逻辑清晰,由于其被支持得较晚,所以存在一定的兼容性问题。将 vw/vh 方案与 rem 方案相结合,给根元素设置随视口变化的 vw 单位,可以通过
postcss-plugin-vwtorem
将其转换。具体的计算过程为:
对于
1080px
宽的设计稿,设置默认根字号的大小为100px
,那么设计稿中1px
对应的是100vw/1080 = 0.0925926vw
,并且1rem = 100px
,也就可以得到1rem = 9.256926vw
同时可以使用媒体查询限制根元素的最大最小值,实现对页面的最大最小宽度限制,对用户的视觉体验更好。
实用性
rem 弹性布局方式作为移动端web页面适配方法,后期从 rem 过渡到 vw ,只需要通过 改变根元素大小的计算方式 不需要其他处理。vw 将会成为一种更好的适配方式,目前由于兼容性的原因得不到广泛应用。rem + vw / vh 不存在 vw/vh 的兼容性问题,可以成为由 rem 向 vw / vh 转变的一种过渡方案。
3.5 基于媒体查询的响应式设计
响应式设计 使得一个网站同时适配 多种设备 和 多个屏幕,让网站的布局和功能随用户的使用环境(屏幕大小、输出方式、设备/浏览器能力而变化),使其视觉合理,交互方式符合习惯。如使得内容区块可伸缩与自由排布,边距适应页面尺寸,图片适应比例变化,能够自动隐藏/部分显示内容等。
原理
主要实现是通过 媒体查询,通过给不同分辨率的设备编写不同的样式实现响应式布局,用于解决不同设备不同分辨率之间兼容问题,一般是指PC、平板、手机设备之间较大的分辨率差异。实现上不局限于具体的方案,通常结合了 流式布局
+
弹性布局 方案。比如给小屏幕手机设置@2x
图,为大屏手机设置@3x
图
@media only screen and (min-width: 375px){
样式1
}
@media only screen and (min-width: 750){
样式2
}
优势
能够使网页在不同设备、不同分辨率屏幕上呈现合理布局,不仅仅是样式伸缩变换
不足
- 要匹配足够多的设备与屏幕,一个web页面需要多个设计方案,工作量比较大
- 通过媒体查询技术需要设置一定量的断点,到达某个断点前后的页面发生显著变化
四、移动端web页面适配方案中的通用问题
4.1 像素问题
设置边框 为
1px
css像素,在 普通屏幕 下1px
,高清屏幕 (dpr
为2
)下2px
的情况。是由于不同移动设备的dpr
不同,导致1px
css像素,转换成物理像素后显示不一样。
4.1.1 设置scale 为 1/dpr
css 中涉及
1
像素的地方仍然使用px
作为单位,设置<meta>
标签中initial-scale = 1/dpr
,将整个页面缩小dpr
倍,对于页面采用 rem 方案的情况,将页面的 根字体 再放大dpr
倍,这个时候就能够在不改变页面其他布局的情况下,保持边框的 css 像素为1px
。
4.1.2 transform 的 scale 属性
transform
的scale
属性允许对元素进行缩放,其中scaleY(y)
通过设置Y轴的值来定义缩放转换,并结合伪元素使用,通过transform-origin: 50% 0%
修改元素变换的中心点实现。针对横着的边框线用scaleY(y)
,针对竖着的边框线要用scaleX(x)
,针对一圈的边框线用scale()
,并且需要注意转移元素变换中心点。
<style>
//针对竖着的边框线
.className: before {
//其他样式
transform-origin: 50% 0%;
}
@media only screen and (-webkit-min-device-pixel-ratio: 2) { //dpr为2时
.className: before {
transform: scaleY(0.5);
}
}
@media only screen and (-webkit-min-device-pixel-ratio: 3) { //dpr为3时
.className: before {
transform: scaleY(0.33);
}
}
</style>
4.1.3 border-image 属性
使用
border-image
,在元素的边框上设置一个一半透明一半显示的图片。
4.2 对图片的处理
加载网页时,平均
60%
以上的流量来自 加载图片。指定图像宽度时使用相对单位,防止意外溢出视口,如width: 50%
,将图片宽度设置为包含元素宽度的50%
。因为 css 允许内容溢出容器, 需要使用**max-width: 100%
来保证图像及其他内容不会溢出。使用 img 元素的alt
属性提供描述,描述有助于提高网站的可访问性,能提供语境给屏幕阅读器及其他辅助性技术。维护自适应页面中图片宽高比固定比较常用的方法是使用padding
**设置。对于不同dpr
以及不同分辨率/尺寸的屏幕,为了避免资源浪费和等待时间延长,需要针对不同的屏幕使用合适的图片,加载的图片分为通过标签引入的图片和背景图片。
4.2.1 srcse 和 sizes
对于
<img>
引入的图片,如果想要图片适应不同像素密度的屏幕,并且屏幕上显示图片的实际尺寸相同,使用srcset
属性用来指定多张图像。它的值是一个逗号分隔的字符串,每个部分都是一张图像的 URL,后面接一个空格,后接是像素密度描述符。浏览器根据当前设备的像素密度,选择需要加载的图像。如果srcset
属性都不满足条件,那么就加载src
属性指定的默认图像。
<img srcset="foo-320w.jpg,
foo-480w.jpg 1.5x,
foo-640w.jpg 2x"
src="foo-640w.jpg">
<!--srcset属性给出了三个图像URL,适应三种不同的像素密度, 后面的像素密度描述符,格式是像素密度倍数 + 字母x。1x表示单倍像素密度,可以省略。-->
如果想要针对不同屏幕,使用不同分辨率版本和尺寸的图片,使用属性
srcse
和sizes
。srcset 定义了允许浏览器选择的图像集,以及每个图像的大小(使用w单位)。sizes
定义了一组媒体条件(例如屏幕宽度),指明当某些媒体条件为真时,什么样的图片尺寸是最佳选择。
<img srcset = "elva-fairy-320w.jpg 320w,
elva-fairy-480w.jpg 480w,
elva-fairy-800w.jpg 800w"
sizes = "(max-width: 320px) 280px,
(max-width: 480px) 440px,
800px"
src = "elva-fairy-800w.jpg" alt="Elva dressed as a fairy">
浏览器的查询过程:
- 查看设备宽度;
- 检查
sizes
列表中哪个媒体条件是第一个为真; - 查看给予该媒体查询的槽大小;
- 加载
srcset
列表中引用的最接近所选的槽大小的图像
4.2.2 异步加载
< img> 引入的图片,使用js自带的异步加载图片。根据不同的
dpr
,加载不同分辨率的图片。
<img id="img" data-src1x="xxx@1x.jpg" data-src2x="xxx@2x.jpg" data-src3x="xxx@3x.jpg"/>
var dpr = window.devicePixelRatio;
if(dpr > 3){
dpr = 3;
};
var imgSrc = $('#img').data('src'+dpr+'x');
var img = new Image();
img.src = imgSrc;
img.onload = function(imgObj){
$('#img').remove().prepend(imgObj);//替换img对象
};
4.2.3 picture
为不同的视口提供不同的图片,使用
<picture>
标签。<picture>
是html5
中定义的一个容器标签,内部使用<source>
和<image>
,浏览器会匹配<source>
的type
,media
,srcset
等属性,找到最适合当前布局 / 视口宽度 / 设备像素密度的图像进行加载。这里的<img>
标签是浏览器不支持picture
元素,或者支持picture
但没有合适的媒体定义时的后备,不能省略。
<picture>
<source media="(min-width: 30px)" srcset="cat-vertical.jpg">
<source media="(min-width: 60px)" srcset="cat-horizontal.jpg">
<img src="cat.jpg" alt="cat">
</picture>
4.2.4 Image-set
对于背景图片,使用
image-set
根据用户设备的分辨率匹配合适的图像, 同时要考虑兼容性问题。
<style>
.css {
background-image: url(1x.png); /*不支持image-set的情况下显示*/
background: -image-set(
url(1x.png) 1x,/* 支持image-set的浏览器的[普通屏幕]下 */
url(2x.png) 2x,/* 支持image-set的浏览器的[2倍Retina屏幕] */
url(3x.png) 3x/* 支持image-set的浏览器的[3倍Retina屏幕] */
);
}
</style>
4.2.5 media query
对于背景图片,使用媒体查询自动切换不同分辨率的版本
<style>
/* 普通显示屏(设备像素比例小于等于1)使用1倍的图 */
.css{
background-image: url(img_1x.png);
}
/* 高清显示屏(设备像素比例大于等于2)使用2倍图 */
@media only screen and (min-device-pixel-ratio:2){
.css{
background-image: url(img_2x.png);
}
}
/* 高清显示屏(设备像素比例大于等于3)使用3倍图 */
@media only screen and (min-device-pixel-ratio:3){
.css{
background-image: url(img_3x.png);
}
}
</style>
五、总结
对于国内的一些网站,查看源码发现,它可能不是某一种方案的单独使用,而是多种方案的结合。一个页面上,元素的宽度设置上有百分比,也有
rem
,字体的样式中有rem
,有em
,还有固定大小的px
,宽度缩放上也会用到媒体查询。当然,市场上对于适配问题已经有了大量成熟的 webpack 插件和 npm 包,本文的初衷在于了解设备适配的历史发展过程,秉着不要重复造轮子的原则建议大家直接用插件包也方便后续他人维护 (ಡωಡ)各种方案的具体代码示例可以参考这篇文章。