移动端适配问题的理解

最近无意间再次接触了适配问题,虽然之前也一直在使用诸如rem类的方案,也研究过引入的适配代码,突然让自己说出其中原委,却模棱两可,真是尴了个大尬,说到底还是没有彻底弄清除。

1、网络求助

自己没弄懂,就只有寻求网络,但是查找了许多博客、笔记好像都写的有点不清晰,很多上来就是 1rem = 100px,看得我也是一头雾水,最后找到一篇文章,按照其理解貌似懂了。原文链接

2、rem适配原理

首先要知道rem是一个相对单位相对于根节点大小来的,假设根节点大小为 50px,则1rem = 50px,2rem = 100px,也就是说 根节点大小npx 其实就等于 1rem,npx = 1rem

3、实际情况

现在移动端设备屏幕大小都不统一,但是我们的设计稿只有一张(固定大小,总不可能一种设备对应一张设计图吧)那如何把一张设计稿运用到设备尺寸不一的移动设备上呢?

其实我们可以想想地图是怎么做的,地图就是有个比例尺,地图上尺寸都是等比缩放的。其实我们也可以做成等比缩放,也就是 750px 的设计稿,对应不同的屏幕宽度,所以就要计算比值 n ,而这个比值实际就是我们需要的设置的根节点大小,我们的 rem 值都是根据这个比值来计算的。

记住上面结论 npx(根节点大小) = 1rem

4、理论推导过程

设备宽度 W,设计稿 750 ,设计稿上有一大小 Wui,那么对应到屏幕上(Wr)应该是多少呢?

 等比缩放
 W / 750 = Wr / Wui  
 Wr = W / 750 * Wui = n * Wui = 1rem * Wui
 因为由上可知 根节点大小 = 1rem
 也就是比值 n = 1rem
 所以  Wr = 1rem * Wui
 即:假设ui测量是 100px,css应该写 100*1rem = 100rem
 对应到屏幕上浏览器会将rem转换为真实大小(100rem * 比值)

由上可知,当我们使用rem为单位时,先求出 屏幕宽设计稿的比值n,也就是1rem的值,并将其设置为根节点字体大小,然后ui测量值 * 1rem 就可以得到实际的rem值

结合实际,假设一设备屏幕宽 370,设计稿 750,得出 n = 0.5,那么,测量100px = 100 * 1rem = 100rem

5、实际开发时

上面我们知道了,只要确定了比值 n,也就是根节点大小(1rem),就可以得到 实际rem = n * 测量值 = 1rem * 测量值

但是有一个问题,但实际上浏览器一般最小字体为12px,也就是比值最小 n = 12 的时候(这里要明白,当你设置了根节点的字体大小为12px,在没使用rem的情况下,默认大小就12px了,所以考虑12是这么来的),W / 750 = 12 此时屏幕宽 = 9000显然不合理,屏幕宽度通常375左右,此时我们将其放大100倍,比值n也就扩大了100倍,那么对应的 1rem就变为1/100rem

所以实际rem = 1/100rem * 测量值,所以当测量值为100px时,对应的rem就等于1rem,因此得到 1rem = 100px

6、总结

最后我们可以知道,当确定好屏幕宽度与设计稿宽度的比值,再将其扩大100倍,这个就是根节点字体大小,计算rem值,只需要测量尺寸/100=需要的rem值,也就是常说的 1rem = 100px的效果。

7、1实际运用

!function (n) {
    
    
            var e = n.document,
                t = e.documentElement,
                i = 720,
                o = "orientationchange" in n ? "orientationchange" : "resize",
                a = function () {
    
    
                    var n = t.clientWidth || 320; n > 720 && (n = 720);
                    t.style.fontSize = n * 100 / i + "px"
                };
            e.addEventListener && (n.addEventListener(o, a, !1), e.addEventListener("DOMContentLoaded", a, !1))
        }(window)

在这里插入图片描述
上面代码就是将比值扩大100倍,然后 测量值 / 100就是所需要的rem值

7、2 vw + rem

还有一种则是 vw+rem 方案,其核心还是预设 1rem = 100px

  1. 750px = 100vw,我们仍然想要 1rem = 100px
  2. 问题转变成 1rem = ? vw
  3. 100px = 100vw / 7.5 = 1rem
  4. 1rem = 13.33333vw

所以设置根节点大小为 13.33333vw,此时rem的计算规则还是 测量值 /100

也可以用vw作为单位,1rem = 100px = 13.33333vw
那么 1px =0.1333333vw

测量100px = calc(0.1333333vw * 100),这种以vw为单位,而不是rem,可以有效避免样式冲突问题

tips:
比如某项目设置了根节点大小,采用的是rem为单位,此时你的第三方组件也需要做适配,刚好你也用rem为单位,那么假如根节点比值不一样致势必会有样式冲突。

为了避免这个问题,同时为了方便计算,你也想用 1rem = 100px 的方案,但是又不能用rem,这时就可以小小转换下

1rem = 100px = 13.33333vw ,1px = 0.1333333vw (也就有了上面 calc(0.1333333vw * 100) 的写法)

改用vw为单位,这样你既用了100的比值,但又没有使用rem为单位,自然尺寸也就不会根据根节点转换了。

8 、也可以这样理解

假设:设备375px设计稿 750px,同样采用rem,那么我们的问题就是如何设定rem的基准值

这里我们采用 分成10份 的方案来计算基准值

375px的设备,分成10份,1rem = 37.5px
750px的设计稿,分成10分,1rem = 75px

那么,ui图上有一大小为100px应该等于多少rem?
由上述分份规则可知 100 / 75 = 1.33333rem (计算在750上占多少份)
对应到设备上时 1.33333 * 37.5 = 50px (乘的设备375的基准值)

上述思路就是,都以 当前宽度1/10 为rem的基准值,进而可以得出ui测量宽度等于多少rem,那么,不管你基准值为多少,只要计算方法都是当前宽度1/10,那么测量宽度/基准值 所得rem份额是不会变的,变的只是rem具体的值而已(仔细品味这句话)

上面 375 和 750 计算得出的 rem基准值就一样,但是他们的计算规则都是1/10,所以750所得rem值在375上仍然是1/10,只是值变了。

9、适配js参考

(function(window) {
    
    
    /* 设计图文档宽度 */
    var docWidth = 750
    var version = ''

    var doc = window.document
    var docEl = doc.documentElement
    var resizeEvt =
            'orientationchange' in window ? 'orientationchange' : 'resize'

    var recalc = (function refreshRem() {
    
    
        var clientWidth = docEl.getBoundingClientRect().width
        /* 8.55:小于320px不再缩小,11.2:大于420px不再放大 */
        // 12.8  480px   iPhone12 pro max  428
        // 6.67 250px Galaxy fold 280
        docEl.style.fontSize =
            Math.max(Math.min(20 * (clientWidth / docWidth), 12.8), 6.67) * 5 + 'px'

        return refreshRem
    })()

    /* 添加倍屏标识,安卓为1 */
    docEl.setAttribute(
        'data-dpr',
        window.navigator.appVersion.match(/iphone/gi)
            ? window.devicePixelRatio
            : 1
    )

    if (/iP(hone|od|ad)/.test(window.navigator.userAgent)) {
    
    
        /* 添加IOS标识 */
        doc.documentElement.classList.add('ios')
        /* IOS8以上给html添加hairline样式,以便特殊处理 */
        version = window.navigator.appVersion.match(/OS (\d+)_(\d+)_?(\d+)?/)
        if (version !== null && parseInt(version[1], 10) >= 8) {
    
    
            doc.documentElement.classList.add('hairline')
        }
    }
    if (!doc.addEventListener) return
    window.addEventListener(resizeEvt, recalc, false)
    doc.addEventListener('DOMContentLoaded', recalc, false)
})(window)

猜你喜欢

转载自blog.csdn.net/qq_45219069/article/details/121764585