移动端适配
适配方案汇总
媒体查询百分比设置(不推荐)
rem 单位 + 动态 html 的 font-size(过渡方案)
viewport,vw 单位适配(推荐)
flex 的弹性布局
1. 媒体查询百分比设置(不推荐)
早期的移动端适配方案,已经被淘汰了!
思路:通过媒体查询来设置不同尺寸屏幕下 html 的 font-size
缺点:
- 不同属性的百分比值,相对的可能是不同参照物,所以百分比往往很难统一
- 需要针对不同的屏幕编写大量的媒体查询
- 如果动态改变尺寸,不会实时更新,只是一个个区间
2. rem + 动态设置 font-size(不推荐)
rem 单位是相对于 html 元素的 font-size 来设置的,通过在不同屏幕尺寸下,动态的修改 html 元素的 font-size 以此来达到适配效果
在开发中,我们只需要考虑两个问题:
- 针对不同的屏幕,设置 html 不同的 font-size
- 将原来设置的尺寸,转化成 rem 单位
function setRemUnit() {
// 获取所有的 html 元素
const htmlEl = document.documentElement
// 375 -> 16px
// 320px -> 12px
// 我们需要动态更改字体大小,因此需要获取网页的宽度
// 拿到客户端宽度
const htmlWidth = htmlEl.clientWidth
// 将宽度分成10份
const htmlFontSize = htmlWidth / 10
console.log('htmlFontSize', htmlFontSize);
// 将值给到html的font-size
htmlEl.style.fontSize = htmlFontSize + 'px'
}
setRemUnit()
// 给 window 添加监听事件
window.addEventListener('resize', setRemUnit)
px 与 rem 的单位换算
手动换算:
- 根元素 html 的文字大小 = 视口宽度/分成的份数(一般为10份,方便计算)
- rem 值 = 元素的 px 值 / 根元素 html 的文字大小
- 比如有一个在375px屏幕上,100px宽度和高度的盒子
- 我们需要将100px转成对应的rem值
- 100/37.5=2.6667,其他也是相同的方法计算即可
less/scss函数:
.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插件:
px to rem & rpx & vw (cssrem)
插件
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))
设备像素比 dpr:
dpr(device pixel ratio)表示设备像素比
,设备像素/设备独立像素,代表设备独立像素到设备像素的转换关系,在JS中可以通过 window.devicePixelRatio 获取- 计算公式为:
DPR = 物理像素/逻辑像素
- 当设备像素比为
1:1
时,使用 1(1×1)个设备像素显示 1 个CSS像素; - 当设备像素比为
2:1
时,使用 4(2×2)个设备像素显示 1 个CSS像素; - 当设备像素比为
3:1
时,使用 9(3×3)个设备像素显示 1 个CSS像素。
3. viewport,vw 单位适配(推荐)
100vw
相当于整个视口的宽度 innerWidth1vw
相当于视口宽度的1%
- 将 px 转换为 vw 即可完成适配
<meta name="viewport" content="width=device-width; initial-scale=1; maximum-scale=1; minimum-scale=1; user-scalable=no;">
width=device-width:
- 这意味着页面的宽度应该等于设备的屏幕宽度。这是响应式设计的基础,因为它确保了页面内容会根据设备的屏幕大小进行适当调整。
initial-scale=1:
- 当页面首次加载时,缩放级别应设为1,既不放大也不缩小。
maximum-scale=1 和 minimum-scale=1:
- 这限制了用户可以缩放页面的最大和最小级别。在这里,它们都被设置为1,意味着用户不能放大或缩小页面。
user-scalable=no:
- 这表示用户不能通过手势(如双指捏合或放大)来缩放页面。这与
maximum-scale=1
和minimum-scale=1
的效果相似,但更直接地禁止了缩放功能。
- 这表示用户不能通过手势(如双指捏合或放大)来缩放页面。这与
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
less/scss 函数:
@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
// postcss.config.js
module.exports = {
plugins: {
'postcss-px-to-viewport-8-plugin': {
unitToConvert: 'px', // 需要转换的单位,默认为"px"
viewportWidth: 375, // 设计稿的视口宽度
exclude: [/node_modules/], // 忽略某些文件夹下的文件或特定文件
unitPrecision: 5, // 单位转换后保留的精度
propList: ['*'], // 能转化为vw的属性列表
viewportUnit: 'vw', // 希望使用的视口单位
fontViewportUnit: 'vw', // 字体使用的视口单位
selectorBlackList: [], // 需要忽略的CSS选择器,不会转为视口单位,使用原有的px等单位。
minPixelValue: 1, // 设置最小的转换数值,如果为1的话,只有大于1的值会被转换
mediaQuery: false, // 媒体查询里的单位是否需要转换单位
replace: true, // 是否直接更换属性值,而不添加备用属性
landscape: false, // 是否添加根据 landscapeWidth 生成的媒体查询条件 @media (orientation: landscape)
landscapeUnit: 'vw', // 横屏时使用的单位
landscapeWidth: 1125 // 横屏时使用的视口宽度
}
}
}
vs Code 插件:
px to vw & rem (cssrem)
插件
扩展
1px 问题
由于屏幕的物理像素与CSS像素(逻辑像素)之间的比例问题,导致设计师预期的1物理像素边框在屏幕上显示得过粗或过细的问题。
这个问题的根源在于,早期没有逻辑像素的概念时,1px 的 CSS 像素直接对应 1 物理像素。但随着响应式设计和 Retina 屏幕的出现,为了适配不同设备,引入了逻辑像素比(Device Pixel Ratio, DPR),使得 1px 的 CSS 像素对应的物理像素由 DPR 决定。
解决方案:
- 使用媒体查询调整:针对不同 DPR 的设备编写不同的样式规则,确保 1px 边框在不同设备上具有相同的视觉效果。这种方法的缺点是需要写多套 CSS 代码,维护成本较高。(不推荐)
- 使用
transform: scale(0.5)
:通过缩放元素大小的方式来保证 1px 边框的精细度。但这种方法可能会影响布局和定位。 - 使用伪类和阴影:利用伪元素和 box-shadow 属性来模拟边框,可以实现较细的边框效果,但会增加代码复杂度和性能消耗。
- 使用 SVG 或图标字体:SVG 和图标字体可以保证在不同设备上的清晰度和一致性,但可能会导致文件大小增加。
- 使用 postcss 插件自动处理:自动化工具可以根据设备的 DPR 自动生成对应宽度的边框,简化了开发流程。
- 使用 flexible 方案:例如使用 lib-flexible 等库,根据屏幕的实际像素密度动态调整 CSS 像素和物理像素之间的映射关系。