移动端适配
为什么要做移动端适配?
目前市面上移动端屏幕尺寸非常的繁多,很多时候我们希望一个元素在不同的屏幕上显示不同的大小以此来更好的还原效果图,因此我们需要通过移动端适配,通过自适应和响应式,确保应用在不同设备上所占据视口的空间比例是相同的
- 自适应:根据不同的设备屏幕大小来自动调整尺寸、大小
- 响应式:会随着屏幕的实时变动而自动调整,是一种更强的自适应
当前流行的几种适配方案
方案一:百分比设置(不推荐),因为不同属性的百分比值,相对的可能是不同参照物,所以百分比往往很难统一
方案二:rem单位+动态html的font-size (兼容性更好)
方案三:vw单位(新项目推荐)
方案四:flex的弹性布局
rem + 动态设置 font-size
rem 单位是相对于 html 元素的 font-size 来设置的,通过在不同屏幕尺寸下,动态的修改 html 元素的 font-size 以此来达到适配效果
在开发中,我们只需要考虑两个问题:
- 针对不同的屏幕,设置 html 不同的 font-size
- 将原来设置的尺寸,转化成 rem 单位
px 与 rem 的单位换算
方案一:手动换算
- 根元素 html 的文字大小 = 视口宽度/分成的份数(一般为10份,方便计算)
- rem 值 = 元素的 px 值 / 根元素 html 的文字大小
比如有一个在375px屏幕上,100px宽度和高度的盒子,我们需要将100px转成对应的rem值:100/37.5=2.6667,其他也是相同的方法计算即可
.pxToRem(@px) {
result: 1rem * (@px / 37.5);
}
.box {
width: .pxToRem(100)[result];
height: .pxToRem(100)[result];
background-color: orange;
}
p {
font-size: .pxToRem(14)[result];
}
方案二: postcss-pxtorem
- 目前在前端的工程化开发中,我们可以借助于webpack的工具来完成自动的转化
- npm install postcss-pxtorem
方案三: VSCode插件
设置不同 font-size
方案一:媒体查询
思路:通过媒体查询来设置不同尺寸屏幕下 html 的 Root font-size
缺点:
- 需要针对不同的屏幕编写大量的媒体查询
- 如果动态改变尺寸,不会实时更新,只是一个个区间
@media screen and (min-width: 320px) {
html {
font-size: 20px;
}
}
@media screen and (min-width: 375px) {
html {
font-size: 24px;
}
}
@media screen and (min-width: 414px) {
html {
font-size: 28px;
}
}
@media screen and (min-width: 480px) {
html {
font-size: 32px;
}
}
.box {
width: 5rem;
height: 5rem;
background-color: blue;
}
方案二:编写 js 代码
思路:通过监听屏幕尺寸的变化来动态修改 html 元素的 font-size 大小
方法:
- 根据 html 的宽度计算出 font-size 的大小,并设置到 html 上
- 监听页面尺寸的变化,实时修改 font-size 大小
(function flexible (window, document) {
var docEl = document.documentElement
var dpr = window.devicePixelRatio || 1
// adjust body font size
function setBodyFontSize () {
if (document.body) {
document.body.style.fontSize = (12 * dpr) + 'px'
}
else {
document.addEventListener('DOMContentLoaded', setBodyFontSize)
}
}
setBodyFontSize();
// set 1rem = viewWidth / 10
function setRemUnit () {
var rem = docEl.clientWidth / 10
docEl.style.fontSize = rem + 'px'
}
setRemUnit()
// reset rem unit on page resize
window.addEventListener('resize', setRemUnit)
window.addEventListener('pageshow', function (e) {
if (e.persisted) {
setRemUnit()
}
})
// detect 0.5px supports
if (dpr >= 2) {
var fakeBody = document.createElement('body')
var testElement = document.createElement('div')
testElement.style.border = '.5px solid transparent'
fakeBody.appendChild(testElement)
docEl.appendChild(fakeBody)
if (testElement.offsetHeight === 1) {
docEl.classList.add('hairlines')
}
docEl.removeChild(fakeBody)
}
}(window, document))
方案三:lib-flexible 库
lib-flexible 是淘宝团队出品的一个移动端自适应解决方案,通过动态计算 viewport 设置 font-size 实现不同屏幕宽度下的 UI 自适应缩放。
(function flexible (window, document) {
var docEl = document.documentElement
var dpr = window.devicePixelRatio || 1
// adjust body font size
function setBodyFontSize () {
if (document.body) {
document.body.style.fontSize = (12 * dpr) + 'px'
}
else {
document.addEventListener('DOMContentLoaded', setBodyFontSize)
}
}
setBodyFontSize();
// set 1rem = viewWidth / 10
function setRemUnit () {
var rem = docEl.clientWidth / 10
docEl.style.fontSize = rem + 'px'
}
setRemUnit()
// reset rem unit on page resize
window.addEventListener('resize', setRemUnit)
window.addEventListener('pageshow', function (e) {
if (e.persisted) {
setRemUnit()
}
})
// detect 0.5px supports
if (dpr >= 2) {
var fakeBody = document.createElement('body')
var testElement = document.createElement('div')
testElement.style.border = '.5px solid transparent'
fakeBody.appendChild(testElement)
docEl.appendChild(fakeBody)
if (testElement.offsetHeight === 1) {
docEl.classList.add('hairlines')
}
docEl.removeChild(fakeBody)
}
}(window, document))
vw 适配方案
100vw 相当于整个视口的宽度 innerWidth,1vw 相当于视口宽度的 1%,将 px 转换为 vw 即可完成适配,其实上面的 rem 就是模仿 vw 方案
vw相比于rem的优势:
- 不需要去计算 html 的 font-size 大小,也不需要去给 html 设置 font-size
- 不会因为设置 html 的 font-size 大小,而必须再给 body 设置一个 font-size 防止继承
- 因为不依赖 font-size 的尺寸,所以不用担心某些原因的 html 的 font-size 尺寸被篡改,导致页面尺寸混乱
- vw 更加语义话,1vw 相当于 1/100 viewport 的大小
- rem 事实上作为一种过渡的方案,它利用的也是 vw 的思想
px 与 vw 的单位转换
方案一:手动换算
比如屏幕尺寸为 375px,元素大小为 150px,我们需要将 150px 转换成对应的 vw 值:150 / 3.75=40
@vwUnit: 3.75;
.pxToVw(@px) {
result: (@px / @vw) * 1vw
}
.box {
width: .pxToVw(100)[result];
height: .pxToVw(100)[result];
}
方案二: postcss-px-to-viewport-8-plugin
- 和rem一样,在前端的工程化开发中,我们可以借助于webpack的工具来完成自动的转化
- npm install postcss-px-to-viewport-8-plugin
方案三: VS Code 插件
px to vw 插件,在编写时自动转化:
H5高清适配方案
为什么需要高清适配
- 高清:在不同 dpr 的高清屏幕下,不让页面看上去有模糊感、粗颗粒的劣质感
- 适配:一份设计稿能同时支持在不同机型分辨率下带来的视觉变形、扭曲感(即希望一个元素在不同的屏幕上显示不同的大小以此来更好的还原效果图)
核心原理
根据 dpr,将 viewport 中 scale 改为 1/dpr, 将 px 逻辑像素处理成物理像素大小;同时根据设备宽度来动态设置Root font-size
/** dpr = 1 */
<meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=1.0,maximum-scale=1.0,minimum-scale=1.0,viewport-fit=cover">
/** dpr = 2 */
<meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=0.5,maximum-scale=0.5,minimum-scale=0.5,viewport-fit=cover">
/** dpr = 3 */
<meta name="viewport" content="width=device-width,user-scalable=no,initial-scale=0.3333333333333333,maximum-scale=0.3333333333333333,minimum-scale=0.3333333333333333,viewport-fit=cover">
以 iphone X 为例
- 从设计稿(750px)读数,直接写到 css
- 经过构建后,css 单位变成:(px/100)rem
- 根据 css 高清方案:当设备宽度为 375px ,对应的下 font-size 为 50px (1rem = 50px)
- 在设备上呈现对应尺寸
常见移动端兼容问题
移动端 1px 适配
在做移动端开发过程中,我们除了针对不同分辨率屏幕进行适配以外,通常还会经常遇到 1px 细线问题。
产生原因
视觉稿上的 1px 物理像素,当 dpr=2 时,物理像素应该为 0.5px,但是这种小数写法只在 ios 8+支持,部分旧安卓系统不支持,因此不同机型分辨率下可能 会出现 1px 变粗 或者直接消失不见的问题;
解决办法
使用伪元素+transform:
.line {
position: relative; /* 相对定位 */
&::after {
content: ' ';
position: absolute;
top: 0;
left: 0;
border: 1px solid #000;
width: 300%; /* 宽度变成 dpr x 100% */
height: 300%; /* 高度变成 dpr x 100% */
transform: scale(0.33333); /* scale变成 1/dpr */
transform-origin: left top; /* 更改元素的转换原点为左上角 */
}
}
IOS 底部安全区域
产生原因
IOS 部分机型如 IPhoneX,会有底部小黑条
解决办法
使用 safe-area-inset-*
.safe-area-inset(@marginBottom: 0px) {
margin-bottom: @marginBottom;
margin-bottom: calc(@marginBottom + constant(safe-area-inset-bottom));
margin-bottom: calc(@marginBottom + constant(safe-area-inset-bottom));
}