閱讀過幾篇關於 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),渲染像素,物理像素的指標,看了應該清晰這三者之間的關系

注:LED,發光二極管,是一種固態的半導體器件,它可以直接把電能轉化為光能。
在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就是物理像素,那么你可能就不知道怎么解決了,甚至百度半天都找不到答案
比如這個問題
面向邏輯像素開發的基本開發流程
- 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?》