移动端web布局:适配
- viewport与移动设备适配的核心问题是什么?
- meta标签与移动适配的相关内容
- 移动web适配
一、viewport与移动设备适配的核心问题是什么?
在前面一篇博客(移动端web布局:像素与成像的基本原理)中详细的介绍了像素相关的原理,只是对屏幕清晰度做了一些演示,但在移动web中关于像素的问题主要还是体现在布局上,前面说过手机屏幕的参照像素是iPhone3的像素,如果所有手机屏幕都按照这个参照像素的像素比来显示内容,会带来一个什么问题?
当在屏幕是iPhone4物理像素为640*960px与iPhone6物理像素750*1334px中显示同一个网页时会发什么?
同样的像素比,却在不同的屏幕宽度中来显示,必然会出现一个的问题就是如果按照iPhone4给出合适的逻辑像素,在iPhone6中就必然无法被填满;如果按照iPhone6给出合适的逻辑像素,在iPhone4中就必然超出屏幕宽度出现滚动条。
那为了适配这两个终端的屏幕我们是需要做两套样式吗?
显然这不符合我们的开发需求,这样这会产生大量的重复工作,降低工作效率。现实中我们都是一套样式让它自动适配各个终端的屏幕。这是如何做到的呢?
要解决这个适配的问题方法有几种:
这里我们先来了解viewport这个mate标签属性。
在了解viewport之前,建议先阅读一下关于BOM的这篇博客《BOM:浏览器对象模型之浏览器剖析入门》,表面上看这些内容没什么直接关系,但请细品。
1.1手机浏览器是以什么像素展示样式的?现在iPhone6.7.8模型上测试下面这段代码:
1 <!doctype html> 2 <html> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Document</title> 6 <style> 7 *{ 8 margin: 0; 9 } 10 div{ 11 width: 740px; 12 height: 100px; 13 border: 5px solid green; 14 } 15 </style> 16 </head> 17 <body> 18 <div></div> 19 </body> 20 </html>
你会惊奇的发现这样的结果:
参数中写的是div的元素宽度包括边框是750px,但是iPhone6的屏幕宽度是375px,然而这个750px的元素还没填满375px宽的窗口。
如果从像素的缩放比角度来说,iPhone6的像素缩放比(DPR)是2,从之前解析过的物理像素、逻辑像素、像素缩放比的关系来理解,div由750px转换成375不奇怪,那是为什么这个div的实际展示宽度还小于375呢?下面我们继续来看下面这个数据:
2.当我们将鼠标悬停在控制台的html标签上,浏览器显示html标签的宽度是980,这又是为什么?带着这些疑问,我们来看viewport的属性:
width:视口的宽度; height:视口的高度; user-scalable:是否允许用户进行页面缩放; initial-scale:页面初始缩放值; minimum-scale:页面最小能够缩放的比例; maximum-scale:页面最大能够缩放的比例;
2.6viewport的属性与属性值:
1 //viewport属性及参数的标准设置 2 //标准设置(后面适配都是参照这个设置的基础进行) 3 <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> 4 1.width: 取值/正整数、device-width、带px单位的像素值。 不建议设置数字和像数值,安卓部分设备不支持 5 2.height: 与widht同理,但一般不设置 6 3.initial-scale: 按比例缩放逻辑像素,值为一个大于0的数值,可以带小数 7 4.minimum-scale: 逻辑像素最小缩放比例,取值与initial-scale一致 8 5.maximum-scale: 逻辑像素最大缩放比例,取值与initial-scale一致 9 6.user-scalable: 是否允许用户进行页面缩放,值为no或yes,一般设置为no 10 11 注意: 12 1.user-scalable有时候会失效,比如部分设备由放大文字的工具,就有可能失效,但可以通过事件处理这个问题。以及ios10中取no。 13 2.最好按照标准将所有属性及参数写全,比如不写全的情况下,可能出现iphone5中的黑边问题,等其他bug 14 15 适配: 16 后面所有适配中都基本是改变initial-scale的取值来完成,百分比适配除外。
这里我们主要要搞清楚width与innitial-scale这两个属性所表达的含义:
注:设备独立像素===设备的css逻辑像素宽度初始值;设备像素===设备的物理像素;
width:表示浏览器的可视逻辑宽度,也就是浏览器的窗口内容区的横向宽度,这个宽度是CSS逻辑像素。设置宽度指为device-width是,则表示浏览器视口宽度就等于设备初始逻辑像素。
initial-scale:表示的是基于设备的独立像素的伸缩比,如果设置初始值设置为1.0则页面的CSS逻辑像素宽度等于设备独立像素,但这个数值可以取大于0的任何值,当取值大于1.0时则表示基于设备独立像素缩小CSS逻辑像素宽度,当取值小于1.0时则表示基于设备独立像素放大CSS逻辑像素宽度。
initial-scale除了可以设置设备独立像素以外,还可以修改浏览器的视口逻辑宽度,这个种情况是在逻辑像素大于width的像素设定时,为什么需要这样的设定逻辑,请看图解:
如果此时不放大浏览器的视口宽度,那CSS逻辑像素的多出来的350px用到什么地方去呢?
2.7以iPhone6为例计算视口宽度以及CSS逻辑像素值:
2.7.1content="width=device-width, initial-scale=1.0"
视口宽度:375px; CSS逻辑像素宽度375px; 此时CSS逻辑像素375px对应物理像素750px,则一个逻辑像素表示两个物理像素; 当元素宽度大于375px时,会出现横向滚动条;
设备屏幕最大可以显示375px的逻辑像素内容。
2.7.2content="width=device-width, initial-scale=1.5"
视口宽度375px; CSS逻辑像素宽度375/1.5=250px; 此时CSS逻辑像素250px对应物理像素750px,则一个逻辑像素表示三个物理像素; 当元素值宽度大于250px时,会出现横向滚动条;
设备屏幕最大可以显示250px的逻辑像素内容,这时候视口超出逻辑像素也就超出了屏幕,加载页面就会出现横向滚动条。
2.7.3content="width=device-width, initial-scale=0.5"
视口宽度750px; CSS逻辑像素宽度375/0.5=750px; 此时CSS逻辑像素750px对应物理像素750px,则一个逻辑像素表示一个物理像素; 当元素宽度大于750px时,会出现横向滚动条;
设备屏幕最大可以显示750px的逻辑像素内容,只有内容CSS像素超出了750px时才会出现横向滚动条。
注:这里有一个很关键的问题,就是viewport的width属性表示的是浏览器内容区的默认宽度,而CSS逻辑像素宽度表示的是该页面上多少个逻辑像素可以填满设备的屏幕。这里的CSS逻辑像素不是设备的逻辑像素,而是由设备的逻辑像素与initial-scale的缩放比值计算而来。
展示2.7.2的测试代码(iPhone6):

1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.5, maximum-scale=1.5, minimum-scale=1.5"> 6 <title>Document</title> 7 <style> 8 *{ 9 margin: 0; 10 } 11 .box div{ 12 height: 100px; 13 float: left; 14 } 15 .box div:nth-child(1){ 16 width: 250px; 17 background-color: red; 18 } 19 .box div:nth-child(2){ 20 width: 50px; 21 background-color: yellow; 22 } 23 .box div:nth-child(3){ 24 width: 75px; 25 background-color: blue; 26 } 27 .box div:nth-child(4){ 28 background-color: green; 29 } 30 31 </style> 32 </head> 33 <body> 34 <div class="box"> 35 <div></div> 36 <div></div> 37 <div></div> 38 </div> 39 </body> 40 </html>
二、meta标签与移动适配的相关内容
2.1移动web真机测试方案一:
在vs code或者webStorm中安装live server插件,让后使用这个插件启动html页面,然后再将IP换成本机IP,windows换成ipv4的ip,这时候将电脑与手机链接到同一个wifi下,然后在手机端的浏览器输入改好ip的链接就可以在手机端真机测试了。(如果嫌手动输入麻烦,就是可以百度一个二维码转换器,将链接地址换成二维码,微信扫一扫链接生成的二维码,然后在微信调整浏览器打开网页)
2.2移动web真机测试方案二:
由于前面一个方案插件打开的链接我的手机一致无法获取页面,可能是端口的问题,然后我就采用node+express做了一个最简单的静态页面服务,项目安装、文件结构和代码如下:
npm init -y
npm install body-parser express --save
文件结构:
1 //工作区间 2 node_modules 3 page--静态资源根目录 4 --index.html 5 index.js 6 package.json 7 package-lock.json 8 9 //index.js代码 10 11 let express=require("express"); 12 let app = express(); 13 app.use(express.static("page")); 14 app.listen(12306); 15 16 //电脑本地可以使用http://127.0.0.1:12306/index.html 17 //手机可以使用:http://本机ip或ipv4地址/index.html(同样是在同一个wifi下)
2.3通过mate标签在移动web端的一些小应用(关键有点炫的就是配置桌面图标,瞬间将web转成原生应用的感觉,但别飘,它还是个web页面):
1 禁止识别电话与邮箱(但是邮箱没效果) 2 <meta name="format-detection" content="telephone=no,email=no" /> 3 设置添加到主屏后的标题(ios) 4 <meta name="apple-mobile-web-app-title" content="标题"> 5 添加到主屏幕后,全屏显示,删除苹果默认的工具栏和菜单栏(无用) 6 <meta name="apple-mobile-web-app-capable" content="yes" /> 7 放在桌面上的logo 8 <link rel="apple-touch-icon-precomposed" href="iphone_logo.png" /> 9 启动时候的画面(无用) 10 <link rel="apple-touch-startup-image" href="logo_startup.png" /> 11 设置x5内核浏览器只能竖屏浏览(只有UC有效) 12 <meta name="x5-orientation" content="portrait" /> 13 设置x5内核浏览器全屏浏览 14 <meta name="x5-fullscreen" content="true" /> 15 设置UC浏览器只能竖屏浏览 16 <meta name="screen-orientation" content="portrait"> 17 设置UC浏览器全屏浏览 18 <meta name="full-screen" content="yes"> 19 如果想屏蔽所有浏览器的横屏的话,需要在后面陀螺仪相关内容,在陀螺仪相关博客中补充
三、移动web适配
移动适配的内容:字体、宽高、间距、图像(图标、图片)
适配目标:在不同尺寸的移动设备上,页面相对性的达到合理的展示(自适应)或者保持统一效果的等比例缩放(看起来差不多)。
一些移动设备的数据链接:
https://www.paintcodeapp.com/news/ultimate-guide-to-iphone-resolutions https://material.io/tools/devices/ http://screensiz.es/phone
3.1百分比适配:
根据父级算百分比,需要配合其他布局使用。只能设置宽度,高度一般采用固定和内容撑开的方式,而是父子级之间的像素关联性特别大,布局较为固定,不适合更新频率高的项目。
采用百分比布局的项目有m.360.cn移动端和m.lagou.com/拉勾网移动端等。
示例为模拟360主页部分:
主要适配包括viewport窗口宽度为默认设备逻辑像素宽度,页面CSS像素与设备逻辑像素比为1:1。
然后,布局元素采用宽度百分比适配,导航高度固定,内容高度采用自适应。

1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> 6 <title>Document</title> 7 <style> 8 *{ 9 margin: 0; 10 padding: 0; 11 } 12 a{ 13 text-decoration: none; color: #2b2b2b; 14 } 15 header{ 16 padding: 0 20px; 17 height: 48px; 18 line-height: 48px; 19 background-color: #23ac38; 20 } 21 header a:nth-child(1){ 22 float: left; 23 } 24 header a:nth-child(2){ 25 float: right; 26 } 27 img{ 28 vertical-align: middle; 29 } 30 header a:nth-child(1) img{ 31 height: 32px; 32 } 33 header a:nth-child(2) img{ 34 height: 16px; 35 } 36 .lanternSlide img{ 37 width: 100%; 38 } 39 nav ul{ 40 width: 100%; 41 padding: 30px 0; 42 } 43 nav ul li{ 44 width: 25%; 45 float: left; 46 list-style: none; 47 } 48 nav ul li a{ 49 display: block; 50 border-right: 1px solid #e5e5e5; 51 } 52 nav ul li img{ 53 width: 50%; 54 padding:0 25%; 55 } 56 nav ul li p{ 57 padding-top: 5px; 58 width: 100%; 59 height: 28px; 60 line-height: 28px; 61 text-align: center; 62 font-size: 14px; 63 } 64 </style> 65 </head> 66 <body> 67 <header> 68 <a href="http://m.360.com"> 69 <img src="//p2.ssl.qhimg.com/t01893213e448dbbfa2.png" alt=""> 70 </a> 71 <a href="#"> 72 <img src="//p1.ssl.qhmsg.com/t010fa93a99715aad32.png" alt=""> 73 </a> 74 </header> 75 <div class="lanternSlide"> 76 <img src="//p1.ssl.qhmsg.com/t01dd057241efdad2ad.png" alt="some pic"> 77 </div> 78 <nav> 79 <ul> 80 <li> 81 <a href="#"> 82 <img src="//p0.ssl.qhimg.com/t01f222be1309c2fd7f.png" alt=""> 83 <p>全部产品</p> 84 </a> 85 </li> 86 <li> 87 <a href="#"> 88 <img src="//p4.ssl.qhimg.com/t01d7427e1f6239b1b1.png" alt=""> 89 <p>360搜索</p> 90 </a> 91 </li> 92 <li> 93 <a href="#"> 94 <img src="//p2.ssl.qhimg.com/t0199146ed535ecd65e.png" alt=""> 95 <p>360商城</p> 96 </a> 97 </li> 98 <li> 99 <a href="#"> 100 <img src="//p1.ssl.qhimg.com/t0192c6a682125e6cc9.png" alt=""> 101 <p>360游戏</p> 102 </a> 103 </li> 104 </ul> 105 </nav> 106 </body> 107 </html>
3.2viewport适配:
根据设备逻辑像素缩放比、浏览器视口、浏览器CSS像素宽度三者的关系,将所有页面的CSS像素设置为一致的。也就是说在实际开发代码中,一套代码在任何移动设备中表现一致。
关键思路:CSS像素宽度相同,所以需要指定一个锚定的设备逻辑像素为CSS像素宽度,然后通过计算获得目标设备逻辑像素与锚定像素的比值,动态配置给viewport的initial-scale的值即可。
注意:
由于是计算比值,就可以存在不能整除,最终适配可能会与实际存在一些偏差。
不能设置viewport的width值,存在兼容Bug风险,虽然现在的设备基本基本都没有遇到这个问题,但不能绝对排除风险。
虽然这种存在一些问题,但这个适配方式基本可用,一般也不会出问题。就是在设计稿要按照锚定像素来做,但为了适配高清晰大屏设备图片素材需要另外按照高清晰设备尺寸来做,不能从原设计稿上直接切。

1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" id="view"> 6 <title>Document</title> 7 <style> 8 *{ 9 margin: 0; 10 padding: 0; 11 } 12 .box div{ 13 width: 93.75px; 14 height: 100px; 15 float: left; 16 } 17 .box div:nth-child(1){ 18 background-color: red; 19 } 20 .box div:nth-child(2){ 21 background-color: yellow; 22 } 23 .box div:nth-child(3){ 24 background-color: green; 25 } 26 .box div:nth-child(4){ 27 background-color: blue; 28 } 29 </style> 30 <script> 31 (function(){ 32 let curWidth = document.documentElement.clientWidth; 33 curWidth = window.innerWidth; 34 curWidth = window.screen.width; 35 let targetWidth = 375; 36 let scale = curWidth / 375 ; 37 let view = document.getElementById("view"); 38 view.content = "user-scalable=no, initial-scale="+ scale +", maximum-scale="+ scale +", minimum-scale="+ scale ; 39 })(); 40 </script> 41 </head> 42 <body> 43 <!-- 这里使用了一张375px宽度的图片测试 --> 44 <img src="image/375.jpg" alt=""> 45 <div class="box"> 46 <div></div> 47 <div></div> 48 <div></div> 49 <div></div> 50 </div> 51 </body> 52 </html>
3.3DPR:
适配原理:将浏览器CSS像素与设备的物理像素呈1:1的状态。
这个适配算不上是一个完整的适配,因为每个设备的物理像素都不相同,这只是作为一个参考适配,为rem适配做准备。

1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" id="view"> 6 <title>Document</title> 7 <style> 8 *{ 9 margin: 0; 10 padding: 0; 11 } 12 .img{ 13 width: 100%; 14 } 15 .img img{ 16 width: 100%; 17 } 18 .box div{ 19 width: 25%; 20 height: 100px; 21 float: left; 22 } 23 .box div:nth-child(1){ 24 background-color: red; 25 } 26 .box div:nth-child(2){ 27 background-color: yellow; 28 } 29 .box div:nth-child(3){ 30 background-color: green; 31 } 32 .box div:nth-child(4){ 33 background-color: blue; 34 } 35 </style> 36 <script> 37 (function(){ 38 //基于css3属性选择器获取meta标签 39 let meta = document.querySelector('meta[name="viewport"]'); 40 let scale = 1/window.devicePixelRatio; //拿到设备的像素缩放比,并取其反比值 41 if(!meta){ 42 //当viewport的meta标签不存在时,创建标签 43 meta = document.createElement("meta"); 44 meta.name = "viewport"; 45 meta.content = "width="+ html.clientWidth +", user-scalable=no, initial-scale=" + scale + ", maximum-scale=" + scale + ", minimum-scale=" + scale ; 46 }else{ 47 meta.setAttribute("content", "width=device-width, user-scalable=no, initial-scale=" + scale + ", maximum-scale=" + scale + ", minimum-scale=" + scale); 48 } 49 })(); 50 </script> 51 </head> 52 <body> 53 <!-- 这里使用了一张375px宽度的图片测试 --> 54 <div class="img"><img src="image/750.jpg" alt=""></div> 55 <div class="box"> 56 <div></div> 57 <div></div> 58 <div></div> 59 <div></div> 60 </div> 61 </body> 62 </html>
3.4REM适配:
适配原理:将CSS逻辑像素宽度分割成若干分,然后讲一份的宽度设置为html的字体宽度,然后再用rem作为布局的像素宽度。
1 //核心代码演示 2 <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" id="view"> 3 <script> 4 (function(){ 5 let html = window.document.documentElement; 6 let width = html.clientWidth; 7 //假设将css逻辑像素切割成16等份 8 html.style.fontSize = width / 16 + "px"; 9 })(); 10 </script> 11
3.5REM基于设计稿宽度作为基准做适配:
设计为了图片不会失真最好的策略就是将设计稿与设备的物理像素相匹配,那我们在开发时为了百分百还原设计稿,最好的方式就是将CSS像素与设计稿像素对应起来,前面介绍了DPR适配方案,但是DPR的适配方案有一个非常大的麻烦就是每个设备的CSS像素都是对应设备的物理像素,这样就不能使用一套代码完成样式设计。
那么换一个思路,将设计稿的像素与CSS像素关联起来:
1 //核心代码 designWidth为设计稿宽度 2 <script> 3 (function(doc, win, designWidth){ 4 const html = doc.documentElement; 5 const refreshRem = () => { 6 const clientWidth = html.clientWidth; 7 html.style.fontSize = 100 * (clientWidth / designWidth) + 'px'; 8 } 9 doc.addEventListener('DOMContentLoaded', refreshRem); 10 })(document, window, 750); 11 </script>
html字体宽度 = CSS逻辑像素宽度 / 设计稿宽度 * 100px;
元素CSS逻辑像素(rem) = 元素设计宽度 / 设计稿宽度 * CSS逻辑像素宽度 /(CSS逻辑像素 / 设计稿宽度 * 100);
= 元素设计宽度 / 设计稿宽度 * CSS逻辑像素宽度 / CSS逻辑像素宽度 * 设计稿宽度 / 100;
= 元素设计宽度 /100;
所以这时候可以实现CSS像素与设计稿像素呈现1:100的关系。
这里需要提醒一下,示例中我使用了ES6的变量和函数申明语法,在ios10不兼容,所以为了兼容需要改成ES5语法。

1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" id="view"> 6 <title>Document</title> 7 <style> 8 *{ 9 margin: 0; 10 padding: 0; 11 } 12 .img{ 13 width: 100%; 14 } 15 .img img{ 16 width: 100%; 17 } 18 .box div{ 19 width: 1.66rem; 20 height: 1.66rem; 21 float: left; 22 } 23 .box div:nth-child(1){ 24 background-color: red; 25 } 26 .box div:nth-child(2){ 27 background-color: yellow; 28 } 29 .box div:nth-child(3){ 30 background-color: green; 31 } 32 </style> 33 <script> 34 (function(doc, win, designWidth){ 35 const html = doc.documentElement; 36 const refreshRem = () => { 37 const clientWidth = html.clientWidth; 38 html.style.fontSize = 100 * (clientWidth / designWidth) + 'px'; 39 } 40 doc.addEventListener('DOMContentLoaded', refreshRem); 41 })(document, window, 500); 42 </script> 43 </head> 44 <body> 45 <div class="img"><img src="image/750.jpg" alt=""></div> 46 <div class="box"> 47 <div></div> 48 <div></div> 49 <div></div> 50 </div> 51 </body> 52 </html>

1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0" id="view"> 6 <title>Document</title> 7 <style> 8 *{ 9 margin: 0; 10 padding: 0; 11 } 12 .img{ 13 width: 100%; 14 } 15 .img img{ 16 width: 100%; 17 } 18 .box div{ 19 width: 2.16rem; 20 height: 2.16rem; 21 float: left; 22 } 23 .box div:nth-child(1){ 24 background-color: red; 25 } 26 .box div:nth-child(2){ 27 background-color: yellow; 28 } 29 .box div:nth-child(3){ 30 background-color: green; 31 } 32 .box div:nth-child(4){ 33 background-color: yellow; 34 } 35 .box div:nth-child(5){ 36 background-color: green; 37 } 38 </style> 39 <script> 40 (function(doc, win, designWidth){ 41 const html = doc.documentElement; 42 const refreshRem = () => { 43 const clientWidth = html.clientWidth; 44 html.style.fontSize = 100 * (clientWidth / designWidth) + 'px'; 45 } 46 doc.addEventListener('DOMContentLoaded', refreshRem); 47 })(document, window, 1080); 48 </script> 49 </head> 50 <body> 51 <div class="img"><img src="image/750.jpg" alt=""></div> 52 <div class="box"> 53 <div></div> 54 <div></div> 55 <div></div> 56 <div></div> 57 <div></div> 58 </div> 59 </body> 60 </html>
3.6通过媒体查询设置根节点字体宽度:
这种根子节点字体宽度设计并不是一个连续缩放,而是每个区间的字体宽度相同,超出一个区间以后再进行缩放,需要针对每种CSS像素宽度进行设置,并非一种理想的适配方案,但在一定范围内也可以做到适配的效果。

1 html { 2 font-size: 50px 3 } 4 5 body { 6 font-size: 24px 7 } 8 9 @media screen and (min-width: 320px) { 10 html { 11 font-size:21.33px 12 } 13 14 body { 15 font-size: 12px 16 } 17 } 18 19 @media screen and (min-width: 360px) { 20 html { 21 font-size:24px 22 } 23 24 body { 25 font-size: 12px 26 } 27 } 28 29 @media screen and (min-width: 375px) { 30 html { 31 font-size:25px 32 } 33 34 body { 35 font-size: 12px 36 } 37 } 38 39 @media screen and (min-width: 384px) { 40 html { 41 font-size:25.6px 42 } 43 44 body { 45 font-size: 14px 46 } 47 } 48 49 @media screen and (min-width: 400px) { 50 html { 51 font-size:26.67px 52 } 53 54 body { 55 font-size: 14px 56 } 57 } 58 59 @media screen and (min-width: 414px) { 60 html { 61 font-size:27.6px 62 } 63 64 body { 65 font-size: 14px 66 } 67 } 68 69 @media screen and (min-width: 424px) { 70 html { 71 font-size:28.27px 72 } 73 74 body { 75 font-size: 14px 76 } 77 } 78 79 @media screen and (min-width: 480px) { 80 html { 81 font-size:32px 82 } 83 84 body { 85 font-size: 15.36px 86 } 87 } 88 89 @media screen and (min-width: 540px) { 90 html { 91 font-size:36px 92 } 93 94 body { 95 font-size: 17.28px 96 } 97 } 98 99 @media screen and (min-width: 720px) { 100 html { 101 font-size:48px 102 } 103 104 body { 105 font-size: 23.04px 106 } 107 } 108 109 @media screen and (min-width: 750px) { 110 html { 111 font-size:50px 112 } 113 114 body { 115 font-size: 24px 116 } 117 }
3.7号称移动端适配终极解决方案hotcss的应用:
下载地址:https://github.com/imochen/hotcss
使用的基本介绍(如果有scss使用经验及scss编辑器插件配置经验的可以跳过):
//工具与插件安装及使用 1.将下载的压缩包解压 2.从解压的src文件夹中将hotcss.js和px2rem.scss拿出来备用(如果使用的是less就将px2rem.scss换成px2rem.less) 3.给编辑器安装scss自动编译插件:https://www.cnblogs.com/ZheOneAndOnly/p/13763219.html //我的工作区间设计(node服务环境我就不写了,如果有需要参看前面的示例) page css hotCss.scss //项目的scss文件(自己些的样式代码,通过编辑器的自动编译插件,会在css文件夹自动生成一个同名的css文件) px2rem.scss//hotcss工具文件(前面准备的) html hotCss.html //项目的结构文件(自己写的代码) image //图片文件夹 js hotcss.js //hotcss工具文件(前面准备的)
hotcss的示例代码:(这里我就不把全部的测试代码粘贴出来了,多了反而看着懵,能说明hotcss怎么用并能体现出它的原理就可以了)

1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <title>Document</title> 6 <link rel="stylesheet" href="/css/hotCss.css"> 7 <script src="/js/hotcss.js"></script> 8 </head> 9 <body> 10 <div></div> 11 <div></div> 12 <div></div> 13 <div></div> 14 </body> 15 </html>

1 @import "px2rem.scss"; 2 $designWidth: 750; 3 4 body{ 5 margin: 0; 6 } 7 8 body div{ 9 float: left; 10 width: px2rem(187.5); 11 height: px2rem(100); 12 } 13 14 body div:nth-child(1){ 15 background-color: red; 16 } 17 18 body div:nth-child(2){ 19 background-color: yellow; 20 } 21 22 body div:nth-child(3){ 23 background-color: green; 24 } 25 26 body div:nth-child(4){ 27 background-color: blueviolet; 28 }
hotcss的原理剖析(适配的核心代码在hotcss.js第83~97行mresize方法):
这个适配的核心,就是将视口的css像素设置为设备的物理像素,并将设备的宽度分为20份,即320/20=16,每份16的基础像素。
然后,通过设备的实际视口宽度,放大或缩小这个宽度,即innerWidth*20/320,最后配置给根元素的字体像素,然后再元素上使用rem来表达元素宽度。
rem由px2rem按照元素的设计实际宽度,对应的设计总宽度1/20的比例。(这个难度在于各种数据的关系,实际上应用的是小学的数学知识就可以搞定)
//然后在实际开发时,hotcss.js还需要修改一个参数,避免出现一些异常
//第14行
maxWidth = 540,
//默认视口最大宽度为540,这是适配iphone5的参数,
//这个参数根据设计图对应的设备像素,也就是设计图的像素宽度
//然后在在你的scss中最上部添加这段代码
@import "px2rem.scss"; //引入px2rem.scss
$designWidth: 540; //配置设计宽度,即也是对应设备的物理像素
3.8最新的标准适配方案:vw适配
vw是相对于视口宽度的1/100的宽度单位,对应的vh也就是相对于视口高度的1/100的高度单位。并且会随着屏幕的竖屏与横屏自动切换缩放页面。
也就是vw是将屏幕宽度切成100,通过前面hotcss直接就能想到通过scss的函数和变量语法,可以直接将设计像素转换成vw像素,请看示例:

1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" 6 content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> 7 <meta http-equiv="X-UA-Compatible" content="ie=edge"> 8 <title>Document</title> 9 <link rel="stylesheet" href="/css/index.css"> 10 </head> 11 <body> 12 <div>a</div> 13 <div>b</div> 14 <div>c</div> 15 <div>d</div> 16 </body> 17 </html>

1 $designWidth:750;/*配置设计像素 */ 2 @function px100vw($px){ /* 将设计像素转换成每单位vw的rem */ 3 @return $px*100/$designWidth +vw; 4 } 5 6 *{ 7 margin: 0; 8 padding: 0; 9 } 10 div{ 11 float: left; 12 width: px100vw(187.5); 13 height: px100vw(187.5); 14 } 15 div:nth-child(1){ 16 background-color: red; 17 } 18 div:nth-child(2){ 19 background-color: yellow; 20 } 21 div:nth-child(3){ 22 background-color: green; 23 } 24 div:nth-child(4){ 25 background-color: blue; 26 }
再来看看对应的rem适配代码:

1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" 6 content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> 7 <meta http-equiv="X-UA-Compatible" content="ie=edge"> 8 <title>Document</title> 9 <link rel="stylesheet" href="/css/index.css"> 10 <script> 11 (function(win, doc){ 12 let tid = null; 13 let mreSize = function() { 14 let html = doc.documentElement; 15 html.style.fontSize = (html.clientWidth / 100) + 'px'; 16 console.log(html.clientWidth); 17 } 18 mreSize(); 19 //当视口发生变化时调整根节点字体大小,这里需要做一下防抖处理 20 win.addEventListener('resize', function () { 21 clearTimeout(tid); 22 tid = setTimeout(mreSize,33); 23 },false); 24 //当页面加载完成以后调用一次,防止一些bug 25 win.addEventListener('load',mreSize,false); 26 //防止怪异现象再调用一次 27 tid = setTimeout(mreSize,333); 28 })(window,document); 29 30 </script> 31 </head> 32 <body> 33 <div>a</div> 34 <div>b</div> 35 <div>c</div> 36 <div>d</div> 37 </body> 38 </html>

1 $designWidth:750;/*配置设计像素 */ 2 @function px100rem($px){ /* 将设计像素转换成每单位vw的rem */ 3 @return $px*100/$designWidth +rem; 4 } 5 6 *{ 7 margin: 0; 8 padding: 0; 9 } 10 div{ 11 float: left; 12 width: px100rem(187.5); 13 height: px100rem(187.5); 14 } 15 div:nth-child(1){ 16 background-color: red; 17 } 18 div:nth-child(2){ 19 background-color: yellow; 20 } 21 div:nth-child(3){ 22 background-color: green; 23 } 24 div:nth-child(4){ 25 background-color: blue; 26 }
上面两个示例的思路都是保持css元素像素与设计像素一致,然后借助css的扩展语言scss或者less的变量和函数来转换成vw或者rem像素,这让编程时更方便,不再需要计算css的元素像素,但不可避免的需要借助一些工具和插件实现,甚至rem还需要借助js来实现,那可不可以不借助工具和插件同时也尽量保持设计像素与css的元素像素一致呢?
当然还是可行的,这时候就需要vw和rem配合来解决,这种是一种可以不借助css的扩展语言和js就可以实现的方案:
首先,回顾一下viewport的适配原理,不记得可回到前面再看一下,然后再来看下面的推导公式。
1 设计像素 * ? = css像素 2 ?= css像素/设计像素 3 即: 4 设计像素放大多少被等于css像素,以iphone6为例,设计稿像素宽度=750,css像素=375 5 375 / 750 = 0.5 6 计算得到设计像素放大0.5倍 = css像素 7 每一个设计像素的0.5被 = 每一个css像素 8 即像素比 9 10 11 1vw = css像素 / 100 12 1vw = 设计像素 * 像素比 / 100 13 设计像素 = 1vw /(像素比 / 100) 14 15 假设使用vw表示根节点字体像素 16 即:一个设计像素实际占比多少个根节点字体像素? 17 像素比/1vw实际表示的css像素 18 即:iphone6为例 19 fontsezi = 0.5 / 3.75 vw 20 21 iphone6 p为例 22 fontsize = 414/1080 / 4.14 vw 23 24 由于这个数值会非常小,并且在使用rem时区分rem值与px值,可以放大像素比100倍,然后rem值缩小一百倍,即可的一下公式: 25 fontsize = css像素宽度 / 设计像素宽度 * 100 / css像素 * 100 vw 26 元素实际像素 = 设计像素 / 100 rem
如果对推导过程不是很明白也不要紧,直接拿最后两行的公式去用就可以了,下面这里是一个简单的示例:

1 <!doctype html> 2 <html lang="en"> 3 <head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" 6 content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"> 7 <meta http-equiv="X-UA-Compatible" content="ie=edge"> 8 <title>Document</title> 9 <link rel="stylesheet" href="/css/index.css"> 10 </head> 11 <body> 12 <div></div> 13 <div></div> 14 <div></div> 15 <div></div> 16 </body> 17 </html>

1 *{ 2 margin: 0; 3 padding: 0; 4 } 5 html{ 6 font-size: 13.33333333333333333vw; /* css像素宽度 / 设计像素宽度 * 100 / css像素 * 100 vw */ 7 } 8 9 div{ 10 float: left; 11 width: 1.875rem; 12 height: 1.875rem; 13 } 14 15 div:nth-child(1){ 16 background-color: red; 17 } 18 div:nth-child(2){ 19 background-color: yellow; 20 } 21 div:nth-child(3){ 22 background-color: green; 23 } 24 div:nth-child(4){ 25 background-color: blue; 26 }
使用vw+rem适配就完全避开了js的调整,规避了JS操作DOM的性能损耗,使用vw系统自带的屏幕自适应功能。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律