基於Webpack的css sprites實現方案

波利尼西亞天空

1、前言

關於css sprites(雪碧圖/精靈圖)的幾種實現方案能夠參考淺談 CSS Sprites 雪碧圖應用。本文主要討論基於webpack的css sprites實現方案。css

因爲使用webpack時會涉及到其餘插件,沒有相關基礎的能夠參考我以前的一篇文章使用Webpack對CSS文件進行後處理先進行配置,不過目錄結構和該篇會有點差異。html

2、 目錄結構預覽

+ node_modules
+ src               // 開發目錄
    + images
        + icon
            ..png
            ..png
    + js
        + main.js
+ dist              // 代碼產出目錄
    – index.html
    + js
        - bundle.js
    + css
        - sprite.css
        - sprite.json
    + images
        - sprite.png
– package.json
– package-lock.json
– webpack.config.js

3、 安裝webpack-spritesmith插件

npm install webpack-spritesmith --save-dev

4、 webpack.config.js配置

var SpritesmithPlugin = require('webpack-spritesmith');

// 生成的雪碧圖CSS文件模板自定義,也能夠不配置直接使用默認的模板
var templateFunction = function (data) {
    
    // PC端配置
    var shared = '.ico { diaplay: inline-block; background-image: url(I); background-size: Dpx Hpx; }'
        .replace('I', data.sprites[0].image)
        .replace('D', data.sprites[0].total_width)
        .replace('H', data.sprites[0].total_height);

    var perSprite = data.sprites.map(function (sprite) {
        return '.ico-N { width: Wpx; height: Hpx; background-position: Xpx Ypx; }'
            .replace('N', sprite.name.replace(/_/g, '-'))
            .replace('W', sprite.width)
            .replace('H', sprite.height)
            .replace('X', sprite.offset_x)
            .replace('Y', sprite.offset_y);
    }).join('\n');
    
    // 移動端配置
    var sharedRem = '.ico { diaplay: inline-block; background-image: url(I); background-size: Drem Hrem; }'
        .replace('I', data.sprites[0].image)
        .replace('D', data.sprites[0].total_width / 100)
        .replace('H', data.sprites[0].total_height / 100);
    
    var perSpriteRem = data.sprites.map(function (sprite) {
        return '.ico-N { width: Wrem; height: Hrem; background-position: X Yrem; }'
            .replace('N', sprite.name.replace(/_/g, '-'))
            .replace('W', sprite.width / 100)
            .replace('H', sprite.height / 100)
            .replace('X', sprite.offset_x / 100)
            .replace('Y', sprite.offset_y / 100);
    }).join('\n');

    return shared + '\n' + perSprite + '\n\n' + sharedRem + '\n' + perSpriteRem;
};

...
...

    plugins: [
        new SpritesmithPlugin({
            src: {
                cwd: path.resolve(__dirname, 'src/images/icon'),            // 圖標根路徑
                glob: '*.png'                                               // 圖標類型
            },
            target: {
                image: path.resolve(__dirname, 'dist/images/sprite.png'),   // 生成雪碧圖的名稱和路徑
                css: [
                    [path.resolve(__dirname, 'dist/css/sprite.css'), {      // 生成CSS文件的名稱和路徑
                        format: 'function_based_template'                   // 模板配置,注意在customTemplates中配置對應名稱的屬性名
                    }],
                    [path.resolve(__dirname, 'dist/css/sprite.json'), {     // 生成json文件的名稱和路徑,想看圖片數據的能夠配置該項
                        format: 'json_texture'
                    }]
                ]
            },
            customTemplates: {
                'function_based_template': templateFunction                 // 上一項使用到的模板變量
            },
            apiOptions: {
                cssImageRef: '../images/sprite.png'                         // 生成的CSS中引用的雪碧圖路徑
            },
            spritesmithOptions: {
                algorithm: 'top-down',                                      // 生成的雪碧圖圖標排列方式
                padding: 1                                                  // 圖標的間隔
            }
        }),
        new SpritesmithPlugin...                                            //若是須要生成不止一張雪碧圖則繼續配置
    ],

4.1 webpack-spritesmith的具體參數設置參考一、2連接:

  1. 中文|Webpack3之雪碧圖插件(WEBPACK-SPRITESMITH配置簡述)
  2. 英文|webpack-spritesmith官方配置文檔
  3. 二倍圖相關配置|Webpack中雪碧圖使用詳解:因爲目前沒有該需求,暫時沒有去了解,先Mark下。

4.2 spritesmithOptions參數詳解

4.2.1 algorithm屬性

  • 'top-down': 從上到下排列
  • 'left-right': 從左到右排列
  • 'diagonal': 對角線排列,從左上到右下
  • 'alt-diagonal': 對角線排列,從右上到左下
  • 'binary-tree': 二叉樹排列,先從左到右,後從上到下
  • 可是還不太理解圖標的排列順序,好比從上到下,那麼是如何判斷哪一個圖標在第一個呢?

4.2.2 在源碼中自定義排列需求(沒有該需求的能夠跳過這步)

  • 需求:每一個圖標的間隔能夠經過padding參數設置,這在PC端是沒有問題的(固然若是用戶縮放比例有問題的話,也會出現移動端一樣的問題),可是在移動端會有該問題Retina屏下的CSS雪碧圖,這篇文章的結論是圖標間隔控制2px,可是在實際狀況下可能2px不夠,主要緣由是移動端在使用rem單位進行計算時不一樣瀏覽器對小數點的不一樣處理方式,因此咱們在排列圖標最好將圖標的起始位置放置於整十倍的位置上,如:10px、20px,這樣使用background-position: 0 50px;時,即便是使用rem單位,也只會變成0 .5rem而不會出現0 49px變成0 .49rem的狀況。
  • 解決:修改源碼 node_modules/spritesmith/src/smith.js
Spritesmith.processImages

- old
    // Add our images to our canvas (dry run)
    images.forEach(function (img) {
        // Save the non-padded properties as meta data
        var width = img.width;
        var height = img.height;
        var meta = {img: img, actualWidth: width, actualHeight: height};
            
        // Add the item with padding to our layer
        layer.addItem({
            width: width + padding,
            height: height + padding,
            meta: meta
        });
    });
    
- new
    // Add our images to our canvas (dry run)
    images.forEach(function (img) {
        // Save the non-padded properties as meta data
        var width = img.width;
        var height = img.height;
        var meta = {img: img, actualWidth: width, actualHeight: height};
            
        // chauncywu
        // 用戶傳入圖標間隔參數padding,規定兩個圖標的間隔至少爲padding,而後下一個圖標的位置以10爲倍數的位置開始
        // eg.圖標1高度爲96px,加上padding: 2px,那麼圖標2的開始位置爲96+2=98,而後取整即100px,從100px位置開始
        var layerWidth = width + padding;
        var layerHeight = height + padding;
        if(layerWidth%10 != 0) {
            layerWidth = Math.ceil(layerWidth/10) * 10;
        }
        if(layerHeight%10 != 0) {
            layerHeight = Math.ceil(layerHeight/10) * 10;
        }
            
        // Add the item with padding to our layer
        layer.addItem({
            width: layerWidth,
            height: layerHeight,
            meta: meta
        });
    });

五. 生成雪碧圖

webpack --mode development

6、後記

在研究雪碧圖的過程當中也有在思考在項目中雪碧圖的必要性,常常看到網上說HTTP/2即將到來,壓縮代碼、文件合併、雪碧圖都是多餘的操做,可是看到這篇文章HTTP/2 下提升網站加載速度的資源打包指南,結論是:儘管 HTTP/2 被設計成一個能夠高效傳輸許多小文件的協議,但當須要傳輸的文件數達到必定規模後,每一個文件帶來的額外開銷也會聚沙成塔,影響效率。因此至少在還有很長一段的時間內雪碧圖仍是頗有必要的。node

相關文章
相關標籤/搜索