怎麼使用 SVG 作為一個圖像佔位符
我對怎麼去讓 web 性能更優化和圖像載入的更快充滿了熱情。在這些感興趣的領域中的其中一項研究就是佔位符:當圖像還沒有被載入的時候應該去展示些什麼?
在前些天,我偶然發現了使用 SVG 的一些載入技術,我將在這篇文章中談論它。
在這篇文章中我們將涉及如下的主題:
- 不同的佔位符類型的概述
- 基於 SVG 的佔位符(邊緣、形狀和輪廓)
- 自動化處理
不同的佔位符類型的概述
之前 我寫過一篇關於圖像佔位符和 延遲載入 的文章以及 關於它的討論。當進行一個圖像的延遲載入時,一個很好的辦法是提供一個東西作為佔位符,因為它可能會很大程度上影響用戶的感知體驗。之前我提供了幾個選擇:
在圖像被載入之前,有幾種辦法去填充圖像區域:
- 在圖像區域保持空白:在一個響應式設計的環境中,這種方式防止了內容的跳躍。從用戶體驗的角度來看,那些布局的改變是非常差的作法。但是,它是為了性能的考慮,否則,每次為了獲取圖像尺寸,瀏覽器就要被迫進行布局重新計算,以便為它留下空間。
- 佔位符:在圖像那裡顯示一個用戶配置的圖像。我們可以在背景上顯示一個輪廓。它一直顯示直到實際的圖像被載入完成,它也被用於當請求失敗或者當用戶根本沒有設置頭像圖像的情況下。這些圖像一般都是矢量圖,並且由於尺寸非常小,可以作為內聯圖片。
- 單一顏色:從圖像中獲取顏色,並將其作為佔位符的背景顏色。這可能是圖像的主要顏色、最具活力的顏色 … 這個想法是基於你正在載入的圖像,並且它將有助於在沒有圖像和圖像載入完成之間進行平滑過渡。
- 模糊的圖像:也被稱為模糊技術。你提供一個極小版本的圖像,然後再去過渡到完整的圖像。最初顯示的圖像的像素和尺寸是極小的。為去除 細節 ,該圖像會被放大並模糊化。我在前面寫的 Medium 是怎麼做的漸進載入圖像、使用 WebP 去創建極小的預覽圖像、和漸進載入圖像的更多示例 中討論過這方面的內容。
此外還有其它的更多的變種,許多聰明的人也開發了其它的創建佔位符的技術。
其中一個就是用梯度圖代替單一的顏色。梯度圖可以創建一個更精確的最終圖像的預覽,它整體上非常小(提升了有效載荷)。
使用梯度圖作為背景。這是來自 Gradify 的截屏,它現在已經不在線了,代碼 在 GitHub。
另外一種技術是使用基於 SVG 的技術,它在最近的實驗和研究中取得到了一些進展。
基於 SVG 的佔位符
我們知道 SVG 是完美的矢量圖像。而在大多數情況下我們是希望載入一個點陣圖,所以,問題是怎麼去矢量化一個圖像。其中一些方法是使用邊緣、形狀和輪廓。
邊緣
在 前面的文章中,我解釋了怎麼去找出一個圖像的邊緣並創建一個動畫。我最初的目標是去嘗試繪製區域,矢量化該圖像,但是我並不知道該怎麼去做到。我意識到使用邊緣也可能是一種創新,我決定去讓它們動起來,創建一個 「繪製」 的效果。
在以前,很少使用和支持 SVG。一段時間以後,我們開始用它去作為一個某些圖標的傳統點陣圖的替代品……
形狀
SVG 也可以用於根據圖像繪製區域而不是邊緣/邊界。用這種方法,我們可以矢量化一個點陣圖來創建一個佔位符。
在以前,我嘗試去用三角形做類似的事情。你可以在 CSSConf 和 Render Conf 上我的演講中看到它。
上面的 codepen 是一個由 245 個三角形組成的基於 SVG 佔位符的概念驗證。生成的三角形是基於 Delaunay triangulation 的,使用了 Possan』s polyserver。正如預期的那樣,使用更多的三角形,文件尺寸就更大。
Primitive 和 SQIP,一個基於 SVG 的 LQIP 技術
Tobias Baldauf 正在致力於另一個使用 SVG 的低質量圖像佔位符技術,它被稱為 SQIP。在深入研究 SQIP 之前,我先簡單介紹一下 Primitive,它是基於 SQIP 的一個庫。
Primitive 是非常吸引人的,我強烈建議你去了解一下。它講解了一個點陣圖怎麼變成由重疊形狀組成的 SVG。它尺寸比較小,適合於直接內聯放置到頁面中。當步驟較少時,在初始的 HTML 載荷中作為佔位符是非常有意義的。
Primitive 基於三角形、長方形、和圓形等形狀生成一個圖像。在每一步中它增加一個新形狀。很多步之後,圖像的結果看起來非常接近原始圖像。如果你輸出的是 SVG,它意味著輸出代碼的尺寸將很大。
為了理解 Primitive 是怎麼工作的,我通過幾個圖像來跑一下它。我用 10 個形狀和 100 個形狀來為這個插畫生成 SVG:
使用 Primitive 處理 ,使用 10 個形狀 、 100 形狀、 原圖。 |
使用 Primitive 處理,使用 10 形狀 、 100 形狀、 原圖 。 |
當在圖像中使用 10 個形狀時,我們基本構畫出了原始圖像。在圖像佔位符這種使用場景里,我們可以使用這種 SVG 作為潛在的佔位符。實際上,使用 10 個形狀的 SVG 代碼已經很小了,大約是 1030 位元組,當通過 SVGO 傳輸時,它將下降到約 640 位元組。
<svg xmlns=」http://www.w3.org/2000/svg" width=」1024" height=」1024"><path fill=」#817c70" d=」M0 0h1024v1024H0z」/><g fill-opacity=」.502"><path fill=」#03020f」 d=」M178 994l580 92L402–62"/><path fill=」#f2e2ba」 d=」M638 894L614 6l472 440"/><path fill=」#fff8be」 d=」M-62 854h300L138–62"/><path fill=」#76c2d9" d=」M410–62L154 530–62 38"/><path fill=」#62b4cf」 d=」M1086–2L498–30l484 508"/><path fill=」#010412" d=」M430–2l196 52–76 356"/><path fill=」#eb7d3f」 d=」M598 594l488–32–308 520"/><path fill=」#080a18" d=」M198 418l32 304 116–448"/><path fill=」#3f201d」 d=」M1086 1062l-344–52 248–148"/><path fill=」#ebd29f」 d=」M630 658l-60–372 516 320"/></g></svg>
正如我們預計的那樣,使用 100 個形狀生成的圖像更大,在 SVGO(之前是 8kB)之後,大小約為 5kB。它們在細節上已經很好了,但是仍然是個很小的載荷。使用多少三角形主要取決於圖像類型和細膩程度(如,對比度、顏色數量、複雜度)。
還可以創建一個類似於 cpeg-dssim 的腳本,去調整所使用的形狀的數量,以滿足 結構相似 的閾值(或者最差情況中的最大數量)。
這些生成的 SVG 也可以用作背景圖像。因為尺寸約束和矢量化,它們在展示 超大題圖 和大型背景圖像時是很好的選擇。
SQIP
用 Tobias 自己的話說:
SQIP 嘗試在這兩個極端之間找到一種平衡:它使用 Primitive 去生成一個 SVG,由幾種簡單的形狀構成,近似於圖像中可見的主要特徵,使用 SVGO 優化 SVG,並且為它增加高斯模糊濾鏡。產生的最終的 SVG 佔位符後大小僅為約 800~1000 位元組,在屏幕上看起來更為平滑,並提供一個圖像內容的視覺提示。
這個結果和使用一個用了模糊技術的極小佔位符圖像類似。(看看 Medium 和 其它站點 是怎麼做的)。區別在於它們使用了一個點陣圖圖像,如 JPG 或者 WebP,而這裡是使用的佔位符是 SVG。
如果我們使用 SQIP 而不是原始圖像,我們將得到這樣的效果:
第一張圖像 和 第二張圖像 使用了 SQIP 後的輸出圖像。 |
輸出的 SVG 約 900 位元組,並且通過檢查代碼,我們可以發現 feGaussianBlur
過濾被應用到該組形狀上:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 2000 2000"><filter id="b"><feGaussianBlur stdDeviation="12" /></filter><path fill="#817c70" d="M0 0h2000v2000H0z"/><g filter="url(#b)" transform="translate(4 4) scale(7.8125)" fill-opacity=".5"><ellipse fill="#000210" rx="1" ry="1" transform="matrix(50.41098 -3.7951 11.14787 148.07886 107 194.6)"/><ellipse fill="#eee3bb" rx="1" ry="1" transform="matrix(-56.38179 17.684 -24.48514 -78.06584 205 110.1)"/><ellipse fill="#fff4bd" rx="1" ry="1" transform="matrix(35.40604 -5.49219 14.85017 95.73337 16.4 123.6)"/><ellipse fill="#79c7db" cx="21" cy="39" rx="65" ry="65"/><ellipse fill="#0c1320" cx="117" cy="38" rx="34" ry="47"/><ellipse fill="#5cb0cd" rx="1" ry="1" transform="matrix(-39.46201 77.24476 -54.56092 -27.87353 219.2 7.9)"/><path fill="#e57339" d="M271 159l-123–16 43 128z"/><ellipse fill="#47332f" cx="214" cy="237" rx="242" ry="19"/></g></svg>
SQIP 也可以輸出一個帶有 Base64 編碼的 SVG 內容的圖像標籤:
<img width="640" height="640" src="example.jpg」 alt="Add descriptive alt text" style="background-size: cover; background-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAw…<stripped base 64>…PjwvZz48L3N2Zz4=);">
輪廓
我們剛才看了使用了邊緣和原始形狀的 SVG。另外一種矢量化圖像的方式是 「描繪」 它們。在幾天前 Mikael Ainalem 分享了一個 codepen 代碼,展示了怎麼去使用兩色輪廓作為一個佔位符。結果非常漂亮:
SVG 在這種情況下是手工繪製的,但是,這種技術可以用工具快速生成並自動化處理。
- Gatsby,一個用 React 支持的描繪 SVG 的靜態網站生成器。它使用 一個 potrace 演算法的 JS 移植 去矢量化圖像。
- Craft 3 CMS,它也增加了對輪廓的支持。它使用了 一個 potrace 演算法的 PHP 移植。
- image-trace-loader,一個使用了 potrace 演算法去處理圖像的 Webpack 載入器。
如果感興趣,可以去看一下 Emil 的 webpack 載入器 (基於 potrace) 和 Mikael 的手工繪製 SVG 之間的比較。
這裡我假設該輸出是使用默認選項的 potrace 生成的。但是可以對它們進行優化。查看 圖像描繪載入器的選項,傳遞給 potrace 的選項非常豐富。
總結
我們看到了從圖像中生成 SVG 並使用它們作為佔位符的各種不同的工具和技術。與 WebP 是一個用於縮略圖的奇妙格式 一樣,SVG 也是一個用於佔位符的有趣格式。我們可以控制細節的級別(和它們的大小),它是高可壓縮的,並且很容易用 CSS 和 JS 進行處理。
額外的資源
這篇文章上到了 Hacker News 熱文。對此以及在該頁面的評論中分享的其它資源的鏈接,我表示非常感謝。下面是其中一部分。
- Geometrize 是用 Haxe 寫的 Primitive 的一個移植。也有一個 JS 實現,你可以直接 在你的瀏覽器上嘗試它。
- Primitive.js,它也是 Primitive 在 JS 中的一個移植,primitive.nextgen,它是使用 Primitive.js 和 Electron 的 Primitive 的桌面版應用的一個移植。
- 這裡有兩個 Twitter 帳戶,裡面你可以看到一些用 Primitive 和 Geometrize 生成的圖像示例。訪問 @PrimitivePic 和 @Geometrizer。
- imagetracerjs,它是在 JavaScript 中的光柵圖像描繪器和矢量化程序。這裡也有為 Java 和 Android 提供的移植。
via: https://medium.freecodecamp.org/using-svg-as-placeholders-more-image-loading-techniques-bed1b810ab2c
作者:José M. Pérez 譯者:qhwdw 校對:wxy
本文轉載來自 Linux 中國: https://github.com/Linux-CN/archive