談一談移動端1px的問題

原由

最近一個月都在準備校招,因此沒什麼時間寫博客。今天想寫的問題來自於網易一面的時候,面試官問我如何在移動端的頁面上畫一條1px的線。這個問題我模糊地記得以前看過相關文章,可是我清楚地記得當時本身腦子一片空白。是的,一面掛了,可是這個問題一直在我回來的路上不斷想起,因此今天我就要解決這個問題,來看看有什麼解決方案吧~javascript

動態改變viewport的縮放

這是淘寶的flexible提出的解決方案,其核心就是根據window.devicePixelRatio(dpr)的值動態改變viewport的縮放,核心代碼以下(有刪減):css

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;
}

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);
    }
}

這個方案只對iOS的Retina屏幕作了處理,而沒有管安卓的Retina屏幕,緣由能夠看《再談Retina下1px的解決方案》這篇文章。使用了flexible以後直接寫1px就能實現效果,可是最新的2.0好像放棄了這種縮放的方案,對於1px的處理則變成了border-image或者background-image,詳細的能夠看《再聊移動端頁面的適配》。html

這裏再簡單談一下這種viewport縮放的原理:前端

首先一開始寫移動端的時候,我是直接加一個meta標籤 <meta name=viewport content="width=device-width,minimum-scale=1,maximum-scale=1,user-scalable=no">,這個meta標籤使得頁面寬度等於設備寬度,頁面的縮放默認爲1,且用戶不能縮放,後來看到一篇文章講viewport計算是這樣的:java

viewport的默認寬度是980px;設置了initial-scale則寬度是device-width/initial-scale;設置了width則寬度等於width的值;同時設置了initial-scale和width則寬度取二者中較大的一個。android

上面的結論我在PC端谷歌瀏覽器的設備模擬器裏證明了有效,可是安卓和iOS真機並無試過。git

transform: scale(0.5)

這個方案也是WeUI正在用的,核心思想是使用transform的scale來總體縮放,若是你想畫一條1px的線,就能夠直接用github

div {
    height: 1px;
      background: #000;
      transform: scaleY(0.5);
      transform-origin: 0 0;
}

理論上在dpr爲2時就是scaleY(0.5),在dpr爲3時就是scaleY(0.333),可是我注意到WeUI並無針對其餘dpr的作特殊處理,多是由於在iPhone6(dpr=2)和iPhone6 Plus(dpr=3)中看起來差異不大吧。web

若是你想給一個元素加一個1px的邊框能夠利用到僞元素,在這個方案下邊框加圓角也很容易實現,具體代碼以下:面試

div:after {
  content: " ";
  width: 78px;
  height: 38px;
  border-radius: 4px;
  border: 1px solid #000;
  transform: scale(0.5, 0.5);
  transform-origin: 0 0;
  position: absolute;
}

其餘方案

border-image

這種是AlloyTeam的《CSS 實現相似原生效果的 1px 邊框》這篇文章上看到的,代碼寫起來挺簡單,可是要本身製做圖片,並且圓角也很差弄,若是改了顏色就要對圖片處理,因此不是很好的方案。

box-shadow

這個顏色很差弄,因此效果也不是很好。

div {
  border: none;
  box-shadow: 0 1px 1px -1px rgba(0, 0, 0, 0.5);
}

0.5px解決方案

div {
    border: 1px solid #000;
}

@media (-webkit-min-device-pixel-ratio: 2) {
  div {
    border: .5px solid #000;
  }
}

安卓和iOS7以前版本碰到0.5px直接就解析成0px了,可是這一特性也是能夠利用的,在最新的flexible中就有對0.5px進行判斷的代碼:

// detect 0.5px supports
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)
}

小結

這篇文章從國慶開始斷斷續續地寫,到最後一天總算是寫完了,這段時間整我的心態經歷了一些變化,主要表如今對待技術比之前更加踏實了,不管如何但願本身能堅持寫博客,堅持學習前端,相信總能找到一個理想的工做的,加油!

相關文章
相關標籤/搜索