在 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 等前處理器,那你可以根據你的實際情況去尋找一種預處理方案,只要達到推導公式的效果就可以了。
什麼?沒有合適的方案,那你是時候充充電了
期待您的關注~~