設備屏幕的物理像素,表示屏幕上能夠鋪多少個點點,而不是一個絕對長度單位(例如in,mm); 單位是px,好比iPhone6的 (750 x 1334px)css
一個物理概念
對於屏幕,分辨率通常表示屏幕上顯示的物理像素總和。好比,咱們說iPhone6屏幕分辨率是(750 x 1334px)
對於圖像,概念等同於圖像尺寸、圖像大小、像素尺寸等等。好比,咱們說(20 x 20px)的iconhtml
是Web編程的概念,指的是CSS樣式代碼中使用的邏輯像素, 或者稱爲設備獨立像素
, 由於只與設備相關;
1個CSS像素在不一樣設備上可能對應不一樣的物理像素數,這個比值是設備的屬性(Device Pixel Ratio,設備像素比),好比,iPhone6:375 x 667pxandroid
經過 document.documentElement.clientWidth/clientHeight / document.documentElement.getBoundingClientRect().width
獲取ios
在CSS規範中,長度單位能夠分爲絕對單位和相對單位。px是一個相對單位,相對的是設備像素(Device Pixels)css3
Android設備的特色是屏幕尺寸不少,所以爲了顯示能儘可能和設備無關,提出了dip,參照的density是160。git
// 當屏幕密度density爲160(單位是ppi或者dpi,一個意思)時,px === dip
px = dip * density / 160
// 因此
dip = px * 160 / density
複製代碼
注: 此處只針對於Android, windows 也有 DIP 概念, 含義不一樣, IOS貌似不存在
github
Device pixel ratio, the ratio between physical pixels and logical pixels used by cascading style sheets (CSS): other names for it are 「CSS Pixel Ratio」 and 「dppx」 表示1個CSS像素(寬度)等於幾個物理像素(寬度)web
DPR = 物理像素(設備像素) / 邏輯像素(css像素/設備獨立像素) // [未縮放]
複製代碼
像素密度也叫顯示密度或者屏幕密度,縮寫爲DPI(Dots Per Inch)或者PPI(Pixel Per Inch)編程
// 屏幕對角線的像素尺寸 / 物理尺寸(inch 英寸)
Math.sqrt(750*750 + 1334*1334) / 4.7 = 326ppi
複製代碼
桌面上視口寬度等於瀏覽器寬度,但在手機上有所不一樣。windows
手機上爲了容納爲桌面瀏覽器設計的網站,默認佈局視口寬度遠大於屏幕寬度,爲了讓用戶看到網站全貌,它會縮小網站 document.documentElement.clientWidth
屏幕的可視區域,即物理像素尺寸, 可變, 與當前縮放值和設備的屏幕寬度有關 visual viewport寬度 = ideal viewport寬度 / 當前縮放值
能夠經過window.innerWidth
來獲取,但在Android 2, Oprea mini 和 UC 8中沒法正確獲取。
ideal viewport是最適合移動設備的viewport,ideal viewport的寬度等於移動設備的屏幕寬度 在移動開發時, 在meta[name='viewport']中, 經過width = device-width
把當前的viewport寬度設置爲理想視口, 不然寬度將默認爲佈局視口980
ideal viewport並無一個固定的尺寸,不一樣的設備擁有有不一樣的ideal viewport。早期全部iPhone理想視口爲320x480px
因此,在沒有縮放的狀況下,屏幕的CSS像素寬度實際上是指理想視口的寬度,而meta標籤:
<meta name="viewport" content="width=device-width, inital-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
複製代碼
指定了佈局視口=理想視口,而且禁止縮放。因此添上width=device-width的viewport meta後頁面變大了(一開始頁面內容小得看不清),其實是佈局視口變小了
initial-scale=1 解決了 iphone、ipad 不管橫豎屏都把寬度設爲豎屏時 ideal viewport 的寬度 width=device-width 解決了IE 不管橫豎屏都把寬度設爲豎屏時 ideal viewport 的寬度
視覺視口與理想視口關係:
visual viewport寬度 = ideal viewport寬度 / 當前縮放值
當前縮放值 = ideal viewport寬度 / visual viewport寬度
複製代碼
參考: www.ayqy.net/blog/徹底理解px… github.com/jawil/blog/…
一個相對長度單位。做用於根元素,相對於初始值/默認值大小;做用域非根元素,相對於根元素html字體大小(經常使用)
Font size of the parent, in the case of typographical properties like font-size, and font size of the element itself, in the case of other properties like width.
做用於font-size屬性,相對於父元素字體大小(經常使用);做用於非font-size屬性,相對於自身字體大小
媒體查詢 前提: <meta name="viewport" content="width=device-width"/>
mate 中的 device-width / width
:
最多見的方式,經過屏幕寬度(用CSS像素描述的寬度)來區分各類設備,以下:
@media (min-width:320px) { /* smartphones, portrait iPhone, portrait 480x320 phones (Android) */ }
@media (min-width:480px) { /* smartphones, Android phones, landscape iPhone */ }
@media (min-width:600px) { /* portrait tablets, portrait iPad, e-readers (Nook/Kindle), landscape 800x480 phones (Android) */ }
@media (min-width:801px) { /* tablet, landscape iPad, lo-res laptops ands desktops */ }
@media (min-width:1025px) { /* big landscape tablets, laptops, and desktops */ }
@media (min-width:1281px) { /* hi-res laptops and desktops */ }
min-width: 480px: Will target mobile devices in landscape mode and up
複製代碼
//
@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: 414px) {
html {
font-size: 63px;
}
}
複製代碼
rem: 根元素(html)的字體大小. 即 1rem = html中設置的font-size
獲取設備寬度 document.documentElement.getBoundingClientRect().width / document.documentElement.clientWidth
原理: 實質時比例問題, 根據設計圖比例計算出固定 rem 值 實現: 主要經過修改 html 的 fontSize
(function (doc, win) {
var docEl = doc.documentElement,
resizeEvt = 'orientationchange' in window ? 'orientationchange' : 'resize',
recalc = function () {
var clientWidth = docEl.clientWidth;
if (!clientWidth) return;
if(clientWidth>=640){
docEl.style.fontSize = '100px';
}else{
docEl.style.fontSize = 100 * (clientWidth / 640) + 'px'; // 640: 可根據設計圖來定
}
};
if (!doc.addEventListener) return;
win.addEventListener(resizeEvt, recalc, false);
doc.addEventListener('DOMContentLoaded', recalc, false);
})(document, window);
複製代碼
缺點:
var root = window.document.documentElement;
var fontSize = parseFloat(root.style.fontSize);
// html最終的font-size大小
var finalFontSize = parseFloat(window.getComputedStyle(root).getPropertyValue("font-size"));
if(finalFontSize !== fontSize) {
root.style.fontSize = fontSize * fontSize / finalFontSize + "px";
}
複製代碼
getComputedStyle Window.getComputedStyle()方法返回一個對象,該對象在應用活動樣式表並解析這些值可能包含的任何基本計算後報告元素的全部CSS屬性的值。 私有的CSS屬性值能夠經過對象提供的API或經過簡單地使用CSS屬性名稱進行索引來訪問。
getPropertyValue 此 CSSStyleDeclaration.getPropertyValue() 接口會返回一個 DOMString ,這個返回值將會包含預請求的CSS屬性信息。
例如 ios
scale=0.5
innerWidth=750
device-width=375
innerWidth * scale = (device-width = layout-viewport-width)
複製代碼
處理過程以下:
;(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(){
// 動態設置的縮放大小會影響佈局視口的尺寸, 設備物理像素大小
// 設備邏輯像素 device-width = 設備物理像素 /(devicePixelRatio * scale)
var width = docEl.getBoundingClientRect().width; // iphone6 => 750
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'] = {}));
複製代碼
meta 標籤固定爲
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no」> 複製代碼
代碼邏輯爲: 取 width 分爲 10rem, font-size = width / 10
注: 手機淘寶首頁 2019-01-16, 也是這個方案, 可是是 width = 3.75rem, font-size = 100px, 估計是爲了方便除
代碼, 添加部分註釋
(function flexible (window, document) {
var docEl = document.documentElement
var dpr = window.devicePixelRatio || 1
// adjust body font size
function setBodyFontSize () {
if (document.body) {
document.body.style.fontSize = (12 * dpr) + 'px'
}
else {
document.addEventListener('DOMContentLoaded', setBodyFontSize)
}
}
setBodyFontSize();
// set 1rem = viewWidth / 3.6, 當設計稿360px, 更好計算
function setRemUnit () {
// var rem = docEl.clientWidth / 10 // 原始代碼
var rem = docEl.clientWidth / 3.6 // 修改後代碼
docEl.style.fontSize = rem + 'px'
}
setRemUnit()
// reset rem unit on page resize
window.addEventListener('resize', setRemUnit)
window.addEventListener('pageshow', function (e) {
// 參考: https://juejin.im/post/5c807802f265da2de33f4e1f
// 頁面從瀏覽器的緩存中讀取該屬性返回 ture, 優化方案 window.performance.navigation.type
// if (e.persisted) { // 原始代碼
// setRemUnit()
// }
if (e.persisted || (window.performance && window.performance.navigation.type === 2)) { // 修改後代碼
setRemUnit()
}
})
// detect 0.5px supports 檢測是否支持0.5px, 用於1px問題
if (dpr >= 2) {
var fakeBody = document.createElement('body')
var testElement = document.createElement('div')
testElement.style.border = '.5px solid transparent'
fakeBody.appendChild(testElement)
docEl.appendChild(fakeBody)
if (testElement.offsetHeight === 1) {
docEl.classList.add('hairlines')
}
docEl.removeChild(fakeBody)
}
}(window, document))
複製代碼
實現:
<meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no" />
設計圖尺寸:設計圖元素尺寸 = 100vw:Y // Y 表示css樣式中設計圖某元素的大小, 單位vw
複製代碼
缺點:
優勢:
實現: 如下實現把屏幕平均分紅10份,即屏幕寬10rem, 以rem與vw關係: rem:vw = 10:1 或者 1rem = 10vw爲依據 100vw === 10 rem === document.documentElement
前提: <meta name="viewport" content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, user-scalable=no" />
// $vw_design: 設計圖尺寸
// $vw_fontsize: 設計圖尺寸 / 10 假設把設計圖分爲10份, 每份的大小(設計圖的1rem), 並以此爲基數
html {
font-size: ($vw_fontsize / $vw_design) * 100vw; // 直接寫 10vw
// 同時,經過Media Queries 限制根元素最大最小值
@media screen and (max-width: 320px) {
font-size: 32px;
}
@media screen and (min-width: 540px) {
font-size: 54px;
}
}
複製代碼
// $basesize: 設計圖元素尺寸
@function rem($basesize) {
@return ($basesize / $vw_fontsize) * 1rem;
}
//簡化
@function rem($basesize) {
@return ($basesize / $vw_design) * 10rem;
}
複製代碼
計算原理:
假設設計圖某元素所佔視口大小爲 X, 單位爲: vw
設計圖某元素尺寸 / 設計圖尺寸 = (X)vw / 100vw
=> (X)vw = (設計圖元素尺寸 / 設計圖尺寸) * 100vw
// 轉化爲以rem單位的數值Y
因爲 (Y)rem = (X)vw / 10 (平局分紅10份,vw:rem = 10:1)
=> (Y)rem = (設計圖元素尺寸 / 設計圖尺寸) * 10vw
複製代碼
觀察最後推論,因而也能夠按照如下理解:
$design_width: 750
$divideNum: 10 //屏幕平均分紅份數
@function px2rem($design_dom) {
@return #{$design_dom/$design_width * $divideNum}rem
}
html {
font-size: (100 / $divideNum)vw;
//同時,經過Media Queries 限制根元素最大最小值
@media screen and (max-width: 320 ) {
font-size: 32;
}
@media screen and (min-width: 640 ) {
font-size: 64;
}
}
複製代碼
js 動態設置 html {font-size: (clientWidth / 屏幕平均分紅份數)px}
$design_width: 750
@function px2rem($dom) {
@return #{$dom/$design_width * 屏幕分紅份數}rem
}
複製代碼
防止使用rem後,未設置font-size的元素繼承使用根元素的font-zsize,重置body的font-size 爲默認值(通常16px)或使用媒體查詢字體響應式(320, 480, 640移動端尺寸)
body {
font-size: 16px;
}
複製代碼
響應式佈局方案
縮放佈局 | 用戶體驗 | 兼容性 | 依賴js | 支持超大屏幕 | 須要修正字體 |
---|---|---|---|---|---|
rem+media-query | 可 | IOS4.1 AN2.1 | × | √ | √ |
rem+js | 良 | IOS4.1 AN2.1 | √ | √ | √ |
rem+vw | 優 | IOS6.1 AN4.4 | × | √ | √ |
vw | 優 | IOS6.1 AN4.4 | × | × | × |
總結:
.hairlines li{
height: 50px;
line-height: 50px;
border:none;
text-align: center;
position: relative;
margin-top: 10px;
}
.hairlines li:after{
content: '';
position: absolute;
left: 0;
top: 0;
border: 1px solid #cccccc;
// border-radius: 26px;
width: 200%;
height: 200%;
-webkit-transform: scale(0.5);
transform: scale(0.5);
-webkit-transform-origin: left top;
transform-origin: left top;
}
複製代碼