利用rem實現網頁的等比適配

可能是理解viewport最好的方式說過使用viewport來實現等比適配的例子,本文來講講等比適配的另一種方式

推導

拿到一個寬度為vWidth的視覺稿
設設備屏幕寬度為dWidth
在視覺稿上量得一個元素的寬度為eleVWidth
那么要實現按照寬度等比適配,在各種設備中元素的實際寬度X將滿足公式

    //等比
    eleVWidth/vWidth = X/dWidth;

    //我們的目的是求X
    X = (eleVWidth/vWidth)dWidth;

拿到視覺稿之后,eleVWidth和vWidth都已經固定了,但是dWidth在不同的設備中確實不同的,而且需要實際運行的時候我們才能拿到dWidth,這個時候就要利用rem的特性了,我們通過阻塞運行的js動態設置rem,使得rem=dWdith/10,套入剛才的公式:


    X=(eleVWidth/vWidth)*10*rem

    //體現在css中
    .className {
        width: (eleVWidth/vWidth)*10 rem
    }

為什么設置是dWdith/10呢?方便計算而已,當然也可以除以別的基數,但是盡量不要讓rem太小,有兼容性問題

動態設置rem

這段css需要放在body標簽的開始位置,因為rem值要盡可能早的設置成功,避免無謂的重排重繪(有些設備中若元素樣式已經生效,動態改變rem未必能起作用,所以這個動作更需要提前)

    <body>
        <script>
        (function() {
            var ua = window.navigator.userAgent;
            var docEl = document.documentElement;
            var html = document.querySelector('html');
            var isAndorid = /Android/i.test(ua);
            var dpr = window.devicePixelRatio || 1;
            var rem = docEl.clientWidth / 10;

            // 設置 rem 基準值
            html.style.fontSize = rem + 'px';

            // Nexus 5 上 rem 值不準,
            // 如:設置100px,getComputedStyle 中的值卻為 85px,導致頁面錯亂
            // 這時需要檢查設置的值和計算后的值是否一樣,
            // 不一樣的話重新設置正確的值
            var getCPTStyle = window.getComputedStyle;
            var fontSize = parseFloat(html.style.fontSize, 10);
            var computedFontSize = parseFloat(getCPTStyle(html)['font-size'], 10);
            if (getCPTStyle && Math.abs(fontSize - computedFontSize) >= 1) {
                html.style.fontSize = fontSize * (fontSize / computedFontSize) + 'px';
            }

            // 設置 data-dpr 屬性,留作的 css hack 之用
            html.setAttribute('data-dpr', dpr);

            // 安卓平臺額外加上標記類
            if (isAndorid) {
                html.setAttribute('data-platform', 'android');
            }
        })();
        </script>
        ...
    </body>

css中應該怎么寫

如果每在視覺稿上量出一個值,在寫到樣式文件之前都得通過那個公司計算一翻,那絕對不是一個好策略,我們希望量到啥就寫啥

使用less,sass等css預處理器的函數

    //設計稿為640
    @function rem($val) {
        @return $val/64 * 1rem;;
    }

    //設計稿為750
    @function rem750($val) {
        @return $val/75 * 1rem;
    }

    //使用
    .className {
        width: @rem(100);
    }

也可以使用webpack-loader,change-rem-loader,這個loader代碼做的事也是上述的轉化計算(寫一個webpack loader并不復雜,你可以自己寫一個符合自己情況的loader)

    'use strict';
    module.exports = function(source) {
        source = source.replace(/rem(\d*)\((.*?)\)/g, function(match, type, value) {
            var divVal;

            // 不做 try catch,及早發現錯誤以免造成故障
            switch (type) {
                // 1080寬度的設計稿,rem1080(value)
                case '1080':
                    divVal = 108;
                    break;
                    // 默認為750寬度的設計稿,rem(value)
                case '750':
                    divVal = 75;
                    break;
                default:
                    divVal = 64;
            }

            return (value / divVal).toFixed(6) + 'rem';
        });
        return source;
    }

這樣之后,我們css同樣可以這樣寫(如何讓loader起作用不是本文的范圍)

    .className {
        width: rem(100);
    }

也許你的工程技術棧里面,使用的不是webpack,也沒有使用less、sass等預處理器,那你可以根據你的實際情況去尋找一種預處理方案,只要達到推導公式的效果就可以了。
什么?沒有合適的方案,那你是時候充充電了

古典2048
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容