半個像素的情懷

移動端的興起才導致前端開發行業的大熱,但是也帶來了不少新的挑戰。

最近我們新做了一個微商城的移動主題,主打移動體驗,設計稿借鑒了一些知名的移動商城。但是最後做出來的時候,發現一個很奇怪的地方:我們的成品網站不如其他商城看起來清新。此話怎麼說呢,我們當時對比的是基於有贊的一個賣書的店鋪,發現他們的商品陳列,中間的分割線看起來更舒服一些,尤其是在 iPhone 上。於是設計找我撕逼,說你這根線給我再細一些,我果斷反擊說這已經是1像素的最小的線了。設計不服,於是拿出有贊這個店鋪跟我做的成品對比,不比不知道,果然,同樣是 iPhone6,人家的看起來確實更清新,更細的樣子。我當時還以為是視覺的原因,但是都是同樣的配色,理論上不應該有如此大的視覺差。於是就有了關於這篇文章的探索。

普及意義和基礎

在具體說明技術的實現,我覺得有必要說一下我對這個技術的看法和普及的意義,其實這個技術就基於一個目標——畫一根 只有 0.5px 粗的線

自然,在我們的認知中,網頁設計的最小單元就是一個像素了,實現一條不到 1px 粗的線聽起來有點不可思議。但實際上,隨著 LED 顯示技術的進步和 GPU 晶元的性能日益增進,我們在常見設備中已經具備這個條件去應用了。

首先,了解一下 Retina 屏幕的發展史

2010年6月8日,蘋果公司正式發布了 iPhone 4,這意味著智能移動設備進入了一個全新的時代。對於前端開發而言,iPhone 4 的發布帶來的一個新挑戰,是其被稱為 Retina 的屏幕技術 —— 640x960個像素,塞進了 3.5 英寸的顯示屏幕,每英寸的面積里有 327 像素。以此帶來的是異常清晰的顯示體驗,由於正常使用完全看不到像素點的存在,因此又稱之為「視網膜屏幕」

題外話:其實我們看高清電視時,如果客廳夠大,距離足夠遠,也是看不到像素點的……所謂的三米變高清,五米抗鋸齒。因此在某種使用條件下,解析度達到一定的標準就足夠細膩了,不是說越高越好(因為顯卡性能很可能跟不上)

幾年過去了,我們現在市面上的手機,基本最低解析度也達到了 720P 的標準,更多的是 1080P 和 iPhone 系列的超高清屏幕。

那麼蘋果是如何解決如此細膩的物理像素帶來的網頁設計的問題呢?

這裡引入了兩個新概念:一個叫 縮放比例(DPR,Device Pixel Ratio),另一個叫 CSS像素,先看下錶:

可以很明確的看到,物理像素的高和寬分別除以縮放比例,就得到了 CSS 像素,下圖是更清晰的說明(圖源自網路):

不過這裡有一個小小的例外,如果你注意到的話:iPhone 6 Plus 的物理像素除以 DPR 得到的是 640x360,跟表中的並不一致。這裡有一個小彩蛋,其實除了 Plus,iPhone 其他均使用 326 的DPI,縮放比例是 2.0。因此同一張圖片在 iPhone 的瀏覽器打開,在 iPhone4/5/6 中大小是一樣的(這裡指實際大小,即拿尺子直接在屏幕量,長度都是 X 厘米),為保持這個特性,基於 iPhone 6 Plus DPI 是 401,理論上蘋果的縮放比例應該是 401 / 326 x 2 = 2.46,但是非整數會給開發和設計帶來很大的不方便,因此考慮的是向下/向上取整,考慮屏幕尺寸最後取得縮放比例為 3.0,因此進行來適當的縮放,也就是虛擬像素為 2208 × 1242(也就是截圖得到的尺寸),再根據上表就得到正確的計算啦。

其實很多安卓設備即便使用 2K 屏幕,但是如果屏幕有一部分用來做虛擬按鍵,那麼實際上可用物理像素是會比屏幕標註的要「矮」一些的

這樣一來就知道在超高清屏幕中,實現半個 CSS 像素,就是只顯示物理像素的一個像素,是有其實際的場景的。那麼現在可以著手怎麼使用代碼實現了。

基於 viewport

前面有提到,CSS 像素其實是一種虛擬的像素,那麼基於 `viewport` 的技術實現就像是把這個虛擬層用代碼定義了出來,參考以下 HTML 代碼:

<meta name="viewport" content="initial-scale=1.0, maximum-scale=1.0, user-scalable=no">

這個 meta 標籤其實非常常見,我們一般都會加上用來指定在移動瀏覽器不要進行縮放。但如果你指定了特殊但縮放比例,會出現什麼情況呢?如:

<meta name="viewport" content="initial-scale=2.0, maximum-scale=2.0, user-scalable=no">

打開瀏覽器可以看到,平時正常但頁面,現在全部元素都比正常要大一倍,那麼反過來呢?道理很明白了,就是讓瀏覽器指定這個虛擬視窗是要縮放的,一個像素粗的元素你只顯示成半個像素就好,再配合 CSS 像素的相關約束,於是……

實際上,淘寶網觸屏版 的 <head> 標籤中就指定了這個

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

於是淘寶就做到了單線條的顯示非常細膩的效果,但是這裡要說以下,因為是整體的縮放,所以手淘的在 iPhone 5 頁面寬度用 CSS 描述是 `640px` 完全跟物理像素對應了起來,高!

這裡對開發設計的要求是,所以的素材和 CSS 尺寸都是所以瀏覽設備的 DPR 倍,這就要求一開始要考慮到不同縮放比例的設備兼容性,同時開發時就必須採用百分比布局,否則不同設備兼容性會很糟糕

基於 background-image

這裡主要是用到了漸變背景,我們知道,漸變背景是可以指定漸變顏色從哪裡開始變化的,那麼考慮以下代碼:

<style> .half-pixel { height: 10px; background-image: linear-gradient(to bottom, red 50%, transparent 100%); background-size: 100% 1px; background-repeat: no-repeat; } .hole-pixel { border-top: 1px solid red; }</style><div class="half-pixel"></div><div class="hole-pixel"></div>

我們這裡指定背景的高度為 1px,同時在漸變中指定前半部分為紅色,後半部分為透明,那麼,由於總共高度就只有一個像素,於是漸變從開始為紅色,到半個像素的時候開始變為透明。於是最終顯示的效果就是看起來好像只有半個像素的紅色。

對比第二條使用邊框實現的紅線,可以發現確實第一條線確實要更細一些。在如此細膩的屏幕中,肉眼是很難具體看到漸變的具體轉換的,因此即便不是非常完美的半個像素,卻達到了欺騙眼球的效果

這個方法有其局限性,首先要生成一個完整的元素來使用(雖然通常使用偽元素保證 HTML 的乾淨),其次漸變背景的兼容性基本只只有現代瀏覽器支持,最後是,畢竟還是有漸變的

基於 border-image

我們知道,高級瀏覽器是支持自定義邊框背景的,這邊跟前面使用背景圖片一樣,同樣利用來背景圖片可以被拉伸的特性,考慮以下代碼:

<style> .half-pixel { border-top: 1px solid transparent; border-image-source: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAYAAAAGAQMAAADaAn0LAAAABlBMVEUAAAC/v79T5hyIAAAAAXRSTlMAQObYZgAAAA5JREFUCNdj+MPQAoV/ABg8BAlEsqabAAAAAElFTkSuQmCC); border-image-slice: 2; border-image-repeat: stretch; }</style><div class="half-pixel"></div>

其中這裡我們的背景圖片應該是這樣子的:

同樣,由於邊框高度設定為一個 CSS像素,而背景圖只有一半是可以被看到的,因此在拉伸後看起來就像是只有半個像素。有贊商城就是採用這種方式做的。

實際上這張圖就來自有贊商城,我們可以探討一下為什麼這個圖片做成 4x4 的可見部分,四周還圍繞一個透明像素,其他尺寸不行么?

我個人的分析是:第一點. 做成總寬度為 6 個像素,是考慮到縮放比例不管是 2 還是 3 都可以公用這個代碼(公倍數);第二點,四周圍繞一圈透明度,是希望可以邊框不管是水平方向還是垂直方向都可以使用同樣的代碼(圖片對稱);第三點,最巧妙的是,設備想要展示的其實是虛線,但我們還是認為完美地模仿來半個像素,看下圖:

其實我們看到的是多個紅色點排列而成的虛線,但是由於單個點紅色部分的面積實際上約一半(4 x 4 / 6 x 6 = 0.45),因此最後排成這樣的的時候視覺上剛剛感覺是半個像素!真是美妙的視覺欺騙呀

基於 transform

其實前面我們都是通過背景圖的拉伸實現的,那麼其實考慮以下 CSS3 本身也有拉伸的功能,考慮以下代碼:

<style>.half-pixel:after { content: ; display: block; border-top: 1px solid #f00; transform: scaleY(0.5);}</style><div class="half-pixel"></div>

沒錯,前面代碼通過定義一個偽元素並使用 transform: scaleY(0.5) 來拉伸達到讓 1px 瘦身的目的,手機京東商城 就是使用來這個方法的。

實際上這個方法我個人認為是最合適的,因為不用繁瑣的背景圖方式,理論上可以縮小放大任意的倍數做到各種粗細的視覺效果,付出的代價僅僅是多一個偽元素,何樂而不為呢?

結語

其實經過測試可以知道,半個像素並不只是在 Retina 級別的屏幕上才能看得到,有些瀏覽器為了兼容 4K 顯示器,順便在普通 1080P 筆記本顯示器這種縮放比例為 1.0 的顯示器中也會進行顯示優化,顯示效果是邊緣不夠清晰,看起來比正常一個像素的要淡一些。不過前面提到,我們可以使用 CSS 媒體查詢來知道當前設備的縮放比例,從而寫不同的 CSS 規則來進行適配。

加入以下查詢活得半個像素體驗 @media only screen and (-webkit-min-device-pixel-ratio: 1.5)

因此 Retina 技術不僅給消費者帶來新的視覺體驗,也給 Web 開發領域帶來新的挑戰和創意,友好速搭的設計師們,之前的移動設計稿標準是 640px 橫向寬度,前端開發也會考慮到 Retina 屏幕中使用 CSS 媒體查詢適配高清大圖。但隨著 iPhone 6 Plus 系列的推出和 1080P 手機的普及,我們開發的時候已經是按照 DPR 3.0 的標準在進行了,設計稿寬度指導尺寸上升到了 960px(雖然前端開發還沒有上升到 `@3X`,不過這在未來是必然會發生的事情)

任何技術的出現都是因為原有技術的局限性,在可預見的未來,超高清顯示器的普及是必然的事情,電視機也從之前的 720P 進化到現在基本上都在熱賣 4K 屏,因此是不是有那麼一個屬性,可以直接控制 CSS像素與物理像素的對應關係呢?

小彩蛋

其實普通的顯示器也可以有半個像素的實現,不知道大家有沒有注意到,即使用 MacBook Air 這樣的渣屏看手機京東/有贊等,也會覺得某些邊框線條更柔和——這就是半個像素的神奇之處。我十分好奇地留意到這個點:普通屏幕的 DPR 就是 1.0,從物理上講沒辦法有半個像素的實現才對!不過經過思考後我給出了我的答案:

瀏覽器可以只渲染其中一半的像素來在低解析度顯示器模擬半個像素,但這樣很可能會穿幫(變成虛線!),於是折中的方法是,將顯示顏色半透明化,即物理像素的只顯示一半的色彩飽和度。如果你表示懷疑,可以拿出你閑置多年的單反鏡頭做個放大鏡看一看吧,我可是用我的小米渣5對焦了好久。

以上。

推薦閱讀:

What are the Differences between HTML5 and HTML 5.1?
如果html傳輸全部使用json, browser 將會快很多?
移動端視網膜(Retina)屏幕下如何解決網頁中1px顯示問題?
HTML5 真能代替 Flash 嗎?
Bilibili聖誕遊戲剖析-用pixi.js實現鬼畜音游

TAG:前端开发 | CSS | HTML5 |