移動端1px誤差的原因以及解決方案
移動端1px問題在面試和工作中會經常遇到,系統地理解它是一個優秀前端的必修課!
為什麼移動端css裡面寫了1px, 實際看起來比1px粗. 其實原因很好理解:這2個’px’的含義是不一樣的. 移動端html的header總會有一句
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
這句話定義了本頁面的viewport的寬度為裝置寬度,初始縮放值和最大縮放值都為1,並禁止了使用者縮放。
手機存在一個能完美適配的理想viewport, 解析度相差很大的手機的理想viewport的寬度可能是一樣的, 這樣做的目的是為了保證同樣的css在不同螢幕下的顯示效果是一致的, viewport的好處就在於一套css可以適配多個機型
在window物件中有一個devicePixelRatio屬性,他可以反應css中的畫素與裝置的畫素比。然而1px在不同的移動裝置上都等於這個移動裝置的1px,這是因為不同的移動裝置有不同的畫素密度。有關這個屬性,它的官方的定義為:裝置物理畫素和裝置獨立畫素的比例,也就是
devicePixelRatio = 物理畫素 / 獨立畫素
1px變粗的原因: viewport的設定和螢幕物理解析度是按比例而不是相同的. 移動端window物件有個devicePixelRatio屬性, 它表示裝置物理畫素和css畫素的比例, 在retina屏的iphone手機上, 這個值為2或3, css裡寫的1px長度對映到物理畫素上就有2px或3px那麼長。
解決方案
1.rem解決:
////根據螢幕大小及dpi調整縮放和大小
(function () {
var scale = 1.0;
var ratio = 1;
if (window.devicePixelRatio >= 2) {
scale *= 0.5;
ratio *= 2;
}
var text = '<meta name="viewport" content="initial-scale=' + scale + ', maximum-scale=' + scale + ',' + ' minimum-scale=' + scale + ', width=device-width,' + ' user-scalable=no" />
';
document.write(text);
document.documentElement.style.fontSize = 50 * ratio + "px";
})();
2.flexible.js
這是淘寶移動端採取的方案, github的地址:https://github.com/amfe/lib-flexible. 前面已經說過1px變粗的原因就在於一刀切的設定viewport寬度, 如果能把viewport寬度設定為實際的裝置物理寬度, css裡的1px不就等於實際1px長了麼. flexible.js就是這樣乾的。
<meta name=”viewport”>
裡面的scale值指的是對ideal viewport的縮放, flexible.js檢測到IOS機型, 會算出scale = 1/devicePixelRatio, 然後設定viewport
metaEl = doc.createElement('meta');
metaEl.setAttribute('name', 'viewport');
metaEl.setAttribute('content', 'initial-scale=' + scale + ', maximum-scale=' + scale + ', minimum-scale=' + scale + ', user-scalable=no');
3.偽類+transform實現
對於解決1px邊框問題,我個人覺得最完美的解決辦法還是偽類+transform比較好。
原理:是把原先元素的 border 去掉,然後利用 :before 或者 :after 重做 border ,並 transform 的 scale 縮小一半,原先的元素相對定位,新做的 border 絕對定位。
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>test</title>
</head>
<body>
<div class="box-shadow-1px scale">box-shadow-1px</div>
<style>
.box-shadow-1px {
height: 200px;
width: 200px;
text-align: center;
}
.scale{
position: relative;
margin-bottom: 20px;
border:none;
}
.scale:after{
content: '';
position: absolute;
top: 0;
left: 0;
border: 1px solid #000;
-webkit-box-sizing: border-box;
box-sizing: border-box;
width: 200%;
height: 200%;
-webkit-transform: scale(0.5);
transform: scale(0.5);
-webkit-transform-origin: left top;
transform-origin: left top;
}
</style>
<script>
console.log(typeof ('a'+1))
</script>
</body>
</html>