摘要:預處理方案代表和預處理方案是預先指定需要生成的雪碧圖切片元素,由工具合成后,得到相應的雪碧圖和數據文件,開發中將二者投入使用。預處理方案一般以頁面為單元組織雪碧圖。結語關于雪碧圖的處理方案的討論就到此結束了。
廣告:SF 里弄了個 CSS 小圈子,歡迎一起來討論問題
前端小圖標處理方案眾多,本文主要介紹基于雪碧圖的處理方案,分析雪碧圖的預處理和后處理模式的得與失,以及在項目中通常會遇到的問題以及解決方案。其他小圖標處理方案未在此文探討之列。
此外,本文更多的是理論性分析,具體技術實現不作深入解釋。如有疑問,歡迎到上述小圈子一起討論!
討論的起點是工程工具 Gulp/Webpack 上的集成方案(手動拼合雪碧圖的做法已經作古了)。
1. 預處理方案代表:gulp.spritesmith 和 webpack-spritesmith
預處理方案是預先指定需要生成的雪碧圖切片元素,由工具合成后,得到相應的雪碧圖和數據 (S)CSS 文件,開發中將二者投入使用。
數據文件內容通常是可定義的,我們可以自定義模板或者內容生成函數(怎么寫這里就不探討了)。借助 SCSS 等 CSS 預編譯語言的威力,如何使用雪碧圖數據就極具便利性。
注意,下面的 SCSS 代碼都是寫的模板函數生成的內容,模板函數具體怎么寫不在本文探討范圍內。
最簡單的,我們可以直接生成類(如),如:
.icon-home { width: 12px; height: 12px; background: url(sprite.png) -56px -48px no-repeat; }
這樣在 HTML 結構中可以直接使用。但如果切片元素眾多,每一條規則都要有多帶帶的 background 屬性的話,未免太冗長,稍微改進下,把公共的 background-image 屬性提取出來(高清模式下還有 background-size,以下從簡討論),可以生成這樣的內容:
.icon-home { width: 12px; height: 12px; background-position:-56px -48px; } .icon-back { width: 18px; height: 20px; background-position: -10px 0; } .icon-home, .icon-back { background-image: url(sprite.png); }
但是,如果有的類沒用到,白白生成一個類豈不是很沒必要?或者我們不想使用 .icon-home 這樣子的類名,那我們用 SCSS 占位符可以解決這個問題:
%-icon-home { width: 12px; height: 12px; background-position:-56px -48px; } %-icon-home, %-icon-back { background-image: url(sprite.png); background-repeat: no-repeat; }
然后需要用到的時候,再繼承這個占位符:
.head-home { @extend %-icon-home; }
這樣,我們就能用上自定義的類名,并節省 CSS。
但如果遇到狀態性變化怎么辦?比如 :hover 時小圖標變紅,這時候其實除了 background-position 改變外,其他數據是沒有變化的。
按上述方案,我們的寫法會是:
.head-home { @extend %-icon-home; &:hover { @extend %-icon-home_hover; } }
生成的最終 CSS 是:
.head-home { width: 12px; height: 12px; background-position:-56px -48px; } .head-home:hover { width: 12px; height: 12px; background-position: 0 0; } .head-home, .head-home:hover { background-image: url(sprite.png); }
思索發現,本質上占位符只是包含了圖片信息,那我們換一種更加高端點的寫法:
// 把雪碧圖數據保存為一個 SCSS Map 數據 $__sprite__: ( "home": ( "width": 12, "height": 12, "x": 56, "y": 48, "url": "sprite.png" ), "home_hover": ( "width": 12, "height": 12, "x": 0, "y": 0, "url": "sprite.png" ) ); // 公共部分依然占位符 %-sprite-common { background-image: url(sprite.png); } // 輸出 background-position @mixin sprite-position($name) { $data: map-get($__sprite__, $name); $x: map-get($data, "x"); $y: map-get($data, "y"); background-position: -#{$x}px -#{$y}px; } // 輸出其他切片元素私有數據 @mixin sprite-item($name) { // 繼承公共樣式 @extend %-sprite-common; // 設置獨有樣式 @include sprite-position($name); $data: map-get($__sprite__, $name); $width: map-get($data, "width"); width: unquote($width + "px"); height: unquote(map-get($data, "height") + "px"); }
生成出以上 SCSS 模板后,我們就可以這樣使用雪碧圖了:
.head-home { @include sprite-item("home"); &:hover { @include sprite-position("home_hover"); } }
生成出來的效果類似于:
.head-home { background-image: url(sprite.png); } .head-home { background-position: -56px -48px; width: 12px; height: 12px; } .head-home:hover { background-position: -0px -0px; }
這樣,在 :hover 態下生成的 CSS 規則只會包含必要的變動。
總結起來,預處理模式的優點在于,通過自定義 CSS 數據文件,可以隨心所欲地使用已經生成出來的雪碧圖信息。
然而,在預處理模式下,開發的頁面依賴的是生成后的雪碧圖,而不是合并前的雪碧圖切片元素,隨之帶來的問題是:沒辦法實現雪碧圖的按需合并。
預處理方案一般以頁面為單元組織雪碧圖。思考這樣的問題:如果一張雪碧圖對應一個頁面,那各頁面的公共組件使用的雪碧圖,是每個頁面各一份副本,還是只保存一次切片元素,把通用的抽成一張公共雪碧圖呢?
如果各存一份,公共組件的切片元素就得保存到多個文件夾,每次更新、刪除、添加的時候得同步多處。如果管理不當,就會導致頁面元素不同、廢棄的切片仍被合并以及遺漏等問題。(以 CSS 文件為單元組織雪碧也會遇到類似的情況。)
如果抽成一張公共雪碧圖,假設一個最簡單場景:有 ABC 三個頁面,其中 AB 頁面有一個共同的切片元素 ab.png ,BC 頁面也有一個共同的切片元素 bc.png。這兩張切片元素都放進了 spr_common.png 中。由于靜態資源管理需要,我們在打包的時候統一給資源加上簽名,也就成了 spr_common.de353d.png。現在,需要更新 ab.png 這張切片,進而變成了 spr_common.5ef25d.png。而 C 頁面的樣式里包含了這張雪碧圖,盡管自身沒有任何變動,但由于公共雪碧圖變了,C 頁面的 CSS 也必須跟著變化。即,公共雪碧圖會帶來耦合問題,局部頁面更新會造成其他頁面不必要的跟隨變化。
后處理方案則解決了這些問題。
2. 后處理方案典型:postcss-sprites
后處理方案通過對已經生成的 CSS 文件進行分析,將其中包含切片元素的 background 或 background-image 作為依賴收集,合并成雪碧圖后再將相關參數替換。
如上述典型工具,生成前是:
.comment { background: url(images/sprite/ico-comment.png) no-repeat 0 0; } .bubble { background: url(images/sprite/ico-bubble.png) no-repeat 0 0; }
生成后是:
.comment { background-image: url(images/sprite.png); background-position: 0 0; } .bubble { background-image: url(images/sprite.png); background-position: 0 -50px; }
如此一來,CSS 中有哪些切片元素就合并哪些,不會把沒有用到的切片也合并進去。即一張 CSS 樣式表有一張專門的雪碧圖。
不過,正如上面所看到的,后處理模式解決了按需合并的問題,也不會造成頁面/組件間雪碧圖的耦合,但卻喪失了預處理方案中直接利用數據的便捷性。在預處理方案中,我們不用人工地去衡量切片元素的寬高,而是讓 SCSS 自動輸出,后處理方案卻做不到這一點。
3. 預處理和后處理相結合既要能像預處理那樣不用人工的地去量切片,又要像后處理那樣實現按需的合并,這就是我理想的開發模式。
基于以上探索,我寫了個工具:postcss-sprite-property 來實現二者的平衡。做法是:
區分開發環境和生產環境。在開發環境中,不合并雪碧圖,直接使用切片元素預覽;生產環境中,為每一張樣式表內的切片合并雪碧圖
利用 node-sass 所支持的自定義函數功能,為 SCSS 注入 image-width 和 image-height 兩個自定義函數,用來查詢圖片寬高數據
使用 @sprite-item 和 @sprite-position 兩個混合,優雅地定義一張雪碧圖元素
盡量為公共屬性生成一個公共的規則
Webpack 中,開發時不拼合雪碧圖,讓切片本身作為組件依賴;打包時合并雪碧圖并剔除切片元素
來看一個實例:
我們把如下樣式放進我們的公共樣式庫中:
// 使用雪碧圖 // 以圖片名作為參數 @mixin sprite-item($name) { // 統一一個路徑存放切片元素 // 這樣就能得到圖片的實際地址了 $url: "../asset/sprite/#{$name}.png"; // 注入的 `image-width` 函數可以幫我們查詢圖片寬度 $width: image-width($url); @if (null == $width) { @warn "Sprite element `#{$name}` not found!"; } @else { // 獲取圖片高度 $height: image-height($url); // 自動書寫 `width` 和 `height` width: $width; height: $height; // 定義背景 // 開發模式下直接按輸出預覽 // 生產環境下將其替換為雪碧圖的數據 background: url($url) 0 0 / #{$width} #{$height} no-repeat; } } // 改變雪碧圖 `background-position` // 和上面的 `sprite-item` 區別僅在于 // 這個混合不輸出 `width` 和 `height` @mixin sprite-position($name) { $url: "../asset/sprite/#{$name}.png"; $width: image-width($url); @if (null == $width) { @warn "Sprite element `#{$name}` not found!"; } @else { $height: image-height($url); background: url($url) 0 0 / #{$width} #{$height} no-repeat; } }
現在,具體項目里就能這樣使用了:
.home-head { @include sprite-item("pageA/home"); &:hover { @include sprite-position("pageA/home_hover"); } } .home-back { @include sprite-item("pageB/back"); }
開發階段,沒有合成雪碧圖,直接使用切片預覽,所以效果是這樣的:
.home-head { width: 12px; height: 12px; background: url("../asset/sprite/pageA/home.png") 0 0 / 12px 12px no-repeat; } .home-head:hover { background: url("../asset/sprite/pageA/home_hover.png") 0 0 / 12px 12px no-repeat; } .home-back { width: 18px; height: 20px; background: url("../asset/sprite/pageB/back.png") 0 0 / 18px 20px no-repeat; }
最后發布生成的樣式則是這樣的:
.home-head { width: 12px; height: 12px; background-position: -56px -48px; } .home-head:hover { background-position: 0 0; } .home-back { width: 18px; height: 20px; background-position: -10px 0; } .home-head, .home-head:hover, .home-back { background-image: url(sprite.png); }
當然,如上所示,.home-head 和 .home-head:hover 都在最后的公共規則中。最理想的當然是沒有更好,但作為一個平衡方案,這仍是可接受的。
更具體的用法和更多的功能請參看 Github:https://github.com/HaoyCn/pos...
4. REM 布局中雪碧圖的錯位問題這個算是雪碧圖的硬傷。其他小圖標方案不會有這個問題。REM 布局里雪碧圖錯位幾乎是不可避免的,在安卓下錯位甚至可以達到 2px 左右。我也總結出一個 CSS 兜錯方案:
background-position 使用百分比單位
給雪碧圖增加透明內邊距,如 spritesmith 工具設置 padding: 8
增加 1-2px 的容錯區域,概要代碼如下:
%-sprite { // 錯位時的容錯區域 padding: 1px; // 從內容區開始繪制背景 background-origin: content-box; // 在內邊距盒外裁切背景 background-clip: padding-box; }
利用以上三個屬性的組合來騰出容錯的空間。
4. 結語關于雪碧圖的處理方案的討論就到此結束了。待 HTTP2 普及之后,也就沒有雪碧圖什么事了,而現在仍有其存在的必要性。
處理小圖標還有其他的方案,如 Iconfont 和 Svg-Sprite。總之沒有最好的,只有最適合的。
感謝閱讀。歡迎來文章頂部的小圈子一起討論!
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/116621.html
摘要:前端日報精選等新方法簡介寫作建議和性能優化小結前端面試之篇第期關于雪碧圖預處理和后處理方案的討論你的網站可以一鍵變色嗎中文譯內存管理碰撞課程掘金阿里前端面試點目標,想成為一名好的前端工程師那些事函數能干啥如何在日常搬磚中使用最全, 2017-09-28 前端日報 精選 before(),after(),prepend(),append()等新DOM方法簡介css寫作建議和性能優化小結前...
摘要:編寫配置文件,以下是關鍵配置代碼雪碧圖合并輸出到文件參數執行目錄參數生成的和圖片的文件名之所以推薦,是因為非常的靈活,看懂模塊的可以根據你的項目情況編寫對應的配置文件。 showImg(https://segmentfault.com/img/bVGpAw?w=518&h=156); 前言 網站開發90%會用到小圖標, 多小圖標調用顯示是前端開發常見的問題;目前小圖標顯示常見有兩種方式...
摘要:一精靈圖使用場景二優點減少圖片的字節。減少網頁的請求,從而大大的提高頁面的性能。解決了網頁設計師在圖片命名上的困擾,只需對一張集合的圖片上命名就可以了,不需要對每一個小元素進行命名,從而提高了網頁的制作效率。 一、精靈圖使用場景: showImg(https://segmentfault.com/img/bVbd4Ex?w=927&h=389); 二、Css Sprite(優點) 減...
摘要:一精靈圖使用場景二優點減少圖片的字節。減少網頁的請求,從而大大的提高頁面的性能。解決了網頁設計師在圖片命名上的困擾,只需對一張集合的圖片上命名就可以了,不需要對每一個小元素進行命名,從而提高了網頁的制作效率。 一、精靈圖使用場景: showImg(https://segmentfault.com/img/bVbd4Ex?w=927&h=389); 二、Css Sprite(優點) 減...
摘要:一精靈圖使用場景二優點減少圖片的字節。減少網頁的請求,從而大大的提高頁面的性能。解決了網頁設計師在圖片命名上的困擾,只需對一張集合的圖片上命名就可以了,不需要對每一個小元素進行命名,從而提高了網頁的制作效率。一、精靈圖使用場景: 二、Css Sprite(優點) 減少圖片的字節。 減少網頁的http請求,從而大大的提高頁面的性能。 解決了網頁設計師在圖片命名上的困擾,只需對一張集合的圖片...
閱讀 1164·2021-09-10 10:51
閱讀 896·2019-08-30 15:53
閱讀 2724·2019-08-30 12:50
閱讀 976·2019-08-30 11:07
閱讀 1990·2019-08-30 10:50
閱讀 3598·2019-08-29 18:47
閱讀 1308·2019-08-29 18:44
閱讀 1599·2019-08-29 17:01