我学会了-js自动生成雪碧图

1,826 阅读3分钟

不知各位是否还有用雪碧图这种东西~听说过时了?哈哈哈,不管,我还是要记录下。

因为我最近用上了,所以学习了使用 webpack-spritesmith自动生成雪碧图。

需求

项目中有很多图标,或相近的按钮等,不能使用iconfont或者svg代替,因此只能从设计稿上切图后引入。

但是,每个图标都单独引入,就会使加载图片时http的请求次数增加,可能造成加载速度慢等问题。

因此,我决定使用雪碧图。

自动生成雪碧图

  1. 安装
npm i webpack-spritesmith
  1. 引入使用
// webpack文件

/*
* 引入webpack-spritesmith
**/
const fs = require('fs');
const SpritesmithPlugin = require('webpack-spritesmith');

/*
* 自定义spritesmith规则 - spritesmith默认单位为 px, 但是我需要使用rem, 所以自定义规则,将单位改为 rem
**/
var templateFunction = function (data) {
  var getRem = function(val) {
    // 修改时要同时修改 _px2rem.scss 中的$rem
    const base = 75;
    return parseInt(val)/base + 'rem'
  }

// 生成sprites.scss文件时的格式
  var perSprite = data.sprites.map(function (sprite) {
    return `@mixin sprites-${sprite.name} {
      background-image: url(${data.sprites[0].image});
      background-size: ${getRem(data.spritesheet.width)} ${getRem(data.spritesheet.height)};
      width: ${getRem(sprite.width)};
      height: ${getRem(sprite.height)}; 
      background-position: ${getRem(sprite.offset_x)} ${getRem(sprite.offset_y)};
    }`
  }).join('\n');
  return perSprite;
}

/*
* 因为想要生成多个不同类型的sprites,正常需要几个就配置几个 new SpritesmithPlugin() 即可。但是为了方便,这里做了遍历实现一次性配置,使得每一次只需要添加图片文件,就可以自动生成,不需要再进行其他配置。
**/
const spritesmithTasks = []
const spritesImg = {}
const spritesScss = []

// 下面需要注意路径是 ./src/assets/sprites 还是 ../src/assets/sprites/${dirname},如果报错,可以一个个打印看看,配置正确的路径
fs.readdirSync('./src/assets/sprites').map(dirname => {
    if (fs.statSync(`./src/assets/sprites/${dirname}`).isDirectory()) {
        spritesmithTasks.push(
            new SpritesmithPlugin({
                src: {
                	// 需要生成雪碧图的文件夹
                    cwd: path.resolve(__dirname, `../src/assets/sprites/${dirname}`), 
                    glob: '*.png'
                },
                target: {
                	// 雪碧图生成后的图片路径
                    image: path.resolve(__dirname, `../src/assets/sprites/${dirname}.png`), 
                    css: [
                    	// 雪碧图生成后对应的css文件
                      [path.resolve(__dirname, `../src/assets/sprites/_${dirname}.scss`), {
                        // 引用自己的模板
                        format: 'function_based_template'
                      }]
                    ]
                },
                // 自定义css处理模板
                customTemplates: {
                  'function_based_template': templateFunction,
                },
                // 样式文件中调用雪碧图地址写法
                apiOptions: {
                    cssImageRef: `~${dirname}.sprites.png`
                },
                spritesmithOptions: {
                    algorithm: 'binary-tree',
                    padding: 10 // 雪碧图中每个图标的间距
                }
            })
        ),
        
        // 设置生成的雪碧图路径别名,与前面的new SpritesmithPlugin 中的apiOptions.cssImageRef 相对应
        spritesImg[`~${dirname}.sprites.png`] = path.resolve(
          __dirname,
          `../src/assets/sprites/${dirname}.png`,
        ),
        // 在后面的rules中需要全局引入生成的_${dirname}.scss也就是 spritesScss文件。
        spritesScss.push(
          `./src/assets/sprites/_${dirname}.scss`
        )
    }
})

const baseResolve = {
  ...spritesImg, // 设置生成的雪碧图路径别名
  '@': path.resolve(__dirname, './src'),
};

module.exports = {
  resolve: {
    alias: {
      ...baseResolve,
      // Support React Native Web
      // https://www.smashingmagazine.com/2016/08/a-glimpse-into-the-future-with-react-native-for-web/
      'react-native': 'react-native-web',
    }
  },
  module: {
    rules: [
          {
            test: /\.scss$/,
            exclude: [/node_modules/],
            use: [
              require.resolve('style-loader'),
              {
                loader: 'sass-resources-loader',
                options: {
                  resources: [
                    ...spritesScss, // 全局引入生成的_${dirname}.scss
                  ]
                },
              },
              },
            ],
          }
        ],
      },
    ],
  },
  
  plugins: [
    ...spritesmithTasks // 遍历的new SpritesmithPlugin()
  ],
};

为了方便理解,上面将webpack配置中的其他配置删除,只留下关于spritesmith的配置信息。

配置完成后,在对应的文件夹中添加需要生成雪碧图的图片。如我上面配置的是 /src/assets/sprites/${dirname}, 此时我需要生成一个 button 的雪碧图,那么就在/src/assets/sprites文件中创建 button目录,然后将图片都导入其中。

运行项目!

当当当~你会发现/src/assets/sprites目录下增加了一个 button.png_button.scss.

button.png就是生成的雪碧图

_button.scss就是雪碧图中每个图标的css, 如下所示

@mixin sprites-btn_get_lottery_hover {
      background-image: url(~button.sprites.png);
      background-size: 11.88rem 11.626666666666667rem;
      width: 3.8133333333333335rem;
      height: 1.36rem; 
      background-position: -3.9466666666666668rem -6.64rem;
    }
@mixin sprites-btn_get_lottery {
      background-image: url(~button.sprites.png);
      background-size: 11.88rem 11.626666666666667rem;
      width: 3.8133333333333335rem;
      height: 1.36rem; 
      background-position: 0rem -6.64rem;
    }

此时,在其他css中引入图标时只需

.test {
	@include sprites-btn_get_lottery
}

知识点

使用雪碧图的好处, 参考 雪碧图有什么用

  • 减少加载图片时对网页的请求次数
  • 提高页面的加载速度
  • 减少鼠标hover的bug:IE6不会主动预加载鼠标滑过即a:hover中的背景图片,所以,如果使用多张图片,鼠标滑过会出现闪白的现象。使用CSS雪碧,由于一张图片即可,所以不会出现这种现象。(没遇到过所以没有太明白~)

疑惑

  • 使用雪碧图有什么弊端,在pc端或移动端中。

感谢

作为菜鸟,基本都靠百度,哈哈哈,下面是参考的文章,感谢感谢~

以上,仅作为菜糯糯的笔记。