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