知乎問題:為什么很多web項目還是使用 px,而不是 rem?


閱讀過幾篇關於 px rem 的文章,感覺 rem 很強大。但是自己接觸到的公司項目全部都使用 px,想知道為什么。是我司技術更新落后了嗎?

我們當然有在用 vw 和 vh,但是只是在 layout 層級,組件層使用 px。

人數贊同最多的回答

先拋出觀點:

  • 本文建議讀者不要使用flexible或者其他修改viewport致使viewport width不等於device-width的方案(因為這會導致一些bug)
  • 本文建議讀者不要使用以rem或者小程序rpx來實現等比縮放為主的布局手段,而使用面向邏輯像素px為主,面向等比縮放的vx和vxxx(vw/vh/vmax/vmin)為輔助的布局單位,搭配一些flex等布局手段
  • 本文建議讀者一般情況遵循:同樣觀看距離情況下,大屏看的更多而不是大屏看的更大的設計最佳實踐來進行布局,並且以這種最佳實踐作為理論依據來傳遞給設計師(當然你覺得等比縮放才是合理的,請看其他回答,該回答不適合你)
  • 本文沒有鼓吹讀者使用響應式布局,也沒有鼓吹讀者盲目忽略兼容性使用vx和vxxx,同時也沒有反對讀者使用rem這個單位本身原有的定義來實現一些布局

吐槽

你們老是鼓吹rem的,醒醒吧,別再看網上大片大片的rem文章

flexible方案已經廢坑了,已經廢坑了,已經廢坑了

對,flexible已經不維護了,原因自己看flexible的github

px沒有問題,用rem來實現自適應才有問題(因為它是vx,vxxx單位的備胎),能問這種問題的人,應該認真研究一下viewport。

下面簡單介紹下上面的幾個知識點:

①:flexible.js

rem是相對於根元素<html>,這樣就意味着,我們只需要在根元素確定一個px字號,則可以來算出元素的寬高。1rem=16px(瀏覽器html的像素,可以設定這個基准值),假如瀏覽器的html設為64px,則下面的元素則1rem=64px來運算。

阿里團隊開源的一個庫flexible.js作用就是通過rem和px之間的換算,把設計稿從px轉到rem。兼容自適應各種移動端設備。

flexible.js關鍵代碼(通過js來調整html的字體大小,而在頁面中的制作稿則統一使用rem這個單位來制作):

;(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 > 540) {
            width = 540 * dpr;
        }
        var rem = width / 10;
        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'] = {}));

從上面的代碼,主要是改變了dpx和document的font-size大小。大小為docEl.getBoundingClientRect().width / 10 + 'px';

詳細可參考地址:《flexible.js 布局詳解》

css里邊px到底是什么

都9102了

都不知道px是viewport像素,

不是物理像素,不是邏輯像素,不是渲染像素

這里簡單解釋一下viewport像素,物理像素,邏輯像素,渲染像素

首先物理像素,邏輯像素,渲染像素的關系

下圖是部分iphone設備的邏輯像素(下圖為像素,其實應該翻譯為點 Points),渲染像素,物理像素的指標,看了應該清晰這三者之間的關系

物理像素(physical pixel)就是反映顯示屏的硬件條件,反映的就是顯示屏內部led燈的數量,可以簡單理解,一組三色led代表一個物理像素,當然根據屏幕物理屬性以及處理led的方法不一樣。強調這是物理的,因為這是一個純硬件指標。比如我把屏幕鋸了一半,物理像素就只有一半。
注:LED,發光二極管,是一種固態的半導體器件,它可以直接把電能轉化為光能。
渲染像素(render pixel),則是在系統內部對物理像素的分配進行再一次的調整,在pc上,渲染像素其實就是設置里邊的分辨率。對於顯示設備,系統為顯示設備提供渲染尺寸,由顯示設備的“縮放引擎”(帶存儲器陣列的數字視頻處理器)處理。這種“縮放引擎”一般內部有一系列的合理分辨率和一個推薦分辨率。一般推薦分辨率就是最大渲染像素,也是設備的物理分辨率(為了最佳表現)。這是一個軟硬件(偏硬)結合的縮放方案。由於部分設備不能設置渲染像素, 所以下文部分場景為了簡化模型,直接跳過渲染像素,直接等同於物理像素

邏輯像素/點(device point / device pixel / point ),是為了調和距離不一樣導致的差異,將所有設備根據距離,透視縮放到一個相等水平的觀看距離之后得到的尺寸,是一個抽象的概念,這個單位就是ios開發的px,安卓開發的dp。對於pc,包括win(8+) linux,mac,由各自系統的或者對應軟件(比如webview內部)提供的圖像界面處理引擎處理進行縮放

在win上,可以通過顯示設置縮放比例來調整部分應用的邏輯像素。對於linux,可以通過x和wayland的縮放比例來調整

但是,由於這個是一個純軟件的方案,如果部分軟件不遵循開發規則,或者使用老舊的api,就會導致邏輯像素不合理,導致縮放問題。例如win10中部分舊的軟件在高分屏的設備會導致界面偏小。因為他們實際是使用的是渲染像素而不是邏輯像素

各種設備,手機,平板,筆記本等邏輯像素

手機:邏輯像素在3xx-4xx(短邊)之間

平板:10寸平板7xx-8xx(短邊)

筆記本:13寸 1280 (長邊)

24寸顯示屏:1920(長邊)

你會發現如果設置width=device-width下,無論是否高分屏,在瀏覽器得到的screen.width仍然符合上述的尺寸

邏輯像素的引入,簡單來說,就是為了消除了不同屏幕觀看距離和不同ppi(見下文)之間的差異,衍生出來的一個虛擬的尺寸

ppi(pixel per inch) 每英寸像素,指的是屏幕在每英寸的物理像素,更高的ppi意味着屏幕的清晰度更佳。

所謂高分屏,其實就是指ppi大於同類設備的屏幕。比如對於桌面設備,大於96ppi。對於移動設備,大於160ppi

所謂視網膜屏,其實就是指在該觀看距離內超出人類的辨認能力的屏幕。比如對於桌面設備,大於192ppi。對於移動設備大於326ppi

ppi,對於移動設備而言,一般來說ppi以160為一個檔次

也就是假設一個ppi160,2寸x3寸的屏幕,物理像素應該是320x480。同理ppi320,同樣尺寸的屏幕,物理像素是640x960

由於它們尺寸一致,假設它們觀看距離一致,那么消除掉ppi的影響,他們的邏輯像素是一致的

也就是

邏輯像素長度 = 物理像素長度 * 160 / ppi

得出都是 320 x 480

當然,由於生產標准不一致,不可能做到絕對的160ppi作為標准,所以ppi的等級划分是動態的

dpr (device point ratio / device pixel ratio) 渲染像素與邏輯像素的比例。由於渲染像素一般等於邏輯像素,如果ppi是以160為基准的話,那么 dpr = ppi / 160

多少倍屏或者多少x(三倍屏,3x,意思就是3dpr),一般來說就是說的是這個值

viewport像素又是什么,它本質是DIP(Device Independent Pixels),中文意思設備無關像素,是與上述所有像素都無絕對邏輯關系的一個單位。其實是瀏覽器內部對邏輯像素進行再處理的結果,簡單來理解就是調整邏輯像素的縮放來達到適應設備的一個中間層

對於pc,viewport是不生效的,所以在pc上,px其實就是邏輯像素(chrome)。但是邏輯像素是與軟件實現有關的,所以會出現一些問題。比如在win上,對於部分國產馬甲瀏覽器,viewport內部沒有適配系統的縮放等級,導致渲染的內容過小

如果你像評論區的某些看客一樣麻木認為pc的px就是物理像素,那么你可能就不知道怎么解決了,甚至百度半天都找不到答案

比如這個問題

谷歌瀏覽器的100%顯示和搜狗125%顯示大小相同?

面向邏輯像素開發的基本開發流程

  • 1. 在head 設置width=device-width的viewport
  • 2. 在css中使用px
  • 3. 在適當的場景使用flex布局,或者配合vw進行自適應
  • 4. 在跨設備類型的時候(pc <-> 手機 <-> 平板)使用媒體查詢
  • 5. 在跨設備類型如果交互差異太大的情況,考慮分開項目開發

那么viewport width=device-width是什么意思,其實就是讓viewport的尺寸等於邏輯像素的尺寸

關於px的疑問

那有朋友就問,不同設備的物理像素是不一樣的呀,我怎么實現不同物理像素的布局,如果設計師給你一張圖,怎么將它轉為我想要的在css里邊的px

首先,你要讀懂設計師給你設計圖的意圖,一般國內的設計師,給出手機版的設計圖,一般是750px,注意這里的px,並不是我們的px(邏輯像素),其實是物理像素,因為設計師是根據iphone6或者同等設備來進行設計的,那么要知道iphone6的邏輯像素其實是 375,dpr是2,那么拿到手的設計稿,轉換為邏輯像素,就得除以2,我們叫這種設計圖,叫兩倍圖

同理,如果是375 + 375 + 375大小,那么我們就得除以3,叫三倍圖

如果設計團隊有使用墨刀或者藍湖,你可以在兩者里邊設置你的查看尺寸,得到我們需要的邏輯像素

如果設計師不用藍湖等工具,給你的並不是375的倍數怎么辦,我先說辦法,原因你們自己琢磨,我不細致分析

最簡單的方法,設計師給你的圖的物理寬度w,除以一個數x,如果得的出來的商在360 - 414之間,那么你換算的公式為【你在設計圖測量出來的物理像素數除以x】,那么dpr就是x,也就是x倍圖

那么朋友又會問,不同設備邏輯像素也不一樣呀

對,不一樣,但問題為什么我們要將它們弄成一樣?其實,不一樣,才是合理的。我暫且不說原因,原因后面的文章解釋

那么不一樣的情況,怎么布局?那就靠你們技術手段,flex,流式布局,vw等

 總結

  • 設計師給的設計稿都是物理像素(比如750px),我們實際處理的是移動端的屏幕尺寸px是邏輯像素(比如iphone6的邏輯像素是375);由於PC端viewport是不生效的,所以pc端的px就是邏輯像素(chrome)
  • ppi(pixel per inch) 每英寸像素,指的是屏幕在每英寸的物理像素,更高的ppi意味着屏幕的清晰度更佳。對於移動設備而言,一般來說ppi以160為一個檔次。
  • dpr (device point ratio / device pixel ratio) 渲染像素與邏輯像素的比例。由於渲染像素一般等於邏輯像素,如果ppi是以160為基准的話,那么 dpr = ppi / 160.
  • 邏輯像素長度 = 物理像素長度 * 160 / ppi,也即是邏輯像素長度 = 物理像素長度 * 1/dpr;

知乎問題地址及詳細答案:《為什么很多web項目還是使用 px,而不是 rem?》


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM