🔥码云GVP开源项目 12k star Uniapp+ElementUI 功能强大 支持多语言、二开方便! 广告
# 参考链接 [链接1](http://caibaojian.com/mobile-knowledge.html) [链接2](https://www.cnblogs.com/luguiqing/p/7910686.html) [链接3](https://www.cnblogs.com/superlizhao/p/8729190.html) [链接4](https://juejin.im/post/5cddf289f265da038f77696c#heading-46) [TOC] # viewport viewport 模板: 忽略将页面中的数字识别为电话号码: `<meta name="format-detection" content="telephone=no" />` H5 页面窗口自动调整到设备宽度,并禁止用户缩放页面: `<meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no" />` 忽略 Android 平台中对邮箱地址的识别: `<meta name="format-detection" content="email=no" />` ```html <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport"> <meta content="yes" name="apple-mobile-web-app-capable"> <meta content="black" name="apple-mobile-web-app-status-bar-style"> <meta content="telephone=no" name="format-detection"> <meta content="email=no" name="format-detection"> <title>标题</title> <link rel="stylesheet" href="index.css"> </head> <body> 这里开始内容 </body> </html> ``` # 相关概念 来源:[链接4](https://juejin.im/post/5cddf289f265da038f77696c#heading-46) ## 英寸 一般用英寸描述屏幕的物理大小,如电脑显示器的`17`、`22`,手机显示器的`4.8`、`5.7`等使用的单位都是英寸。 需要注意,上面的尺寸都是屏幕对角线的长度: ![](https://box.kancloud.cn/d8b1c4e3b44aaef589cbb9476f6d9f9f_1280x903.png =400x) 英寸(`inch`,缩写为`in`)在荷兰语中的本意是大拇指,一英寸就是指甲底部普通人拇指的宽度。 英寸和厘米的换算:`1英寸 = 2.54 厘米` ## 分辨率 <span style="font-size: 20px;">像素</span> 像素即一个小方块,它具有特定的位置和颜色。 图片、电子屏幕(手机、电脑)就是由无数个具有特定颜色和特定位置的小方块拼接而成。 像素可以作为图片或电子屏幕的最小组成单位。 通常我们所说的分辨率有两种:屏幕分辨率和图像分辨率。 <span style="font-size: 20px;">屏幕分辨率</span> 屏幕分辨率指一个屏幕具体由多少个像素点组成: ![](https://img.kancloud.cn/25/a1/25a1d169356e62a92591f92a63f0658c_1280x889.png ) `iPhone XS Max`和`iPhone SE`的分辨率分别为`2688 x 1242`和`1136 x 640`。这表示手机分别在垂直和水平上所具有的像素点数。 当然分辨率高不代表屏幕就清晰,屏幕的清晰程度还与尺寸有关。 <span style="font-size: 20px;">图像分辨率</span> 我们通常说的`图片分辨率`其实是指图片含有的`像素数`,比如一张图片的分辨率为`800 x 400`。这表示图片分别在垂直和水平上所具有的像素点数为`800`和`400`。 同一尺寸的图片,分辨率越高,图片越清晰。 ![](https://box.kancloud.cn/bb0283d7d91c744d8cc56326dfd2dc59_788x133.png) <span style="font-size: 20px;">PPI</span> `PPI(Pixel Per Inch)`:每英寸包括的像素数。 `PPI`可以用于描述屏幕的清晰度以及一张图片的质量。 使用`PPI`描述图片时,`PPI`越高,图片质量越高,使用`PPI`描述屏幕时,`PPI`越高,屏幕越清晰。 在上面描述手机分辨率的图片中,我们可以看到:`iPhone XS Max` 和 `iPhone SE`的`PPI`分别为`458`和`326`,这足以证明前者的屏幕更清晰。 由于手机尺寸为手机对角线的长度,我们通常使用如下的方法计算`PPI`: ![](https://box.kancloud.cn/b46a79e3e5b553dd6748339a6782889a_307x83.png) <span style="font-size: 20px; ">DPI</span> `DPI(Dot Per Inch)`:即每英寸包括的点数。 这里的点是一个抽象的单位,它可以是屏幕像素点、图片像素点也可以是打印机的墨点。 平时你可能会看到使用`DPI`来描述图片和屏幕,这时的`DPI`应该和`PPI`是等价的,`DPI`最常用的是用于描述打印机,表示打印机每英寸可以打印的点数。 一张图片在屏幕上显示时,它的像素点数是规则排列的,每个像素点都有特定的位置和颜色。 当使用打印机进行打印时,打印机可能不会规则的将这些点打印出来,而是使用一个个打印点来呈现这张图像,这些打印点之间会有一定的空隙,这就是`DPI`所描述的:打印点的密度。 ![](https://box.kancloud.cn/d6e006606b6347be499cd8621fa5f90a_624x296.png) 在上面的图像中我们可以清晰的看到,打印机是如何使用墨点来打印一张图像。 所以,打印机的`DPI`越高,打印图像的精细程度就越高,同时这也会消耗更多的墨点和时间。 ## 设备独立像素 实际上,上面我们描述的像素都是`物理像素`,即设备上真实的物理单元。 下面我们来看看`设备独立像素`究竟是如何产生的: 智能手机发展非常之快,在几年之前,我们还用着分辨率非常低的手机,比如下面左侧的白色手机,它的分辨率是`320x480`,我们可以在上面浏览正常的文字、图片等等。 但是,随着科技的发展,低分辨率的手机已经不能满足我们的需求了。很快,更高分辨率的屏幕诞生了,比如下面的黑色手机,它的分辨率是`640x940`,正好是白色手机的两倍。 理论上来讲,在白色手机上相同大小的图片和文字,在黑色手机上会被缩放一倍,因为它的分辨率提高了一倍。这样,岂不是后面出现更高分辨率的手机,页面元素会变得越来越小吗? ![](https://img.kancloud.cn/eb/9b/eb9ba996c7e20b126dad7262d9d71d07_1268x960.png) 然而,事实并不是这样的,我们现在使用的智能手机,不管分辨率多高,他们所展示的界面比例都是基本类似的。乔布斯在`iPhone4`的发布会上首次提出了`Retina Display`(视网膜屏幕)的概念,它正是解决了上面的问题,这也使它成为一款跨时代的手机。 ![](https://img.kancloud.cn/2c/2a/2c2ad082244b5bc140585376fc66db9c_488x367.png) 在`iPhone4`使用的视网膜屏幕中,把`2x2`个像素当`1`个像素使用,这样让屏幕看起来更精致,但是元素的大小却不会改变。 ![](https://box.kancloud.cn/07092530b6d4c6d2e6c85615e94bfd70_926x333.png) 我们必须用一种单位来同时告诉不同分辨率的手机,它们在界面上显示元素的大小是多少,这个单位就是设备独立像素(`Device Independent Pixels`)简称`DIP`或`DP`。上面我们说,列表的宽度为`300`个像素,实际上我们可以说:列表的宽度为`300`个设备独立像素。 <span style="font-size:20px;">设备像素比</span> 设备像素比`device pixel ratio`简称`dpr`,即物理像素和设备独立像素的比值。 在`web`中,浏览器为我们提供了`window.devicePixelRatio`来帮助我们获取`dpr`。 在`css`中,可以使用媒体查询`min-device-pixel-ratio`,区分`dpr`: ```css @media (-webkit-min-device-pixel-ratio: 2),(min-device-pixel-ratio: 2){ } ``` # 移动端适配方案 ## flexible 方案 `flexible`方案是阿里早期开源的一个移动端适配解决方案,引用`flexible`后,我们在页面上统一使用`rem`来布局。 它的核心代码非常简单: ```js // set 1rem = viewWidth / 10 function setRemUnit () { var rem = docEl.clientWidth / 10 docEl.style.fontSize = rem + 'px' } setRemUnit(); ``` `rem`是相对于`html`节点的`font-size`来做计算的。上面的代码中,将`html`节点的`font-size`设置为页面`clientWidth`(布局视口)的`1/10`,即`1rem`就等于页面布局视口的`1/10`,这就意味着我们后面使用的`rem`都是按照页面比例来计算的。 这时,我们只需要将`UI`出的图转换为`rem`即可。 以`iPhone6`为例:布局视口为`375px`,则`1rem = 37.5px`,这时`UI`给定一个元素的宽为`75px`(设备独立像素),我们只需要将它设置为`75 / 37.5 = 2rem`。 ## vh、vw 方案 `vh、vw`方案即将视觉视口宽度 `window.innerWidth`和视觉视口高度`window.innerHeight` 等分为 100 份。 上面的`flexible`方案就是模仿这种方案,因为早些时候`vw`还没有得到很好的兼容。 | 单位 | 描述 | | --- | --- | | vw| viewpoint width 视窗宽度; 1vw = 视窗宽度的 1% | | vh| viewpoint height 视窗高度;1vh = 视窗高度的 1% | | vmin| vw 和 vh 中较小的那个 | | vmax| vw 和 vh 中较大的那个 | 如果视觉视口为`375px`,那么`1vw = 3.75px`,这时`UI`给定一个元素的宽为`75px`(设备独立像素),我们只需要将它设置为`75 / 3.75 = 20vw`。 这里的比例关系我们也不用自己换算,我们可以使用`PostCSS`的 `postcss-px-to-viewport` 插件帮我们完成这个过程。写代码时,我们只需要根据`UI`给的设计图写`px`单位即可。 当然,没有一种方案是十全十美的,`vw`同样有一定的缺陷: - `px`转换成`vw`不一定能完全整除,因此有一定的像素差。 - 比如当容器使用`vw`,`margin`采用`px`时,很容易造成整体宽度超过`100vw`,从而影响布局效果。当然我们也是可以避免的,例如使用`padding`代替`margin`,结合`calc()`函数使用等等. # 1px 问题 为了适配各种屏幕,我们写代码时一般使用设备独立像素来对页面进行布局。 而在设备像素比大于`1`的屏幕上,我们写的`1px`实际上是被多个物理像素渲染,这就会出现`1px`在有些屏幕上看起来很粗的现象。 ## 伪类 + transform 基于`media`查询判断不同的设备像素比对线条进行缩放: ```css .border_1px:before{ content: ''; position: absolute; top: 0; height: 1px; width: 100%; background-color: #000; transform-origin: 50% 0%; } @media only screen and (-webkit-min-device-pixel-ratio:2){ .border_1px:before{ transform: scaleY(0.5); } } @media only screen and (-webkit-min-device-pixel-ratio:3){ .border_1px:before{ transform: scaleY(0.33); } } ``` ## 使用图片来代替 基于`media`查询判断不同的设备像素比给定不同的`border-image`或`background-image` ```css .border_1px{ border-bottom: 1px solid #000; } @media only screen and (-webkit-min-device-pixel-ratio:2){ .border_1px{ background: url(../img/1pxline.png) repeat-x left bottom; background-size: 100% 1px; } } ``` ## svg 上面我们`border-image`和`background-image`都可以模拟`1px`边框,但是使用的都是位图,还需要外部引入。 借助`PostCSS`的`postcss-write-svg`我们能直接使用`border-image`和`background-image`创建`svg`的`1px`边框: ```css @svg border_1px { height: 2px; @rect { fill: var(--color, black); width: 100%; height: 50%; } } .example { border: 1px solid transparent; border-image: svg(border_1px param(--color #00b1ff)) 2 2 stretch; } ``` 编译后: ```css .example { border: 1px solid transparent; border-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' height='2px'%3E%3Crect fill='%2300b1ff' width='100%25' height='50%25'/%3E%3C/svg%3E") 2 2 stretch; } ``` # 图像模糊问题 ## 产生原因 我们平时使用的图片大多数都属于位图(`png、jpg...`),位图由一个个像素点构成的,每个像素都具有特定的位置和颜色值。理论上,位图的每个像素对应在屏幕上使用一个物理像素来渲染,才能达到最佳的显示效果。 而在`dpr > 1`的屏幕上,位图的一个像素可能由多个物理像素来渲染,然而这些物理像素点并不能被准确的分配上对应位图像素的颜色,只能取近似值,所以相同的图片在`dpr > 1`的屏幕上就会模糊: ![](https://box.kancloud.cn/c6b954987f3174e445a5e9aac358e6c0_498x166.png) ## 解决方案 为了保证图片质量,我们应该尽可能让一个屏幕像素来渲染一个图片像素,所以,针对不同`DPR`的屏幕,我们需要展示不同分辨率的图片。 如:在`dpr=2`的屏幕上展示两倍图`(@2x)`,在`dpr=3`的屏幕上展示三倍图`(@3x)`。 ![](https://box.kancloud.cn/3b25b503fe80b0619377e73a88f124da_700x330.png) <span style="font-size: 20px;">media 查询</span> 使用`media`查询判断不同的设备像素比来显示不同精度的图片: ```css .avatar{ background-image: url(conardLi_1x.png); } @media only screen and (-webkit-min-device-pixel-ratio:2){ .avatar{ background-image: url(conardLi_2x.png); } } @media only screen and (-webkit-min-device-pixel-ratio:3){ .avatar{ background-image: url(conardLi_3x.png); } } ``` <span style="font-size: 20px;">image-set</span> ```css .avatar { background-image: -webkit-image-set( "conardLi_1x.png" 1x, "conardLi_2x.png" 2x ); } ``` <span style="font-size: 20px;">srcset</span> 使用`img`标签的`srcset`属性,浏览器会自动根据像素密度匹配最佳显示图片: ```html <img src="conardLi_1x.png" srcset=" conardLi_2x.png 2x, conardLi_3x.png 3x"> ``` <span style="font-size: 20px;">使用 svg</span> `SVG`的全称是可缩放矢量图(`Scalable Vector Graphics`)。不同于位图的基于像素,`SVG`则是属于对图像的形状描述,所以它本质上是文本文件,体积较小,且不管放大多少倍都不会失真。 除了我们手动在代码中绘制`svg`,我们还可以像使用位图一样使用`svg`图片: ```css <img src="conardLi.svg"> <img src="data:image/svg+xml;base64,[data]"> .avatar { background: url(conardLi.svg); } ``` # 触摸事件 ## 点透问题 场景:当点击绝对定位元素的时候,下面的元素虽然被遮盖,但也被触发了。 原因:touchstart 早于 touchend 早于click。 亦即 click 的触发是有延迟的,这个时间大概在 300ms 左右,也就是说我们 tap 触发之后蒙层隐藏, 此时 click 还没有触发,300ms 之后由于蒙层隐藏,我们的 click 触发到了下面的 a 链接上。 解决方案: (1)尽量都使用 touch 事件来替换 click 事件。例如用 touchend 事件(推荐)。 (2)用 fastclick,https://github.com/ftlabs/fastclick ,原理: 在检测到 touchend 事件的时候,通过 DOM 自定义事件立即触发一个 click 事件,并把浏览器在 300ms 之后真正的 click 事件阻止掉 (3)延迟一定的时间(300ms+)来处理事件 (不推荐) [https://www.cnblogs.com/jianzhixuan/p/6944960.html](https://www.cnblogs.com/jianzhixuan/p/6944960.html)