- 一份支持响应式页面的PSD设计稿、或多份具体响应式细节的PSD设计稿(少见)。
- 宽屏:按比例放大项、或增加项数量。
<meta>的viewport
值方案:理想视口方案、或1/DPR
缩放方案。- 响应式设计三大要素:媒体查询、流式布局、弹性图片。
- 不同PPI使用不同分辨率图片。
font-size
阶梯取值方案(位图字体):CSS媒体查询额外设置方案、或JS设置html的data-dpr统一控制方案。- 半像素处理。
- rem+@media方案。
- 雪碧图使用百分比background-position
- 使用rem导致的CSS小数、百分比的四舍五入问题处理。
- 自适应宽度布局(
flex
优雅解决布局、自适应问题)。 - 是否横竖屏切换显示(特殊单屏应用,如:游戏)。
-
使用
vm
(或vh
)参考:大漠:再聊移动端页面的适配。
-
设置为理想视口:
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
-
用
vw
(或vh
、vmin
、vmax
)设置html的font-size
:root { --psd: 设计稿宽度px; /* 无单位 */ --rfz: 缩小倍数; /* 无单位。可以设置为100:方便计算、也因为PC浏览器通常都有最小字体限制 */ } html { /* 单位:vw */ font-size: 具体vw; /* 兼容性 */ font-size: calc(100vw / 设计稿宽度px * 缩小倍数); /* 兼容性 */ font-size: calc(100vw / var(--psd) * var(--rfz)); }
-
若原本是PC页面而增加的WAP适配,就在html外添加媒体查询。
-
若要限制最大宽度(或高度)的页面,可以媒体查询限定html的
font-size
为具体px
。要限制最小宽度(或高度)同理,改变
min-
为max-
、改变最大
为最小
。/* 在上面的基础上增加媒体查询限制最大宽度 */ @media (min-width: 最大宽度px) { html { /* 单位:px(不能换算成rem) */ font-size: 具体px; /* 兼容性 */ font-size: calc(最大宽度px / 设计稿宽度px * 缩小倍数); /* 兼容性 */ font-size: calc(最大宽度px / var(--psd) * var(--rfz)); } } /* 或 */ @media (min-height: 最大高度px) { html { /* 单位:px(不能换算成rem) */ font-size: 具体px; /* 兼容性 */ font-size: calc(最大宽度px / 设计稿宽度px * 缩小倍数); /* 兼容性 */ font-size: calc(最大高度px / var(--psd) * var(--rfz)); } }
-
-
元素用设计稿的px除以缩小倍数(
--rfz
),单位用rem
(手动计算或px转rem方案)。
原理思路:把设计稿转化为宽度的百分比(vw)显示在移动端上。
-
针对1080px的设计稿,初始设定:
--psd: 1080; --rfz: 100;
:300px的设计稿距离计算如下
-
1080px设计稿中300px占用:
300px / 1080px ≈ 0.278
; -
300px的设计稿距离写成CSS为:
300 / var(--rfz) + rem => 300 / 100 + rem = 3rem
;3rem => 3 * html的font-size => 3 * calc(100vw / var(--psd) * var(--rfz)) => 3 * 100vw / 1080 * 100 ≈ 27.8vw
; -
27.8vw === 设计稿宽度的0.278
,从而设计稿对应了响应式页面。
-
-
-
已过时的方案-
类似的还有:hotcss。
-
用JS动态设置
<meta>的viewport
缩放比为1/DPR
,解决0.5px问题。缩放比例
1/DPR
原因:在这个缩放比例下,CSS中的1px设备独立像素等于屏幕的1px物理像素。事实上取消了px作为设备独立像素的含义,变成物理像素的值。能够画出1px物理像素的内容。 -
用JS根据不同布局视口宽度动态设置html的font-size值,把要做成响应式的内容转换为rem单位。
把视觉稿分成100份来看待(为了以后兼容vh,vw单位)。每一份被称为一个单位a,1rem单位认定为10a。拿750px宽度的视觉稿举例:
750px = 100a => 75px = 10a = 1rem => 1rem = 75px
,因此当设计稿宽度为750px时,量取的长度要除以75并加上rem单位。 -
用JS在
<html>
上动态设置data-dpr,对额外需要根据DPR变化而改变的内容设置样式(如:字体)。@mixin font-size($font-size) { font-size: $font-size; [data-dpr="2"] & { font-size: $font-size * 2; } [data-dpr="3"] & { font-size: $font-size * 3; } }
因为字体很多都是点阵字体或位图字体,通常有16px、24px的清晰字号,其他字号大小是位图缩放的结果,有损美观。
- 不需要设置
媒体查询改变html的font-size值。
-
-
媒体查询设置不同布局视口宽度下html的font-size值,把要做成响应式的内容转换为rem单位
-
设置为理想视口:
<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
。若改变布局视口(放大),则只需媒体查询内容添加更大布局视口下html的font-size值。
-
切图取值用设计稿的px带入下面方法而生成的rem值。
/* 用Sass预处理语言换算px -> rem */ @function rem($px, $base-font-size: 20px) { @if unitless($px) { @return rem($px + 0px); } @if unit($px) != "px" { @error "rem()的参数单位必须是px或不带单位"; } // $base-font-size:切图时设计稿宽度对应的媒体查询中html的font-size @return $px / $base-font-size + rem; }
-
媒体查询仅设置不同布局视口宽度下html的的font-size值。
0.5px问题用WAP端半像素处理,或直接设置为1px。
-
-
当Android系统的样式出现问题又找不到原因时,有可能是系统字体大小被修改过。
-
视口(viewport)
网站外层的容器,控制网站的最高块状容器:
<html>
。-
PC端的viewport就是浏览器窗口的宽度高度,严格等于浏览器的窗口。
PC端无视
<meta>
的viewport设置。 -
WAP端的viewport太窄,为了能更好为CSS布局服务,所以提供了两个viewport
-
可视视口(visual viewport):
浏览器的窗口,可以放大缩小、滚动页面来改变可见部分。
-
通过
<meta name="viewport" content="initial-scale=缩放值, user-scalable=是否缩放, minimum-scale=缩放值, maximum-scale=缩放值">
控制缩放。视觉上,可视视口永远是浏览器大小,但宽度值等于逻辑像素宽度。
可视视口宽度(屏幕范围内CSS像素堆积的总宽度) =
浏览器宽度 / 现在的缩放值
-
JS获取:
window.innerHeight || document.documentElement.clientHeight || document.body.clientHeight
。随页面缩放而改变。
-
-
布局视口(layout viewport):
网站外层容器的固定大小,决定
<html>
宽度。不同浏览器默认大小不同,980px左右;不能小于可视视口。-
通过
<meta name="viewport" content="width=布局视口初始宽度值, initial-scale=缩放值">
设置并缩放成为最终布局视口宽度。布局视口宽度 =
初始宽度值 / 初始缩放值 > 可视视口宽度 : 初始宽度值 / 初始缩放值 ? 可视视口宽度
-
JS获取:
document.documentElement.clientWidth || document.body.clientWidth
。随写入的viewport的
宽度值 / 初始缩放值
改变而改变。 -
CSS媒体查询判断:
@media (min-width: 宽度px) and (max-width: 宽度px)
。随写入的viewport的
宽度值 / 初始缩放值
改变而改变。
-
概念理解
想象下layout viewport是一张大的不能改变大小和角度的图片。现在你有个更小的框来观看这张大图片,这个框被不透明的材料包围,因此你只能看到大图片的一部分。你通过这个框看到的大图片的部分被称为visual viewport。你能拿着这个框站得离大图片远点(用户的缩小页面功能),以一次性看到这个大图片的更多区域;或你能站得近点(用户的放大页面功能),以看到更少区域。你能改变这个框,但这张大图片的大小和形状都不会改变。
-
理想视口(ideal viewport):
<meta name="viewport" content="width=device-width,initial-scale=1.0">
- 可视视口,初始不缩放。
- 布局视口,设置为屏幕宽度。
-
-
-
像素(pixel)
像素是数字图像的最小组成单元,它不是一个物理尺寸,但和物理尺寸存在一个可变的换算关系(物理尺寸之间的换算是固定的),也是分辨率的单位。
-
CSS像素(前端用的
px
)viewport的像素。浏览器内部对逻辑像素进行再处理的结果(调整逻辑像素的缩放来达到适应设备的一个中间层)。
width=device-width
:让viewport的宽度等于逻辑像素的总宽度。 -
设备独立像素(Device-Independent Pixels,DIPs),密度无关像素(Density-Independent Pixels,DIPs)
逻辑像素,一种度量单位。由程序使用的虚拟像素,然后由相关系统转换为物理像素呈现。
iOS用
pt
(point),Android用dp
(density-independent pixel),前端用px
。 -
物理像素(physical pixels),设备像素(device pixels)
- 对物理显示设备而言,是固定的。反映显示设备内部led灯的数量,由操作系统控制来发光填色值。
- 对系统层面而言,是系统解析后提供给应用展示的。所以改变屏幕分辨率后,应用面对的物理像素也改变了。
e.g. iPhone 13 Mini的渲染像素是
1125x2436
,对应的逻辑像素是375x812@3x
(css或js获取的像素也是这个逻辑像素值),但是它的物理像素是1080x2340
(特殊,设计成渲染分辨率和物理分辨率不一致)。 -
-
比例
-
DPR(Device Pixel Ratio,设备像素比)、dppx(dots per pixel,每物理像素包含的逻辑像素数量)、dpi(dots per inch,每英寸包含逻辑像素数量)
DPR = 物理像素 / 设备独立像素
、1DPR = 1dppx = 96dpi
- JS获取:
window.devicePixelRatio
。 - CSS媒体查询:
@media (min或max-resolution: 整数dppx)
、@media (min或max-resolution: 整数dpi)
。
- 改变浏览器缩放,会改变DPR。
- 对于固定的逻辑像素,当系统分辨率改变时(导致物理像素变化),DPR不变。
- JS获取:
-
PPI(Pixels Per Inch,每英寸物理像素,物理像素密度)
PPI =
Math.sqrt(物理像素宽的平方 + 物理像素高的平方) / 屏幕对角线长度的英寸
表示每英寸所拥有的物理像素数目。
-
-
主屏分辨率
物理像素(或系统提供的、被当做物理像素的值)的宽x高。
rem:相对于根元素的字体大小的单位。rem单位转换为具体px值:rem乘于html的font-size像素。
-
SCSS在
.scss
文件进行@function rem($px, $base-font-size: 20px) { @if unitless($px) { @return rem($px + 0px); } @if unit($px) != "px" { @error "rem()的参数单位必须是px或不带单位"; } // $base-font-size:切图时设计稿宽度对应的媒体查询中html的font-size 或 --rfz+px @return $px / $base-font-size + rem; } a { width: rem(20); } /* a { width: 1rem; } */
-
px2rem(或px2rem-postcss)在
.css
文件进行/* px2rem设置:remUnit: 20,baseDpr: 1 */ a { width: 20px; height: 40px; /*px*/ padding: 60px; /*no*/ margin: 80PX; } /* a { width: 1rem; padding: 60px; margin: 80PX; } [data-dpr="1"] a { height: 40px; } [data-dpr="2"] a { height: 80px; } [data-dpr="3"] a { height: 120px; } */
注意可能对base64等文本进行没必要的转化,按需加上
/*no*/
。
- 媒体查询方式
-
CSS属性:
@media (min-width: 360px) and (max-width: 640px) {...}
-
HTML标签:
<link rel="stylesheet" type="text/css" media="(min-width: 360px) and (max-width: 640px)" href="...">
-
- 响应式设计三大要素
-
媒体查询
-
流式布局:节点用
百分比
或rem
或flex
或grid
布局使用自适应结构,如:自适应宽度布局。
-
弹性图片:
img {max-width: 100%;}
-
@1、@2、@3不同分辨率的图片放大倍数是1倍、2倍、3倍。
若图片是雪碧图
- 则其间距也要是和放大倍数一致,@1、@2、@3分别间距是2px、4px、6px。
- 则其
background-size
设置为最低分辨率整个雪碧图的高宽)、background-position
设置为最低分辨率时的值。单位用px
、rem
、百分比
都可以。
-
媒体查询
@mixin image($url) { background-image: url($url + "@1.png"); @media (min-resolution: 2dppx) { background-image: url($url + "@2.png"); } @media (min-resolution: 3dppx) { background-image: url($url + "@3.png"); } } .img { @include image("去除后缀的图片地址"); }
-
<img>
的一些属性:srcset
、sizes
;background-image
的值:image-set()
-
SVG(矢量图)代替位图使用
-
高倍图在低PPI设备下显示,不会导致图片模糊或锯齿,因此有时可简化地把高倍图用在所有机型上
-
高倍图使用在低PPI设备下可能导致的问题:
- 越大的图,加载到内存需要越大的内存空间
- 越大的图,显示时发送到GPU需要更大的计算空间
- 大图显示在更小的区域,需要系统计算图片缩放
-
iOS、Android可以在系统设置中修改字体大小
、视图大小/显示大小
,会导致H5页面的样式数值发生变化(PC版的缩放同理)。
-
修改
字体大小
、视图大小/显示大小
后,手机内H5现象:-
iOS
仅变化视窗,节点样式数值不变化
-
Android
-
某些机型:
- 节点
font-size
数值变化 - 节点其他样式属性,若数值是
rem
单位时也跟着变化,否则不变化
- 节点
-
某些机型:
仅变化视窗,节点样式数值不变化
line-height
可能都有问题
-
-
解决方案:利用 html的font-size
+ 元素的font-size用rem
单位、元素的其他样式属性数值不要用 单位(e.g. 用rem
vw
、px
、等) 的响应式方案,并根据系统放大/缩小的倍数,对html的font-size
进行逆向处理:
-
先设置某节点
font-size
值,再获取该节点真实font-size
值,他们的倍数就是系统放大/缩小的倍数,需要逆向处理这个倍数; -
获得前面的倍数,若放大/缩小多少,则html的
font-size
等效缩小/放大多少;e.g. 一开始设置50px;计算得到100px,得知放大了2倍,这时再最终设置为25px。节点的1rem=1rem x 25px x 2倍=50px,与一开始1rem=50px的期望相同。
-
节点的
font-size
数值用rem
,其他样式属性数值不要用(可以用rem
vw
的响应式方案);因为其他样式属性数值,用
rem
时,某些机型跟随系统变化,某些机型又不跟随,但是用vw
、px
等其他单位时还未发现会跟随变化。 -
规避系统通过其他方式放大/缩小页面的手段(e.g. iOS会通过
text-size-adjust
同步系统放大/缩小)。
实现
<style>
html {
text-size-adjust: 100% !important;
-ms-text-size-adjust: 100% !important;
-webkit-text-size-adjust: 100% !important;
-moz-text-size-adjust: 100% !important;
}
</style>
<script type="text/javascript">
(function (window, document, widthOrHeight) {
var eventType =
"orientationchange" in window ? "orientationchange" : "resize",
html = document.documentElement;
function listener() {
switch (widthOrHeight) {
case "height":
var height = Math.max(html.clientHeight, window.innerHeight),
htmlFontSize = 100 * (height / 1334) || 50; // 设计稿
break;
default:
var width = Math.max(html.clientWidth, window.innerWidth),
htmlFontSize = 100 * (width / 750) || 50; // 设计稿
}
// if (htmlFontSize < 30) { // 限制最小
// htmlFontSize = 30
// }
// if (htmlFontSize > 70) { // 限制最大
// htmlFontSize = 70
// }
// 先设置一个设计稿的font-size
html.style.fontSize = htmlFontSize + "px";
// 获取设置font-size之后计算得出的真实font-size
var realHtmlFontSize = +window.getComputedStyle(html).fontSize.replace("px", "");
// 若不相等则说明被放大/缩小了,那么就逆向设置一个新的font-size
if (htmlFontSize !== realHtmlFontSize) {
html.style.fontSize =
htmlFontSize * (htmlFontSize / realHtmlFontSize) + "px"; // e.g. 一开始设置50px;计算得到100px,得知放大2倍,这时再最终设置为25px
}
}
window.addEventListener(eventType, listener, false);
listener();
})(window, document, "width");
</script>
节点的font-size数值都用rem作为单位,节点的其他样式属性数值不要用rem,可规避系统导致的放大/缩小(就是使系统导致的变化失效)
非纯前端解决方案:让客户端打开WebView时就禁止
变化。e.g. WebView初始化时客户端设置字体大小
、视图大小/显示大小
textZoom: 100