黑马程序员技术交流社区

标题: 【上海校区】关于移动端 rem 布局的一些总结 [打印本页]

作者: frainbowv    时间: 2019-8-22 22:58
标题: 【上海校区】关于移动端 rem 布局的一些总结
【资源一】基础知识恕不回顾
基础知识参考以下两篇博客:
http://isux.tencent.com/web-a...
http://www.w3cplus.com/css3/d...
【资源二】淘宝m站首页的动态实现
学习http://m.taobao.com 首页的实现。
最近读到@大漠的新文章《使用Flexible实现手淘H5页面的终端适配》,和本部分有点关系。暂且加上来以供参考。(updated 2015-11-24)
源码进行美化、解读之后,基本布局部分的代码已经被我还原出来了:(2016-01-13补充:后来才发现,早就开源在github上了)
[JavaScript] 纯文本查看 复制代码

!function(win, lib) {
    var timer,
        doc     = win.document,
        docElem = doc.documentElement,

        vpMeta   = doc.querySelector('meta[name="viewport"]'),
        flexMeta = doc.querySelector('meta[name="flexible"]'),

        dpr   = 0,
        scale = 0,

        flexible = lib.flexible || (lib.flexible = {});

    // 设置了 viewport meta
    if (vpMeta) {

        console.warn("将根据已有的meta标签来设置缩放比例");
        var initial = vpMeta.getAttribute("content").match(/initial\-scale=([\d\.]+)/);

        if (initial) {
            scale = parseFloat(initial[1]); // 已设置的 initialScale
            dpr = parseInt(1 / scale);      // 设备像素比 devicePixelRatio
        }

    }
    // 设置了 flexible Meta
    else if (flexMeta) {
        var flexMetaContent = flexMeta.getAttribute("content");
        if (flexMetaContent) {

            var initial = flexMetaContent.match(/initial\-dpr=([\d\.]+)/),
                maximum = flexMetaContent.match(/maximum\-dpr=([\d\.]+)/);

            if (initial) {
                dpr = parseFloat(initial[1]);
                scale = parseFloat((1 / dpr).toFixed(2));
            }

            if (maximum) {
                dpr = parseFloat(maximum[1]);
                scale = parseFloat((1 / dpr).toFixed(2));
            }
        }
    }

    // viewport 或 flexible
    // meta 均未设置
    if (!dpr && !scale) {
        // QST
        // 这里的 第一句有什么用 ?
        // 和 Android 有毛关系 ?
        var u = (win.navigator.appVersion.match(/android/gi), win.navigator.appVersion.match(/iphone/gi)),
            _dpr = win.devicePixelRatio;

        // 所以这里似乎是将所有 Android 设备都设置为 1 了
        dpr = u ? ( (_dpr >= 3 && (!dpr || dpr >= 3))
                        ? 3
                        : (_dpr >= 2 && (!dpr || dpr >= 2))
                            ? 2
                            : 1
                  )
                : 1;

        scale = 1 / dpr;
    }

    docElem.setAttribute("data-dpr", dpr);

    // 插入 viewport meta
    if (!vpMeta) {
        vpMeta = doc.createElement("meta");
         
        vpMeta.setAttribute("name", "viewport");
        vpMeta.setAttribute("content",
            "initial-scale=" + scale + ", maximum-scale=" + scale + ", minimum-scale=" + scale + ", user-scalable=no");

        if (docElem.firstElementChild) {
            docElem.firstElementChild.appendChild(vpMeta)
        } else {
            var div = doc.createElement("div");
            div.appendChild(vpMeta);
            doc.write(div.innerHTML);
        }
    }

    function setFontSize() {
        var winWidth = docElem.getBoundingClientRect().width;

        if (winWidth / dpr > 540) {
            (winWidth = 540 * dpr);
        }

        // 根节点 fontSize 根据宽度决定
        var baseSize = winWidth / 10;

        docElem.style.fontSize = baseSize + "px";
        flexible.rem = win.rem = baseSize;
    }

    // 调整窗口时重置
    win.addEventListener("resize", function() {
        clearTimeout(timer);
        timer = setTimeout(setFontSize, 300);
    }, false);

     
    // 这一段是我自己加的
    // orientationchange 时也需要重算下吧
    win.addEventListener("orientationchange", function() {
        clearTimeout(timer);
        timer = setTimeout(setFontSize, 300);
    }, false);


    // pageshow
    // keyword: 倒退 缓存相关
    win.addEventListener("pageshow", function(e) {
        if (e.persisted) {
            clearTimeout(timer);
            timer = setTimeout(setFontSize, 300);
        }
    }, false);

    // 设置基准字体
    if ("complete" === doc.readyState) {
        doc.body.style.fontSize = 12 * dpr + "px";
    } else {
        doc.addEventListener("DOMContentLoaded", function() {
            doc.body.style.fontSize = 12 * dpr + "px";
        }, false);
    }
  
    setFontSize();

    flexible.dpr = win.dpr = dpr;

    flexible.refreshRem = setFontSize;

    flexible.rem2px = function(d) {
        var c = parseFloat(d) * this.rem;
        if ("string" == typeof d && d.match(/rem$/)) {
            c += "px";
        }
        return c;
    };

    flexible.px2rem = function(d) {
        var c = parseFloat(d) / this.rem;
         
        if ("string" == typeof d && d.match(/px$/)) {
            c += "rem";
        }
        return c;
    }
}(window, window.lib || (window.lib = {}));
注意:
淘宝首页在iPhone4上设置的initial-scale是0.5(其他尺寸类似)。
因此,这句在iPhone4上得出的结果是640:
[JavaScript] 纯文本查看 复制代码
var winWidth = docElem.getBoundingClientRect().width;
正是因为淘宝这种独特的设置,使得 ios 上 1px边框的问题完美解决(1px变2px, 又被 initial-scale=0.5 缩小了一半)。
【资源三】常规情况下js根据屏幕宽度动态计算
使用js动态计算:
[JavaScript] 纯文本查看 复制代码
!(function(doc, win) {
    var docEle = doc.documentElement,
        evt = "onorientationchange" in window ? "orientationchange" : "resize",
        fn = function() {
            var width = docEle.clientWidth;
            width && (docEle.style.fontSize = 20 * (width / 320) + "px");
        };
     
    win.addEventListener(evt, fn, false);
    doc.addEventListener("DOMContentLoaded", fn, false);

}(document, window));
【资源四】媒体查询较密集的断点
使用css3 media query 实现
[JavaScript] 纯文本查看 复制代码
@media screen and (min-width: 320px) {
    html {font-size: 14px;}
}

@media screen and (min-width: 360px) {
    html {font-size: 16px;}
}

@media screen and (min-width: 400px) {
    html {font-size: 18px;}
}

@media screen and (min-width: 440px) {
    html {font-size: 20px;}
}

@media screen and (min-width: 480px) {
    html {font-size: 22px;}
}

@media screen and (min-width: 640px) {
    html {font-size: 28px;}
}
【资源五】强大的单位——vw
使用单位 vw 实现动态计算。
[JavaScript] 纯文本查看 复制代码
html {
    font-size: 31.25vw; /* 表达式:100*100vw/320 */
}
不过考虑到国内兼容性的问题,还是结合媒体查询来使用比较好。(媒体查询的断点暂时是借用上面的例子)
[JavaScript] 纯文本查看 复制代码
@media screen and (min-width: 320px) {
    html {
        font-size: 100px;
    }
}

@media screen and (min-width: 360px) {
    html {
        font-size: 112.5px;
    }
}

@media screen and (min-width: 400px) {
    html {
        font-size: 125px;
    }
}

@media screen and (min-width: 440px) {
    html {
        font-size: 137.5px;
    }
}

@media screen and (min-width: 480px) {
    html {
        font-size: 150px;
    }
}

@media screen and (min-width: 640px) {
    html {
        font-size: 200px;
    }
}

html {
    font-size: 31.25vw;
}

【总结】
对以上种种方法的综合:
1、meta:viewport, 还是initial-scale为 1;
2、320px屏幕下,把页面根元素html的字体大小设置为50px;
3、鉴于我们拿到的设计图目前是640px宽的基准,这样我们就不用每次自己除以2了,直接在PS中量就好;
4、宽度什么的最好还是用百分比处理;涉及到高度、字体大小之类的则用rem。
eg:
设计稿上,div高度为40px;那么css就是 div {height: 0.4rem;}
结果就只剩下一步转换:设计稿上量的长度转化为小数。 50% => 0.5 这种计算,不要太简单。。。
【方法一】纯粹css,支持calc函数的动态计算;不支持的用css媒体查询断点,优雅降级。
[JavaScript] 纯文本查看 复制代码
@media screen and (min-width: 320px) {
    html {
        font-size: 50px;
    }
}

@media screen and (min-width: 360px) {
    html {
        font-size: 56px;
    }
}

@media screen and (min-width: 400px) {
    html {
        font-size: 63px;
    }
}

@media screen and (min-width: 440px) {
    html {
        font-size: 69px;
    }
}

@media screen and (min-width: 480px) {
    html {
        font-size: 75px;
    }
}

/**
* 2016-01-13 订正
* 做适当限制
* 大于640的屏幕 固定为100px
* 同时需要对body或者最外层wrapper做max-width: 640px的限制
*/
/*
@media screen and (min-width: 640px) {
    html {
        font-size: 100px;
    }
}

html {
    font-size: 15.625vw;
}
*/

html {
    font-size: 15.625vw;
}

@media screen and (min-width: 640px) {
    html {
        font-size: 100px;
    }
}
【方法二】脚本动态计算
大前提:
1、initial-scale 为 1;
2、在项目css中(注意不要被公共的base、common之类的影响了,资源加载顺序也是蛮重要的),先把html的fontSize设置为 50px(或者加上媒体查询代码), 避免加载未完成时候样式错乱;
[JavaScript] 纯文本查看 复制代码
/* css */
html {font-size: 50px;}
[JavaScript] 纯文本查看 复制代码
/* javascript */

!(function(win, doc){
    function setFontSize() {
        // 获取window 宽度
        // zepto实现 $(window).width()就是这么干的
        var winWidth =  window.innerWidth;
        // doc.documentElement.style.fontSize = (winWidth / 640) * 100 + 'px' ;
        
        // 2016-01-13 订正
        // 640宽度以上进行限制 需要css进行配合
        var size = (winWidth / 640) * 100;
        doc.documentElement.style.fontSize = (size < 100 ? size : 100) + 'px' ;
    }

    var evt = 'onorientationchange' in win ? 'orientationchange' : 'resize';
   
    var timer = null;

    win.addEventListener(evt, function () {
        clearTimeout(timer);

        timer = setTimeout(setFontSize, 300);
    }, false);
   
    win.addEventListener("pageshow", function(e) {
        if (e.persisted) {
            clearTimeout(timer);

            timer = setTimeout(setFontSize, 300);
        }
    }, false);

    // 初始化
    setFontSize();

}(window, document));
文章转载至:https://segmentfault.com/a/1190000003690140





欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2