为了设计稿完美适配各种设备,我们就需要将设计稿展示到各个设备的理想视口中,而不是默认的布局视口(980px)中。那么如何适配,分两步:
- 设置布局视口的宽度等于设备理想视口的宽度
- 将设计稿按照比例展示到理想视口中
一、meta
meta 元素就是为了解决第一步
/*
width是设备的宽度,这里的宽度不是设备的物理像素宽度,而是设备独立像素的最佳宽度;
设置布局视口宽度为设备理想视口宽度
*/
<meta name="viewport" content="width=device-width, initial-scale=1.0">
meta其他属性:
-
width: 该属性被用来控制视窗的宽度,可以将 width 设置为980px这样确切的像素数,也可以设为device-width这样的关键字,表示设备的宽度,一般为了自适应布局,普遍的做法是将width设置为 device-width。
-
height: 该属性被用来控制视窗的高度,可以将height设置为640px这样确切的像素数,也可以设为 device-height 这样的关键字,表示设备的实际高度,一般不会设置视窗的高度,这样内容超出的话采用滚动方式浏览。
-
initial-scale: 该属性用于指定页面的初始缩放比例,可以配置 0.0~10 的数字,initial-scale=1表示不进行缩放,布局视口刚好等于理想视口;当大于1时表示将视窗进行放大,小于1时表示缩小。这里只表示初始视口的缩放值,用户也可以自己进行缩放,例如双指拖动手势缩放或者双击手势放大。
- 安卓设备上的initial-scale默认值: 无默认值,一定要设置,这个属性才会起作用
- 在iphone和ipad上,无论你给viewport设的宽度是多少,如果没有指定默认的缩放值,则iphone和ipad会自动计算这个缩放值,以达到当前页面不会出现横向滚动条(或者说viewport的宽度就是屏幕的宽度)的目的
-
minimum-scale: 该属性表示用户能够手动放大的最大比例,可以配置 0.0~10 的数字
-
maximum-scale: 该属性类似maximum-scale,用来指定页面缩小的最小比例。通常情况下,不会定义该属性的值,页面太小将难以浏览
-
user-scalable: 该属性表示是否允许用户手动进行缩放,可配置 no或者yes 。当配置成no时,用户将不能通过手势操作的方式对页面进行缩放
这里需要注意的是 viewport 只对移动端浏览器有效,对PC端浏览器是无效的
二、解决方案
接下来我们解决问题的第二步:将设计稿按照比例展示到设备理想视口中;
下面以 设计稿宽度为750px,图片A 宽度为600px 为例
将宽度为750px的设计稿放到理想视口宽度为375px的设备中,只需要将设计稿中所有的元素宽度像素除以2即可;比如:设计稿中 图片A 的宽度为600px,那我们只需要将图片宽度设置为300px,那么就能完美适配 iPhone6;
问:设备各种各样,宽度也都不相同,那么我们如何适配所有的设备呢?
答:给设计稿的宽度乘以某个系数,这个系数如何计算呢
通过上面描述,我们可以得到如下的等式:
可变系数\alpha=\frac{设备理想视口宽度}{设计稿宽度}=\frac{设备元素宽度}{设计稿元素宽度}
举两个例子:
- iPhone6 的最佳设备宽度为375px,那么 可变系数\alpha=\frac{375px}{750px}=0.5
- iPhone6 Plus 的最佳设备宽度为414px,那么 可变系数\alpha=\frac{414px}{750px}=0.552
那么我们在设置 图片A 的时候只需要如下伪代码:
if 设备为iPhone6 时,<img src="图片A.png" width="600px * 0.5"></img>
if 设备未iPhone6 Plus 时,<img src="图片A.png" width="600px * 0.552"></img>
2.1、@media方案(不建议使用)
media
media介绍参考:media简介
media方案实现
基于 css 的媒体查询属性 @media 分别为不同屏幕尺寸的移动设备编写不同尺寸的 css 属性,示例如下所示:
虽然此方法能在一定程度上解决移动设备适配的问题,但我们也可以看出其存在以下问题,所以其已几乎被历史潮流淘汰。
- 页面上所有的元素都得在不同的 @media 中定义一遍不同的尺寸,这个代价有点高
- 如果再多一种屏幕尺寸,就得多写一个 @media 查询块
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!-- 设置屏幕宽度 -->
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
@media only screen and (min-width: 320px) {
.logo {
/* width = 320px / 750px * 600px */
width: 256px;
}
}
@media only screen and (min-width: 375px) {
.logo {
/* width = 375px / 750px * 600px */
width: 300px;
}
}
@media only screen and (min-width: 414px) {
.logo {
/* width = 414px / 750px * 600px */
width: 331px;
}
}
</style>
</head>
<body>
<!-- 图片A宽度为600px -->
<img class="logo" src="图片A.jpg">
</body>
</html>
2.2、rem方案
rem
rem(font size of the root element)是CSS3新增的一个相对单位,是指相对于根元素的字体大小的单位。如果我们设置 html 的 font-size 为 16px,则如果需要设置元素字体大小为 16px,则写为 1rem。
2.2.1、rem方案一实现
还是以上面的案例为例,设计稿页面宽度为750px,图片A 的宽度为600px,我们要将 图片A 放到iPhone6中(理想视口宽度375px)
方案:
- 设置 html 的 font-size 为:fontSize = \frac{设备理想视口宽度}{设计稿宽度}*100px :通过在各个设备设置不同的fontSize来达到元素宽度/高度的变化
解释一下上面的公式:
问:我们要想把 设计稿宽度(750px)中 图片A(600px) 显示到 iPhone6设备理想视口宽度(375px)中,那么设计稿中1px应该对应设备理想视口中多少像素?
答:这个不难计算, 设备理想视口宽度/设计稿宽度 = 375px/750px = 0.5,可得出设计稿中1px等于理想视口0.5px;
根据计算,我们把 font-size 设置为 0.5px,那么 图片A 在设计稿中的宽度是多少,就写多少rem,即在理想视口中的的宽度 就可以设置为 600rem = 600px * 0.5 = 300px; 600px则是图片A在设计稿中的宽度,300px为理想视口中的宽度;
但是,html 中规定,font-szie最小为12px,那么我们就要将font-size扩大,扩大多少倍,就要给 图片A 的宽度缩小多少倍,为了方便计算,我们将 fontSize扩大100倍,这时 图片A在理想视口中的宽度就可以设置为 6rem = 6px * 0.5 * 100 = 300px
此时,我们将设计稿中所有的元素宽度和高度缩小100倍即可
- 使用 rem 单位设置图片的宽度、高度、字体大小等
代码如下:
采用js代码来设置 html 的 fontSize
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>rem方案-移动端适配</title>
<script>
function setFontSize() {
// 1、设置设计稿的像素: 注意不同的设计稿要进行调整
const designSize = 750
// 2、获取设备大小
var deviceSize = document.documentElement.clientWidth
// 3、假设设计稿的宽度是750px
var fontSize = (deviceSize / designSize) * 100
// 4、设置 html 的 font-size
document.documentElement.style.fontSize = fontSize + "px"
}
// 首次打开页面执行函数
setFontSize()
// 窗口变动执行函数
window.onresize = function() {
setFontSize()
}
</script>
<style>
* {
margin: 0;
padding: 0
}
.logo {
width: 6rem;
height: 3rem;
}
</style>
</head>
<body>
<img src="resources/图片A.png" class="logo">
</body>
</html>
因为html的fontSize随着设备变化而变化,因此 图片A 的宽度也会随着变化。
计算下各个设备的适配情况,如下:
| 设备 | 设备最佳独立像素 | 设计稿宽度 | Html-fontSize大小(100为扩大系数) | 图片A 宽度 |
|---|---|---|---|---|
| iPhone6 | 375 * 667 | 750px | 375px / 750px * 100 = 50px | 600px / 100 * 50px = 300px = 6rem |
| iPhone6 Plus | 414 * 736 | 750px | 414px / 750px * 100 = 55.2px | 600px * 100 * 55.2px = 331.2px = 6rem |
可以看到,在不同的设备下,图片A的宽度一直是6rem,html的fontSize随着设备的不同值不同,这样图片A就可以动态适配
效果展示:视频展示不了
2.2.2、rem方案二实现(推荐)
优化:
方案一的js写的不是特别完美,没有考虑到横屏之类的,可以采用下面js,代码实现如下:
phone.js
(function (doc, win) {
// 设计稿宽度
const designSize = 750
// 获取当前页面元素
var docEle = doc.documentElement,
// 判断横屏或者屏幕大小事件
resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
recalc = function () {
// js获取当前设备的宽度 :document.documentElement.clientWidth;
var clientWidth = docEle.clientWidth;
if (!clientWidth) return;
//宽度大于750px 平板或者桌面
if (clientWidth >= designSize) {
// 设置fontSize为100px,即最大为100px
docEle.style.fontSize = '100px';
} else {//移动端的适配
// 设置 html 的 font-size
var fontSize = (clientWidth / designSize) * 100
docEle.style.fontSize = fontSize + 'px';
}
};
if (!doc.addEventListener) return;
// 增加窗口大小变化监听器
win.addEventListener(resizeEvt, recalc, false);
// 增加内容刷新监听器
doc.addEventListener('DOMContentLoaded', recalc, false);
})(document, window);
引入phone.js,进行页面变化的监控:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>rem优化版方案-移动端适配</title>
<style>
* {
margin: 0;
padding: 0
}
.logo {
width: 6rem;
height: 3rem;
}
</style>
<script src="js/phone.js"></script>
</head>
<body>
<img src="resources/图片A.png" class="logo">
</body>
</html>
2.2.3、flexible方案:与方案二的原理一样
2.2.4、postcss-pxtorem(后续学习)
目前在前端工程化开发中,我们可以借助webpack的工具来完成自动的转化
2.2.5、VS Code插件
px to rem的插件,在编写时自动转化,不过在插件中要设置设计稿的大小
2.3、vw方案
vw
vw、vh 方案即将视觉视口宽度 window.innerWidth 和视觉视口高度 window.innerHeight 等分为 100 份。
上面的 flexible 方案就是模仿这种方案,因为早些时候 vw 还没有得到很好的兼容。
- vw(viewport's width):1vw等于视觉视口宽度的1%
- vh(viewport's height) : 1vh 等于视觉视口高度的1%
- vmin : vw 和 vh 中的较小值
- vmax : 选取 vw 和 vh 中的较大值
如果按视觉视口为 375px,那么 1vw = 3.75px,这时 UI 给定一个元素的宽为 75px (设备独立像素),我们只需要将它设置为 75 / 3.75 = 20vw;每个设备的视觉视口都是不同的,因此也需要动态计算 1vw等于多少px
2.3.1、vw方案一实现
通过meta元素将布局视口的宽度设置为理想视口的宽度(即最佳设备独立像素),可得出 100vw = 设备的宽度
以 iPhone6 为例,即 100vw = 375px,得 1vw = 3.75px,10vw = 37.5px
在设计稿宽度为750px的布局中,图片A 像素为 600*300;
当设置html的fontSize为10vw时,在 iPhone6 中,图片A的宽度为:300px / 37.5px = 8rem
如下代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
padding: 0px;
margin: 0px;
}
html{
font-size: 10vw;
}
.logo {
width: 8rem;
height: 4rem;
}
</style>
</head>
<body>
<img src="resources/图片A.png" class="logo">
</body>
</html>
以上就可以了,不用再添加 js 代码计算html的fontSize,因为html的fontSize为10vw,会随着设备的width不断的变化;
计算下各个设备的适配情况,如下:
| 设备 | 设备最佳独立像素 | 设计稿宽度 | Html-fontSize大小:10vw | 图片A 宽度 |
|---|---|---|---|---|
| iPhone6 | 375 * 667 | 750px | 10vw = 375px / 100 * 10 = 37.5px | 600px * (375px / 750px) = 300px = (300px / 37.5px) rem = 8rem |
| iPhone6 Plus | 414 * 736 | 750px | 10vw = 414px / 100 * 10 = 41.4px | 600px * (414px / 750px) = 331.2px = (331.2px / 41.4px) rem = 8rem |
可以看到,在不同的设备下,图片A的宽度一直是8rem,html的fontSize是10vw,随着设备的不同,10vw的值不同,这样 图片A 就可以动态适配
当然我们可以通过上面的计算,发现 vw 的一个问题:
-
计算元素的像素非常的不方便,主要是以下三步
- 先根据 设计稿宽度与设备宽度 计算出缩放比
- 设计稿元素像素 乘以 缩放比计算出 元素在理想视口像素
- 元素理想视口像素 换算成 rem 单位的值
2.3.2、VS Code插件
px to rem的插件,在编写时自动转化,不过在插件中要设置设计稿的大小,可自行搜索;主要是为了解决方案一计算困难的问题,让插件帮助我们自动计算
2.3.3、vw方案三实现(推荐)
该方案也是为了解决方案一计算困难的问题,个人理解比较方便;思路与 rem方案二 是一样的,只不过是html的FontSize换了个单位表达而已;
算法中心思想:计算设计稿中1px在设备视觉窗口中的vw
以设计稿宽度为750px, iPhone6 为例,由于100vw是设备的宽度,那么以下转换公式是等价的:
\frac{100vw}{750px} <=> \frac{375px}{750px}
可以得出:
设计稿中1px=100vw/750=0.1333333vw(设备视觉视口中),那么,元素在设计稿中的像素是多少,就可以写多少rem;但是html的fontSize最小值为12px,因此,我们将html的fontSize设置扩大100倍为 13.33333 = 0.133333vw * 100,这样设计稿中的像素缩小100倍就可以得到在视觉窗口中的像素。 例如:图片A 在视觉稿中的宽度为600px,那么只需要将图片A的宽度设置为6rem即可;
代码如下:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
* {
padding: 0px;
margin: 0px;
}
html{
font-size: 13.33333vw;
}
.logo {
width: 6rem;
height: 3rem;
}
</style>
</head>
<body>
<img src="resources/图片A.png" class="logo">
</body>
</html>
以上就可以了,不用再添加 js 代码计算html的fontSize,因为html的fontSize为13.33333vw,会随着设备的width不断的变化;
计算下各个设备的适配情况,如下:
| 设备 | 设备最佳独立像素 | 设计稿宽度 | Html-fontSize大小:10vw | 图片A 宽度 |
|---|---|---|---|---|
| iPhone6 | 375 * 667 | 750px | 13.33333vw = 375px / 100 * 13.33333 = 49.99998px | 600px = (600/100)rem = 6rem = 6 * 49.99998 = 299.99988px |
| iPhone6 Plus | 414 * 736 | 750px | 13.33333vw = 414px / 100 * 13.33333 = 55.199998px | 600px = (600/100)rem = 6rem = 6 * 55.199998 = 331.199988px |
可以看到
- 在不同的设备下,图片A的宽度一直是6rem,html的fontSize是13.33333vw,随着设备的不同,13.33333vw的值不同,这样 图片A 就可以动态适配
- 与 vw方案一 中 图片A 最终在设备上的宽度几乎是一样的
相比vw方案一优势在于,计算元素的rem值非常方便,只需要将对应的设计稿中的元素除以100即可;
总结
移动端适配方案就学习到这里,后续有更好的方案再进行更新;
推荐使用:2.3.3、vw方案三实现(推荐)或者 2.2.2、rem方案二实现(推荐)
另外需要特别注意:重要的事情说三遍!!!
当设计稿不是750px的时候,需要将上面的计算公式中的750px换成对应的设计稿宽度的像素
当设计稿不是750px的时候,需要将上面的计算公式中的750px换成对应的设计稿宽度的像素
当设计稿不是750px的时候,需要将上面的计算公式中的750px换成对应的设计稿宽度的像素
参考文档:
2022 年移动端适配方案指南 — 全网最新最全 - 掘金 (juejin.cn)
关于移动端适配,你必须要知道的 - 掘金 (juejin.cn)