移动端样式开发小结

1,027 阅读9分钟

疑问

  1. 拿到设计搞,如何开发适配不同屏幕尺寸?

  2. 用模拟器访问html页面是,为什么宽度是980px?

  3. 图片为什么要引入3倍图?

  4. 媒体查询适配规则?

  5. flexible、rem、vh使用场景,以及优缺点?

  6. flex、grid布局兼容性问题?

正文

  1. 什么是绝对长度单位?什么是相对长度单位?
  • 绝对单位:in(inch英寸)、cm(厘米)、mm(毫米)、pt(磅)、pc(pica)
  • 相对单位:px、em、rem等
  1. 什么是屏幕尺寸、屏幕分辨率、屏幕像素密度?
  • 屏幕尺寸:5寸
  • 屏幕分辨率: 1080px X 1920px
  • 屏幕像素密度: 勾股定理算出对角线的分辨率:√(1920²+1080²)≈2203px 对角线分辨率除以屏幕尺寸:2203/5≈440dpi。
  1. 什么是ppi、dpi、dp、dip、px?
  • ppi:pixels per inch,屏幕上每英寸可以显示的像素点的数量,即屏幕像素密度。
  • dpi:dots per inch,最初用于衡量打印物上每英寸的点数密度,就是打印机可以在一英寸内打多少个点。当dpi的概念用在计算机屏幕上时,就称之为ppi。ppi和dpi是同一个概念,Android比较喜欢使用dpi,IOS比较喜欢使用ppi。
  • dp、dip:dp和dip都是Density Independent Pixels的缩写,密度独立像素,可以想象成是一个物理尺寸,使同样的设置在不同手机上显示的效果看起来是一样的。

在Android中,规定以160dpi为基准,1dp=1px。如果密度是320dpi,则1dp=2px,以此类推。 Android和IOS都会通过转换系数让控件适应屏幕的尺寸。一个按钮给了44x44dp的大小,在160dpi密度的时候,按钮就是44x44px大小;在320dpi密度的时候,按钮就是88x88px的大小。不需要我们去书写多套尺寸。

打开chrome的开发者工具,我们可以模拟各个手机型号的显示情况,每种型号上面会显示一个尺寸,比如iPhone X显示的尺寸是375x812,实际iPhone X的分辨率会比这高很多,这里显示的就是设备独立像素。

  • px:就是通常所说的像素,使网页设计中使用最多的长度单位(css像素)。将显示器分成非常细小的方格,每个方格就是一个像素。(网页重构中使用的px和屏幕分辨率的px不一定是一样的大小。)

从 iPhone4 开始,苹果推出了 Retina 屏,分辨率提高了一倍(640*960),而屏幕尺寸却没变。当页面缩放比例为100%时,1个css像素=1个dp 1个css像素=2个设备像素

回答:为什么要引入3倍图(解决图片模糊问题)? 理论上,位图的每个像素对应在屏幕上使用一个物理像素来渲染,才能达到最佳的显示效果。

而在dpr > 1的屏幕上,位图的一个像素可能由多个物理像素来渲染,然而这些物理像素点并不能被准确的分配上对应位图像素的颜色,只能取近似值,所以相同的图片在dpr > 1的屏幕上就会模糊:

解决方案: 为了保证图片质量,我们应该尽可能让一个屏幕像素来渲染一个图片像素,所以,针对不同DPR的屏幕,我们需要展示不同分辨率的图片。

如:在dpr=2的屏幕上展示两倍图(@2x),在dpr=3的屏幕上展示三倍图(@3x)。

  1. 什么是mdpi、hdpi、xdpi、xxdpi? Google官方指定按照下列标准区分不同设备的dpi:

苹果的区分则更为简单:非高清屏、高清屏、超高清屏。

从数值上看,苹果和安卓有这样的对应关系:

  1. 1x、2x、3x……这个系数怎么来的(设备像素比)?

设备像素比device pixel ratio简称dpr,即物理像素和设备独立像素的比值。

在web中,浏览器为我们提供了window.devicePixelRatio来帮助我们获取dpr。

在css中,可以使用媒体查询min-device-pixel-ratio,区分dpr:

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

在React Native中,我们也可以使用PixelRatio.get()来获取DPR。

当然,上面的规则也有例外,iPhone 6、7、8 Plus的实际物理像素是1080 x 1920,在开发者工具中我们可以看到:它的设备独立像素是414 x 736,设备像素比为3,设备独立像素和设备像素比的乘积并不等于1080 x 1920,而是等于1242 x 2208。

实际上,手机会自动把1242 x 2208个像素点塞进1080 * 1920个物理像素点来渲染,我们不用关心这个过程,而1242 x 2208被称为屏幕的设计像素。我们开发过程中也是以这个设计像素为准。

  1. viewpoint 手机浏览器是把页面放在一个虚拟的“窗口”(viewport)中,窗口可大于或小于手机的可视区域,一般手机默认viewport大于可视区域。这样不会破坏没有针对手机浏览器优化的网页的布局,用户可以通过平移和缩放来看网页的其他部分。 部分机型默认viewport大小:
<meta name="viewport" content="width=device-width, initial-scale=1.0" />

每个设置对应的是:

注意: 网页中使用的单位px,就是通常所说的像素,是网页设计中使用最多的长度单位。将显示器分成非常细小的方格,每个方格就是一个像素(这和我们理解的屏幕分辨率的1920px*1080px的px是不同的)。不同设置下,方格的大小不一样。

例如iPhone4S如果不设置viewport,他就会默认是980px,就像把屏幕分成980份(不是屏幕分辨率的640px哦!)。如果设置一个元素为100px*100px,看起来就是屏幕的100/980,可能如下图所示:

例如iPhone4S如果设置viewport width=device-width,他就会是320px,就像把屏幕分成320份(不是屏幕分辨率的640px哦!)。如果设置一个元素为100px*100px,看起来就是屏幕的100/320,可能如下图所示:

未设置viewport,弹出来的都是设备的默认宽度,基本是980px,除了最后一台三星老爷机是800px。

设置了viewport,width=device-width,弹出来的是设置好的宽度,375px、360px、320px。为什么是这个大小?这就要用到上面讲的知识点了。

iPhone6的屏幕分辨率是1334*750px,ppi是326,所以系数是2x。那device-width就等于750/2=375px。

红米1s的屏幕分辨率是1280*720px,ppi是312,所以系数是2x。那device-width就等于720/2=360px。

页面里的红色块给的是200*200px,在几个设备看起来好像差不多大的样子。

(上图左边是三星note4,右图是红米1s)

三星note4的屏幕分辨率是2560*1440px,ppi是515,所以系数是4x。那device-width就等于1440/4=360px,和小米的divice-width一样大小。但是看起来左边的红色块明显大于右边,因为左边的设备大啊,虽然两者都是分成360份,但是明显左边的1份比右边的1份大。

以上解释了为什么给元素赋予固定的像素值,给字体16px的大小,在大部分手机里居然能看起来差不多大小,也明白了我们并不需要写其他尺寸来适配不同的屏幕大小。

回答:拿到设计搞,如何开发适配不同屏幕尺寸?

  1. 1px问题 而在设备像素比大于1的屏幕上,我们写的1px实际上是被多个物理像素渲染,这就会出现1px在有些屏幕上看起来很粗的现象。

伪类+transform方案1:

.border-1px {
    position: relative;
    height: 1px;
    top: 0;
    left: 0;
    width: 100%;
    transform-origin: 50% 100%;
    border-bottom: 1px solid #ebebeb;
}
@media (-webkit-min-device-pixel-ratio: 2),
(min-resolution: 2dppx) {
    .border-1px {
        -webkit-transform: scaleY(.5);
                transform: scaleY(.5);
    }
}
@media (-webkit-min-device-pixel-ratio: 3),
(min-resolution: 3dppx) {
    .border-1px {
        -webkit-transform: scaleY(.3333);
                transform: scaleY(.3333);
    }
}
// heightwidth使div放大,但是border仍为1
// transform使
<div class='border-1px'></div>
  1. flexible方案

flexible方案是阿里早期开源的一个移动端适配解决方案,引用flexible后,

  • 我们在页面上统一使用rem来布局(width、height), rem 是相对于html节点的font-size来做计算的。布局(width、height)用rem.
  • 设置body的font-size,根据不同倍屏设置字体的大小。
<!DOCTYPE html>
<html>
<head>
    <meta charset=utf-8>
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <meta name="flexible" content="initial-dpr=2,maximum-dpr=3" />
    <title>flexible</title>
    <script>
        (function flexible(window, document) {
            var docEl = document.documentElement
            var dpr = window.devicePixelRatio || 1
            // adjust body font size
            function setBodyFontSize() {
                if (document.body) {
                    document.body.style.fontSize = (12 * dpr) + 'px'
                    console.log(document.body.style.fontSize)
                } else {
                    document.addEventListener('DOMContentLoaded', setBodyFontSize)
                }
            }
            setBodyFontSize();

            // set 1rem = viewWidth / 10
            function setRemUnit() {
                var rem = docEl.clientWidth / 10
                console.log(rem)
                docEl.style.fontSize = rem + 'px'
            }

            setRemUnit()

            // reset rem unit on page resize
            window.addEventListener('resize', setRemUnit)
            window.addEventListener('pageshow', function (e) {
                if (e.persisted) {
                    setRemUnit()
                }
            })

            // detect 0.5px supports
            if (dpr >= 2) {
                var fakeBody = document.createElement('body')
                var testElement = document.createElement('div')
                testElement.style.border = '.5px solid transparent'
                fakeBody.appendChild(testElement)
                docEl.appendChild(fakeBody)
                if (testElement.offsetHeight === 1) {
                    docEl.classList.add('hairlines')
                }
                docEl.removeChild(fakeBody)
            }
        }(window, document))
    </script>
    <style>
        img {
            width: 2rem;
            height: 2rem;
        }
        img.px {
            width: 75px;
            height: 75px;
        }
    </style>
</head>

<body>
    <div>2rem VS 75px</div>
    <br>
    <img src='./1.jpg' />
    <img class='px' src='./1.jpg' />
</body>
</html>

效果图: 缺点:屏幕越大,图像或字体显示更大而不是更多

  1. 项目中的适配方案: *. 通过rem转成两套px进行适配 *. 图片等使用flex布局
html {
    font-size: 100px;
    line-height: 100px;
}
@media screen and (max-width: 360px) {
    html {
        font-size: 90px;
        line-height: 90px;
    }
}
  1. 横屏竖屏安全适配、暗黑模式
  2. 低版本操作系统例如ios9、android5.1不支持es6语法,直接解析失败
  3. 引入vconsole可以再手机上显示控制台
  4. 防止滚动穿透
// 防止滚动穿透问题: 上层弹出以后给下层hidden;关闭弹层改为auto
document.body.style.overflow = this.isShowReasons ? 'hidden' : 'auto';
  1. ios fixed抖动
position: -webkit-sticky;
position: fixed;

15.图片不被压缩

.img-li {
        position: relative;
        box-sizing: content-box;
        width: 80px;
        height: 80px;
        margin: 0 8px 15px 0;
        border-radius: 6px;
        display: inline-block;
        vertical-align: middle;
        text-align: center;
        &:nth-child(4n) {
            margin-right: -8px;
        }
        img {
            width: 100%;
            height: 100%;
            object-fit: cover;
            border-radius: 6px;
        }
   }

参考:

深入了解viewport和px|【第414期】

关于移动端适配,你必须要知道的

怎么画一条0.5px的边