【學習筆記】移動端 1px 的解決方案

背景

在高清屏(Retina)下移動端的 1px 會很粗, 以下面圖一是假的 1 像素,圖二是真的 1 像素 image.pngjavascript

image.png

產生緣由

  • 主要是跟 DPR(device Pixel Ratio) 設備像素比有關,它是默認縮放爲 100% 的狀況下設備像素和 CSS 像素的比值 window.devicePixelRatio = 物理像素 / CSS像素
  • 目前主流的屏幕 DPR=2 (iPhone 8)、3(iPhone 8 Plus),拿 2 倍屏來講,設備的物理像素要實現 1 像素,而 DPR=2,因此 CSS 像素只能是 0.5;通常設計稿是按照 750 來設計的,它上面的 1px 是以 750 來參照的,而寫 CSS 樣式是以設備 375 爲參照的,因此應該寫 0.5px 就行了,iOS 8+ 系統支持,安卓系統不支持

解決方案

WWDC 對 iOS 系統給出的方案

0.5px 的邊框css

border: 0.5px solid #E5E5E5;
複製代碼

問題是 Retina 屏的瀏覽器可能不認識 0.5px 的邊框,將會把它解釋成 0px 沒有邊框,包括 iOS 7 和 以前版本、OS X Mavericks 及之前版本,Android 設備在 3 倍屏下不是 0.3333px ,在 Chrome 上模擬 iPhone 8Plus,發現小於 0.46px 時是顯示不出來html

總結
1)優勢:簡單,沒有反作用
2)缺點:支持 iOS 8+,不支持安卓,後期安卓 follow 就行了java

解決方案
經過 JS 檢測瀏覽器可否處理 0.5px 的邊框,若能夠則給 元素添加個class,看起來是個解決方案,但是要兼容安卓設備,和 iOS 8 如下設備怎麼辦?這個思路行不通git

// 腳本應該放在<body>內, 若在<head>裏面運行,需包裝 $(document).ready(function() {})
if (window.devicePixelRatio && devicePixelRatio >= 2) {
  var testElem = document.createElement('div');
  testElem.style.border = '.5px solid transparent';
  document.body.appendChild(testElem);
  if (testElem.offsetHeight == 1)
  {
    document.querySelector('html').classList.add('hairlines');
  }
  document.body.removeChild(testElem);
}
// 而後,極細的邊框樣式就容易了
div {
  border: 1px solid #bbb;
}
.hairlines div {
  border-width: 0.5px;
}
複製代碼

使用邊框圖片

圖片的顏色就是此後 border 的顏色github

border: 1px solid transparent;
border-image: url('./../../image/96.jpg') 2 repeat;
複製代碼

總結
1)優勢:沒有反作用
2)缺點:border 顏色變了就得從新要改圖片;圓角會比較模糊web

使用 box-shadow 實現

前面兩個值 x,y 主要控制顯示哪條邊,後面兩值控制的是陰影半徑、擴展半徑瀏覽器

box-shadow: 0  -1px 1px -1px #e5e5e5,   //上邊線
            1px  0  1px -1px #e5e5e5,   //右邊線
            0  1px  1px -1px #e5e5e5,   //下邊線
            -1px 0  1px -1px #e5e5e5;   //左邊線
複製代碼

總結
1)優勢:使用簡單,圓角也能夠實現
2)缺點:模擬的實現方法,仔細看可看出來這是陰影不是邊框markdown

淘寶M站是經過 viewport + rem 實現的

在 devicePixelRatio = 2 時,輸出 viewportapp

<meta name="viewport" content="initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no">
複製代碼

在 devicePixelRatio = 3 時,輸出 viewport

<meta name="viewport" content="initial-scale=0.3333333333333333, maximum-scale=0.3333333333333333, minimum-scale=0.3333333333333333, user-scalable=no">
複製代碼

同時經過設置對應 viewport 的 rem 基準值,這種方式就能夠像之前同樣輕鬆愉快的寫 1px

總結
1)優勢:全機型兼容,直接寫 1px 更方便
2)缺點:適用於新的項目,老項目可能改動大

<html>
  <head> <title>1px question</title> <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"> <meta name="viewport" id="WebViewport" content="initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no"> <style> html { font-size: 1px; } * { padding: 0; margin: 0; } .top_b { border-bottom: 1px solid #E5E5E5; } .a,.b { box-sizing: border-box; margin-top: 1rem; padding: 1rem; font-size: 1.4rem; } .a { width: 100%; } .b { background: #f5f5f5; width: 100%; } </style> <script> var viewport = document.querySelector("meta[name=viewport]"); //下面是根據設備像素設置viewport if (window.devicePixelRatio == 1) { viewport.setAttribute('content', 'width=device-width,initial-scale=1, maximum-scale=1, minimum-scale=1, user-scalable=no'); } if (window.devicePixelRatio == 2) { viewport.setAttribute('content', 'width=device-width,initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no'); } if (window.devicePixelRatio == 3) { viewport.setAttribute('content', 'width=device-width,initial-scale=0.3333333333333333, maximum-scale=0.3333333333333333, minimum-scale=0.3333333333333333, user-scalable=no'); } var docEl = document.documentElement; var fontsize = 32* (docEl.clientWidth / 750) + 'px'; docEl.style.fontSize = fontsize; </script> </head> <body> <div class="top_b a">下面的底邊寬度是虛擬1像素的</div> <div class="b">上面的邊框寬度是虛擬1像素的</div> </body> </html> 複製代碼

image.png

僞類 + transform

原理是把原先元素的 border 去掉,而後利用 :before 或 :after 重作 border,並將 transform 的 scale 縮小一半,原先的元素相對定位,新作的 border 絕對定位

單條 border

.hairlines li {
    position: relative;
    border:none;
}
.hairlines li:after  {
    content: '';
    position: absolute;
    left: 0;
    background: #000;
    width: 100%;
    height: 1px;
    -webkit-transform: scaleY(0.5);
            transform: scaleY(0.5);
    -webkit-transform-origin: 0 0;
            transform-origin: 0 0;
}

// 將僞元素設置絕對定位而且和父元素的左上角對齊,將 width 設置 100%,height 設置爲 1px
// 而後進行在Y方向縮小0.5倍
.setOnePx {
  position: relative;
  &::after {
    position: absolute;
    content: '';
    background-color: #e5e5e5;
    display: block;
    width: 100%;
    height: 1px; /*no*/
    transform: scale(1, 0.5);
    top: 0;
    left: 0;
  }
}
複製代碼

四條 border

.hairlines li{
    position: relative;
    margin-bottom: 20px;
    border:none;
}
.hairlines li: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;
}

// 一樣爲僞元素設置絕對定位而且和父元素左上角對其
// 將僞元素的長和寬先放大 2 倍
// 而後再設置一個邊框,以左上角爲中心,縮放到原來的 0.5 倍
.setBorderAll {
     position: relative;
     &:after {
         content:" ";
         position:absolute;
         top: 0;
         left: 0;
         width: 200%;
         height: 200%;
         transform: scale(0.5);
         transform-origin: left top;
         box-sizing: border-box;
         border: 1px solid #E5E5E5;
         border-radius: 4px;
    }
}

// 樣式使用的時候,也要結合 JS 代碼,判斷是否 Retina 屏
if(window.devicePixelRatio && devicePixelRatio >= 2) {
    document.querySelector('ul').className = 'hairlines';
}
複製代碼

總結
1)優勢:全機型兼容,實現了真正的 1px,並且能夠圓角
2)缺點:暫用了 after 僞元素,可能影響清除浮動;<td> 用不了
3)能夠參照這個 border 生成器 寫一個 scss 的 1px 邊框生成器,該生成器考慮了三種狀況:單邊邊框、多邊邊框、邊框的圓角

踩坑

使用僞元素方法,僞類裏面再設置僞元素,能夠選擇到嗎? 看圖,須要修改中間的豎線 image.png 代碼,然而展現出來的樣式是下面這樣的

&:nth-child(2) {
    // border-color: #e5e5e5 !important;
    border: 0;
    position: relative;
    &:after {
        position: absolute;
        content: '';
        background-color: #e5e5e5;
        display: block;
        width: 100%;
        height: 1px; /*no*/
        transform: scale(1, 0.5);
        top: 0;
    }
}
複製代碼

image.png

爲何中間的豎線沒有了?在僞類下面再寫僞元素after是能夠的,這裏的緣由是 width 和 height 設置有問題,對於豎線應該是 width = 1px,height = 100%,而後再縮放 X 方向 0.5 倍,這樣豎線就出來了;一樣設置水平線應該是 width = 100%,height = 1px,而後再縮放 Y 方向 0.5 倍

&:nth-child(2) {
    position: relative;
    &:after {
      position: absolute;
      content: '';
      top: 0;
      left: 0;
      width: 1px;
      height: 100%;
     transform: scaleX(0.5);
     background: #e5e5e5;
     transform-origin: 0 0;
   }
}
// 或
 &:nth-child(2) {
    position: relative;
    &:after {
      content:" ";
      position:absolute;
      top: 0;
      left: 0;
      width: 200%;
      height: 200%;
      transform: scale(0.5);
      transform-origin: left top;
      box-sizing: border-box;
      border-left: 1px solid #E5E5E5;
    }
}
複製代碼
相關文章
相關標籤/搜索