本文提供几种常用的多屏适配方案,由于不同公司和产品,适配需求不同,这里没法一一概况完全,仅供参考。
流行多屏适配种类
-
PC 和移动端各自为一套独立的页面
PC 固定宽度(设置最小宽度),移动端自适应(flexible)
参考示例:https://www.ifanr.com -
PC 和移动端共用一套
响应式和自适应结合
参考示例:https://www.ifanr.com/api/ifanr-special/vivo-nex -
移动端为主,PC 放置介绍页或者二维码
参考示例:https://www.ifanr.com/api/ifanr-special/apple-2018-autumn
多屏适配方案五花八门,不同公司有不同的适配要求,对于开发者而言,需要在开发前明确产品需求,再选择相应的适配方案。
移动端自适应方案
前面提到了几种多屏适配的方案,这里重点讲讲移动端自适应的方案
1. init-scale 方案
scale = 1 / dpr;
metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
自动拉伸,好处是简单,只需要根据设计稿的尺寸开发即可,不需要考虑单位转换,坏处是完全放弃 px 单位,没法再进行细粒度的调整,在大屏幕上,仅仅是进行放大操作,而不是展示更多内容,丧失大屏幕的优势。
2. rem 方案
关于 rem 这里就不多介绍了,不过要理解 rem 的定义,即 1rem 等于根字体的大小。这也是后面说把设计稿的基准字体大小设为 100 方便计算的原因。
使用 rem 做自适应的原理,即根据设计稿尺寸和实际的屏幕尺寸,等比例地缩放设计稿上的元素的尺寸。
两个公式:
/*
* 等比公式
* clientWidth 为通过 documentElement.clientWidth 获取的当前屏幕宽度
* designWidth 为设计稿宽度(通常为 750)
* designFontSize 为自定义的设计稿的基准字体大小,为方便计算,设为 100
* 通过该公式求出 clientFontSize,即动态设置的根字体大小
*/
clientWidth / designWidth = clientFontSize / designFontSize
/*
* px 转 rem 公式
* pxValue 为设计稿中元素的尺寸
* designFontSize 在前面已定义的 100 (可设为任何数,不过两边要保持一致)
* remValue 即为我们实际设置的 rem 大小
* 该转换一般是在 sass 等 css 编译语言中进行
*/
remValue = pxValue / designFontSize
示例代码如下:
javascript:
const designWidth = 750
const designFontSize = 100
function getFontSize() {
const clientWidth = document.documentElement.clientWidth
const fontSize = clientWidth * designFontSize / baseClientWidth
return fontSize
}
function adjustFontSize() {
const htmlEle = document.getElementsByTagName('html')[0]
let fontSize = this.getFontSize()
htmlEle.style.fontSize = fontSize + 'px'
window.onresize = () => {
fontSize = this.getFontSize()
htmlEle.style.fontSize = fontSize + 'px'
}
}
sass:
$designFontSize = 100
@function r ($size) {
@return $size / $designFontSize + rem;
}
补充:rem + init-scale 解决 1px 问题
在 2dpr 屏上,css border 的 1px 实际上是 2px,如果设计师要求 border 的宽度为普通屏幕上的 1px,则此时需要将 border 设置为 0.5 px,然而大部分浏览器不允许 css 的值为小数点,因此我们可以通过 init-scale 的缩小功能,将 1px border 缩小为 0.5 px,从而解决 1px 问题
scale = 1 / dpr;
metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
fontSize = clientWidth * 100 * dpr / baseClientWidth;
其它解决 1px 问题的方案:before after 伪类 + transform 的 scale 属性
3. vw 方案
rem 其实是 vw 的 hack 实现,因为在 rem 方案诞生时,vw 的兼容性还不够理想。
使用 vw 边不再需要 js 动态设置根字体的大小了,直接使用 sass 等语言,将 px 转为 vw 即可:
@function vw($px) {
@return ($px / 375) * 100vw;
}
补充:提供最大和最小宽度限制需求
使用上面的 rem 或则 vw 做自适应,在移动端的适配效果还是很不错的,但是如果用户在 PC 上打开了链接,而你又没有专门的 PC 页面的话,那么,适配效果就会很糟糕,示例效果如下:
虽然元素仍然是等比放大,但是在 PC 上放大有点吓人了,因此我们有时有必要限制一个最大的宽度,大于这个宽度,页面边不再放大了。如下为限制了最大宽度的 h5,在 PC 上的效果:
给出 rem 和 vw 宽度限制的代码示例:
- rem
const designFontSize = 100
const clientWidth = document.documentElement.clientWidth
if (clientWidth > 540) {
fontSize = (540 * designFontSize) / designDocsWidth
}
if (clientWidth < 320) {
fontSize = (minWidth * designFontSize) / designDocsWidth
}
- vw
html {
font-size: 16px;
}
@media screen and (min-width: 320px) {
html {
font-size: calc(16px + 4 * (100vw - 320px) / 220);
}
}
@media screen and (min-width: 540px) {
html {
font-size: 20px;
}
}
@function r($size){
@return ($size / 16) + rem; // 16px for 375.
}
计算公式:
font-size: calc([minimum size] + ([maximum size] - [minimum size]) * ((100vw - [minimum viewport width]) / ([maximum viewport width] - [minimum viewport width])));
在整理这篇文章之前,我一直对多屏适配心生畏惧,即使已经做了一年多的前端工程师,完成了几个产品和 H5 页面的开发,但一说起多屏适配,还是很头疼,没法很清晰地解答如何做好多屏适配。在我完成这篇文章后,我觉得我的思路清晰了很多,这并不是我已经找到了银弹 —— 我觉得根本没有一套解决所有适配需求的银弹,而是我认识到,多屏适配的方案是在太多了,我并没有必要全部去了解,全部去实验,我觉得我只要掌握了原理,掌握几套常用的解决方案即可,剩下的再根据产品需求,去完善,找出最佳的解决方案。这样一想,适配问题不再那么可怕了。
参考资料
使用Flexible實現手淘H5頁面的終端適配
再聊移動端頁面的適配
Fluid Typography
rem, vw, 還是...? 各憑本事的移動端適配方案