盘点移动端适配方案

2020-02-07  本文已影响0人  learninginto

盘点移动端适配方案

移动端.png

作为开发者,在手机移动端做适配的时候会出现很多问题:最不希望用户看到的就是横向滚动条。其次是文字(图片等)的大小不能一成不变,要根据用户设备的物理像素调节大小。

手机浏览器是把页面放在一个虚拟的"窗口"(viewport)中,通常这个虚拟的"窗口"(viewport)比屏幕宽,这样就不用把每个网页挤到很小的窗口中(<u>这样会破坏没有针对手机浏览器优化的网页的布局</u>),用户可以通过平移和缩放来看网页的不同部分。

这就该轮到meta标签出场了。meta标签的作用是让当前viewport的宽度等于设备的宽度,同时不允许用户手动缩放。也许允不允许用户缩放,不同的网站有不同的要求,但让viewport的宽度等于设备的宽度,这个应该是大家都想要的效果,如果你不这样的设定的话,就会使用那个比屏幕宽的默认viewport,也就是说会出现横向滚动条;

一个常用的针对移动网页优化过的页面的 viewport meta 标签大致如下:

<meta name="viewport" content="width=device-width, initial-scale=1.0">

控制 viewport 的大小,可以指定的一个值,如 600,或者特殊的值,如 device-width 为设备的宽度(单位为缩放为 100% 时的 CSS 的像素)。

初始缩放比例,也即是当页面第一次 load 的时候缩放比例。

用户是否可以手动缩放(默认设置为no,因为我们不希望用户放大缩小页面)

允许用户缩放到的最小比例(默认设置为1.0)

允许用户缩放到的最大比例(默认设置为1.0)

一、rem适配

rem(font size of the root element)是指相对于根元素的字体大小的单位。简单的说它就是一个相对单位。看到rem大家一定会想起em单位,em(font size of the element)是指相对于父元素的字体大小的单位。它们之间其实很相似,只不过一个计算的规则是依赖根元素一个是依赖父元素计算。

正是因为它适配的标准是根元素的字体大小,而不同的手机型号对于根元素的规定不同[1],这便增加了很多不必要的判断。

二、vw适配

vw(viewpoint width),即视窗宽度,1vw等于视窗宽度的1%

计算方式(以750的设计稿为准):设计图中元素固定的大小(px) * 100 / 750 => vw

eg : 90px转化为vw : 90 * 100 / 750 => 12vw

如果是375的设计稿需要乘以2 : 设计图中元素固定的大小(px) * 2 * 100 / 750 => vw

不难看出,使用的时候仍需进行大量的计算。

三、vw+rem适配

因为vw的比例需要动态地计算,而rem做移动端布局的时候刚好需要动态地改变,因此我们只要稍加计算,将根元素的字体大小换成vw即可。

以上面的几种设计稿为例,尤其是750的设计稿居多,大多数情况下根据元素的尺寸 / 120 ,这么难以计算的数字,还是交给插件来做吧。以VScode中的cssrem为例:

注意:配置完参数之后,重启软件

cssrem.png

四、flexible.js布局(推荐)

通过flexible.js实现了rem自适应,有了flexible.js,我们就不必再为移动端各种设备兼容烦恼。通过rem与px的换算,把设计稿从px转到rem。再也不用为各种设备横行而担忧。

rem是相对于根元素html,这样就意味着,我们只需要在根元素确定一个px字号,则可以来算出元素的宽高。1rem=16px(浏览器html的像素,可以设定这个基准值),假如浏览器的html设为64px,则下面的元素则1rem=64px来运算。

阿里团队开源的一个库。使用flexible.js轻松搞定各种不同的移动端设备兼容自适应问题。

  1. 删除viewport的meta标签,替换为下面的JS代码

    (function (win, lib) {
        var doc = win.document;
        var docEl = doc.documentElement;
        var metaEl = doc.querySelector('meta[name="viewport"]');
        var flexibleEl = doc.querySelector('meta[name="flexible"]');
        var dpr = 0;
        var scale = 0;
        var tid;
        var flexible = lib.flexible || (lib.flexible = {});
    
        if (metaEl) {
            //        console.warn('将根据已有的meta标签来设置缩放比例');
            var match = metaEl.getAttribute('content').match(/initial\-scale=([\d\.]+)/);
            if (match) {
                scale = parseFloat(match[1]);
                dpr = parseInt(1 / scale);
            }
        } else if (flexibleEl) {
            var content = flexibleEl.getAttribute('content');
            if (content) {
                var initialDpr = content.match(/initial\-dpr=([\d\.]+)/);
                var maximumDpr = content.match(/maximum\-dpr=([\d\.]+)/);
                if (initialDpr) {
                    dpr = parseFloat(initialDpr[1]);
                    scale = parseFloat((1 / dpr).toFixed(2));
                }
                if (maximumDpr) {
                    dpr = parseFloat(maximumDpr[1]);
                    scale = parseFloat((1 / dpr).toFixed(2));
                }
            }
        }
    
        if (!dpr && !scale) {
            var isAndroid = win.navigator.appVersion.match(/android/gi);
            var isIPhone = win.navigator.appVersion.match(/iphone/gi);
            var devicePixelRatio = win.devicePixelRatio;
            if (isIPhone) {
                // iOS下,对于2和3的屏,用2倍的方案,其余的用1倍方案
                if (devicePixelRatio >= 3 && (!dpr || dpr >= 3)) {
                    dpr = 3;
                } else if (devicePixelRatio >= 2 && (!dpr || dpr >= 2)) {
                    dpr = 2;
                } else {
                    dpr = 1;
                }
            } else {
                // 其他设备下,仍旧使用1倍的方案
                dpr = 1;
            }
            scale = 1 / dpr;
    
        }
    
        docEl.setAttribute('data-dpr', dpr);
        if (!metaEl) {
            metaEl = doc.createElement('meta');
            metaEl.setAttribute('name', 'viewport');
            metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
            if (docEl.firstElementChild) {
                docEl.firstElementChild.appendChild(metaEl);
            } else {
                var wrap = doc.createElement('div');
                wrap.appendChild(metaEl);
                doc.write(wrap.innerHTML);
            }
        }
    
        function refreshRem() {
            var width = docEl.getBoundingClientRect().width;
    
            if (width / dpr > 768) {
                width = 768 * dpr;
            }
            var rem = width / 7.5;
            docEl.style.fontSize = rem + 'px';
            flexible.rem = win.rem = rem;
        }
    
        win.addEventListener('resize', function () {
            clearTimeout(tid);
            tid = setTimeout(refreshRem, 300);
        }, false);
        win.addEventListener('pageshow', function (e) {
            if (e.persisted) {
                clearTimeout(tid);
                tid = setTimeout(refreshRem, 300);
            }
        }, false);
    
        if (doc.readyState === 'complete') {
            doc.body.style.fontSize = 12 * dpr + 'px';
        } else {
            doc.addEventListener('DOMContentLoaded', function (e) {
                doc.body.style.fontSize = 12 * dpr + 'px';
            }, false);
        }
    
        refreshRem();
    
        flexible.dpr = win.dpr = dpr;
        flexible.refreshRem = refreshRem;
        flexible.rem2px = function (d) {
            var val = parseFloat(d) * this.rem;
            if (typeof d === 'string' && d.match(/rem$/)) {
                val += 'px';
            }
            return val;
        }
        flexible.px2rem = function (d) {
            var val = parseFloat(d) / this.rem;
            if (typeof d === 'string' && d.match(/px$/)) {
                val += 'rem';
            }
            return val;
        }
    
    })(window, window['lib'] || (window['lib'] = {}));
    
    
  2. 以750设计稿为准,元素尺寸 / 100 = rem ; 以375设计稿为准,元素尺寸 * 2 / 100 = rem ,因为flexble中会再除以2,所以这里乘以2将其抵消。

参考博客1 前端开发博客


  1. 根字体大小:iPhone5s:12px iPhone6S:14px iPhone6P:16px

上一篇下一篇

猜你喜欢

热点阅读