42

关于移动端适配的一些问题(笔记)

 4 years ago
source link: https://www.tuicool.com/articles/zie6j2E
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

移动端适配,经常遇到的问题。首先要分清 设备独立像素、CSS像素、DPR、PPI、DIP 概念和区别。

  • 基础概念
  • 字体大小区别
  • 1px 问题
  • UI 图完美适配方案
  • iPhoneX 适配方案
  • 横屏适配
  • 高清屏图片模糊的问题

理解问题产生的原因,可以更好的解决问题。

一、基本概念

1.1 英寸(inch)

一般用英寸来描述屏幕的大小,如手机 5.7、6.5 等使用的单位都是英寸。

屏幕的尺寸指定是对角线的长度。

1.2 分辨率

分辨率一般分为两种:屏幕分辨率和图片分辨率。

屏幕分辨率是指一块屏幕上有多少个像素点。如: iPhone 11 Max Pro 的分辨率是 2688 x 1242 ,表示的是屏幕在水平和垂直方向上像素点数。

图像分辨率是指图片含有的 像素数 ,比如一张分辨率为 1080*768 ,表示图片在垂直和水平方向上所具备的像素点数。

1.3 每英寸像素(PPI)

PPI 表示每英寸中像素的数目,确切的说是像素密度,对于屏幕来说是指每英寸物理像素的数目及显示器设备的点距。数值越高,代表显示器的显示密度越高。

PPI = \frac{\sqrt{水平像素点数^2+垂直像素点数^2}}{尺寸}

iPhone 11 Max pro 为例。

\frac{\sqrt{2688^2+1242^2}}{6.5} ≈ 455

那么屏幕每英寸约有 455 个物理像素点。实际尺寸不到6.5。

1.4 DPI

DPI:每英寸包含的点数,是一个抽象单位,可以是屏幕像素点、图片像素点也可以是打印机墨点。当 DPI 描述屏幕和图片时与 PPI 等价。

1.5 设备独立像素(DIP)

设备独立像素,也称为逻辑像素,简称 DIP。

用来告诉不同分辨率的手机,它们在界面上显示元素的大小是多少。

1.6 设备像素(DP)

设备像素(Device Pixels,px),又称为物理像素,是指显示屏上的一个个物理点。物理像素的点大小是固定的,单位是 pt 。每块屏幕的物理像素多少在出厂时已经确定。 ptCSS 单位中属于真正的 绝对单位1pt = 1/72(inch) 一英寸等于2.54厘米。例如: iPhone 11 Max pro 2688 x 1242 像素分辨率,458 ppi

1.7 CSS像素(PX)

CSS像素(CSS Pixel, px),又称为虚拟像素。就是 Web 中使用的像素,单位是 px 。 在 CSS 规范中,长度分为两类,绝度单位和相对单位。这里 px 是一个相对单位,相对的是设备像素。

CSS像素是图像显示的基本单元,是一个相对量,是一个抽象的概念。所以在说CSS像素大小的时候要考虑上下文的不同。

由于不同的设备的物理像素是不同的,所以 CSS 认为浏览器会对 CSS 中的像素进行调节,使得浏览器中 1 CSS 像素的大小在不同的设备上看起来差不多,目的是为了保证阅读体验。

在 DPR 为 1的屏幕中,浏览器可视窗口 800px 里面有个 div 为 400px ,此时将页面放大 200%,发现 div 宽度占满这个浏览器。也就是说默认一个 CSS 像素等于一个物理像素宽度,放大后一个CSS像素等于两个物理像素宽度。所以,可以看出 CSS 像素是一个相对值。

1.8 设备像素比 (DPR)

在为缩放状态下,设备像素和CSS像素之间的比例关系。苹果的 Retina 屏的DPR为2,也就是说2*2个物理像素表示一个CSS像素。

在 web 中,浏览器通过 window.devicePixelRatio 来获取 DPR。

在 CSS 中,使用媒体查询 min-device-pixel-ratio ,区分 DPR。

@media (-webkit-min-device-pixel-ratio:2),(min-device-pixel-ratio:2){}

换算关系

DPR = \frac{设备像素}{设备独立像素}

二、设备独立像素(DIP)

iOS 、Android 和 ReactNative 中使用的都是设备独立像素。

iOS 单位为 ptAndroid 单位为 dpReactNative 未指明其实也是设备独立像素 dp

为了适配屏幕,需要将物理像素转为设备独立像素。例如:给定一个元素的高度 200px (此时px表示物理像素而不是CSS像素), iPhone 6 的设备像素比为2,那么给定的元素高度应该是 200px/2 = 100dp

在 Web 开发中,使用的是CSS像素,当页面的缩放比为100%时,一个CSS像素等于一个设备独立像素。

当页面放大是,CSS像素也会被放大,一个CSS像素跨越多个设备独立像素。

页面缩放比 = \frac{CSS像素}{设备独立像素}

经常用 P 和 K 描述屏幕。

P代表的是屏幕纵向的像素个数, 1080p 即纵向上有1080个像素点,分辨率为 1920 * 1080 的屏幕就属于 1080P 屏幕。

K代表屏幕横向有几个1024个像素,一般来将横向屏幕超过 2048 就属于 2K 屏,超过 4096 就属于 4K 屏。

三、视口

视口( viewport)代表当前可见的计算机图形区域。在 Web浏览器术语中,通常与浏览器窗口相同,但不包括浏览器的 UI, 菜单栏等——即指你正在浏览的文档的那一部分。

一般我们所说的视口共包括三种:布局视口、视觉视口和理想视口,它们在屏幕适配中起着非常重要的作用。

  • 布局视口:是网页布局的基准窗口。
  • 视觉视口:用户在屏幕中真是看到的窗口。
  • 理想视口:网页在移动展示的理性大小。

Meta Viewport

借助

元素的 viewport来帮助我们设置视口、缩放等,从而让移动端得到更好的展示效果.

<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no,viewport-fit=cover">

看一下 viewport 的配置含义。

名称 可选值 描述 width 正整数、device-width 定义布局视口的宽度,单位px height 正整数、device-height 定义布局视口的高度,单位px inital-scale 0 - 10.0 定义页面初始缩放比率 minimum-scale 0-10.0 定义缩放的最小值,必须小于或等于maximum-scale maximum-scale 0-10.0 定义缩放的最大值 user-scalable yes、no 是否允许缩放 viewport-fit contain、cover 适配iphoneX等屏幕

移动适配,为了使页面获得更好的显示效果,让布局视口、视觉视口尽可能等于理想视口。

device-width 就是等于理想视口的宽度。由于 inital-scale=理性视口宽度/视觉视口宽度 ,所以 initial-scale=1 时,理性视口等于视觉视口。

这是,一个 CSS像素等于一个设备独立像素,且是基于理想视口布局的,所以呈现出的效果在各设备上大致相似。

四、字体大小 px、em、rem、pt

4.1 px

回忆一下 px 像素是相对值,相对于屏幕的分辨率而言的。

1px是多少物理像素呢?需要先知道每英寸像素数DPI, Windows 默认是 96dpi, Apple 默认是 72dpi。

任意的浏览器的默认字体是 16px。

单位换算,默认情况下 16px = 1em = 1rem。

4.2 em

em 是指 相对于父元素的字体大小 的单位。相对字符宽度的倍数,类似于百分比。浏览器默认 1em = 16px。

默认浏览器符合:16px = 1em,12px = 0.75em, 10px = 0.625em。如设置 body 选择器的 font-size=62.5%,使1em = 10px, 1.2em = 12px。这样方便计算。

4.3 rem

rem 是相对于根元素的字体大小的单位,它是CSS3新增的相对单位。与 em 的区别在于前者相对父元素字体大小后者相对根元素自字体大小。

单位换算,根元素字体16px: 16px = 1rem;16px * 0.75 = 12px => 0.75rem = 12px。

4.4 pt

pt(磅): 是一个物理长度单位,指1/72英寸。

pt = 1/72(英寸),px = 1/dpi(英寸) => pt = px * 72/dpi

在 windows 下以96dpi来计算,pt = px 72/96 = px 3/4 及默认 16px = 12pt。

五、1px 边框问题

为了适配屏幕使用设备独立像素来对页面布局。在DPR大于1的屏幕上,1px实际上被多个物理像素渲染,这就出现 1px 在屏幕上看起来很粗的原因。

5.1 border-image

基于 media 查询判断不同的 DPR 给定不同的 border-image

.border_1px {
    border-bottom: 1px solid #000;
}
@media only screen and (-webkit-min-device-pixel-ratio:2) {
    .border_1px {
        border-bottom: none;
        border-width: 0 0 1px 0;
        border-image: url(../img/1pxline.png) 0 0 2 0 stretch;
    }
}

5.2 background-image

border-image 类似,将图片放在背景上。

.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;
    }
}

上面两种缺点是无法添加圆角。

5.3 伪类 + transform

基于 media 获取 DPR 对线条缩放。

.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)
    }
}

可以添加圆角,只需为伪类添加 border-radius 即可。

5.4 svg

借助 PostCSSpostcss-write-svg 直接使用 border-image 和 background-image 创建 svg 的 1px 边框。

@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;
}

推荐使用这种方法。

六、适配iPhoneX屏幕

iPhoneX 出现取消了物理按键,改成了底部的小黑条,但是这样的改动给前端适配增加了难度。

6.1 安全区域

iPhoneX屏幕外观三个改动:圆角、刘海和小黑条。为了适配诞生了安全区域的概念,它是一个不受上面三个改动的影响的可视窗口。

6.2 viewport-fit

前面 viewport 中有个 viewport-fit=cover ,这个属性专门为 iPhoneX 诞生的属性,用于限制网页如何在安全区域内展示。

  • contain: 可视窗口完全包含网页内容
  • cover:网页内容完全覆盖可视窗口

默认情况下或者设置为 auto 和 contain效果相同。

6.3 env 和 constant

我们需要将顶部和底部合理的摆放在安全区域内, iOS11新增了两个 CSS函数 env、constant,用于设定安全区域与边界的距离。

函数内部可以是四个常量:

  • safe-area-inset-left:安全区域距离左边边界距离
  • safe-area-inset-right:安全区域距离右边边界距离
  • safe-area-inset-top:安全区域距离顶部边界距离
  • safe-area-inset-bottom:安全区域距离底部边界距离

注意:我们必须指定 viweport-fit 后才能使用这两个函数:

<meta name="viewport" content="viewport-fit=cover">

constant在 iOS<11.2 的版本中生效, env在 iOS>=11.2 的版本中生效,这意味着我们往往要同时设置他们,将页面限制在安全区域内:

body {
    padding-bottom: constant(safe-area-inset-bottom);
    padding-bottom: env(safe-area-inset-bottom);
}

七、横竖屏适配的问题

7.1 JavaScript 检测横竖屏

window.orientation:获取屏幕旋转方向

window.addEventListener('resize', () => {
    if (window.orientation === 180 || window.orientation === 0) {
        // 正常方向或者旋转180度
        console.log('竖屏')
    }
    if (window.orientation === 90 || window.orientation === -90) {
    // 顺时针或逆时针 90 度
        console.log('横屏')
    }
})

7.2 CSS检测横屏

@media screen and (orientation: portrait) {
    // 竖屏
}
@media screen and (orientation: landscape) {
    // 横屏
}

八、图片模糊问题

8.1 原因

平时使用的图片大多数属于位图(.png, jpg…),位图是由一个个像素点构成的,每个像素点具有特定的位置和色值。

理论上,一个像素点有一个物理像素点渲染效果最佳。但是在 dpr > 1 的屏幕中,一个位图像素点由多个物理像素点渲染,这些物理像素点不能准确的分配颜色,只能去近似值,所以相同的图片在 dpr > 1 的屏幕上模糊了。

8.2 解决方案

为了保证图片质量,可以在不同的 DPR 屏幕中显示不同分辨率的图片。

如: DPR = 2 的屏幕显示二倍图(@2x),在 DPR = 3 的屏幕中显示三倍图(@3x)。

8.3 media 查询

使用 media 查询来判断不同的 DPR 来显示不同的背景图片。

.avatar {
    background-image: url(xxx.png);
}
@media only screen and (-webkit-min-device-pixel-ratio: 2) {
    .avatar {
        background-image: url(xxx_2x.png);
    }
}
@media only screen and (-webkit-min-device-pixel-ratio: 3) {
    .avatar {
        background-image: url(xxx_3x.png);
    }
}

这种方式只适用于背景图

8.4 image-set

.avatar {
    background-image: -webkit-image-set('xxx.png' 1x, 'xxx_2x.png' 2x);
}

这种方式只适用于背景图

8.5 srcset

使用 img 标签的 srcset 属性,浏览器会自动根据像素密度匹配最佳图片显示。

<img src="xxx.png" srcset="xxx_2x.png 2x, xxx_3x.png 3x">

8.5 JS拼接图片 url

使用 window.devicePixelRatio 获取 DPR,遍历所有图片,替换图片地址。

const dpr = window.devicePixelRatio
const images = document.querySelectorAll('img')
images.forEach(item => {
    item.src.replace('.', `@${dpr}x.`)
})

9.6 使用 svg

SVG的全称是可缩放矢量图。不同于位图使用像素,SVG使用向量描述图片。它无论如何缩放都不会失真。除了用代码绘制 svg ,还可以像使用位图一样使用 svg 图片。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK