使用Vue + 雪碧图(CSS sprite),webpack-spritesmith

5,530 阅读5分钟

开发了将近两年的小程序,一直默默的在小程序迭代中。很久没有玩过Vue, 最近有个新的项目用上了Vue, 看到Ui图小图标太多了, 马上想着要用雪碧图, 啪啦啪啦的找雪碧图合成方法

最后给大家推荐webpack-spritesmith , 这是一个融合node的生成css + 雪碧图(CSS sprite)排版的工具, 可以导入css之后,利用标签像iconfont一样调用即可,比较的方便

github: github.com/mixtur/webp…

前言: 可以自定义设置样式模板,之后可以自定义标签,不管是stylus / scss / less / css 都能定义

1. 什么都不要说了,安装吧~

npm i spritesheet-templates --save-dev

2. 先批量重命名图片

为什么? 整齐好看,就像阅兵仪式一样, 其实按顺序排列,是方便你css表找更好

3. 有零零碎碎的图标库后,就开始配置webpack

目录结构

# 依据使用说明
/
|-src
| |-icons
| | |-icon_01.png
| | |-icon_02.png
| | |-icon_03.png
| | ...
| |-style.styl
| ...
|- webpack.config.js

通用配置

var path = require('path');
var SpritesmithPlugin = require('webpack-spritesmith');
module.exports = {
    // ...
    module: {
        rules: [
            {test: /\.styl$/, use: [
                'style-loader',
                'css-loader',
                'stylus-loader'
            ]},
            {test: /\.png$/, use: [
                'file-loader?name=i/[hash].[ext]'
            ]}
        ]
    },
    resolve: {
        modules: ["node_modules", "spritesmith-generated"]
    },
    plugins: [
        new SpritesmithPlugin({
            src: {
                cwd: path.resolve(__dirname, 'src/icons'),    #图标所在的位置
                glob: '*.png' #图标的格式
            },
            target: {
                #建议这两个文件放一起
                image: path.resolve(__dirname, 'src/spritesmith-generated/sprite.png'), #生成指定位置的雪碧图
                css: path.resolve(__dirname, 'src/spritesmith-generated/sprite.[styl|css|stylus|scss|less]'), #生成指定位置样式表,会对应雪碧图所有的背景偏移位置
            },
            apiOptions: {
                #针对上面生成的样式表的相对路径sprite.[styl|css|stylus|scss|less]
                cssImageRef: "./sprite.png"
            }
        })
    ]
    // ...
};

自定义样式模板

第二种配置方式,通用,这种自定义样式模板

# 除法精度问题
function divide (arg1, arg2) {
    console.log(arg1, arg2)
    var t1 = 0; var t2 = 0; var r1; var  r2
    try { t1 = arg1.toString().split('.')[1].length } catch (e) {}   // --小数点后的长度
    try { t2 = arg2.toString().split('.')[1].length } catch (e) {}  // --小数点后的长度
    r1 = Number(arg1.toString().replace('.', ''))  // --去除小数点变整数
    r2 = Number(arg2.toString().replace('.', ''))  // --去除小数点变整数
    return (r1 / r2) * Math.pow(10, t2 - t1)   // ---整数相除 在乘上10的平方  小数点的长度
}
 
 # 函数自定义样式表方法
 #需要特意说明: 如果你的
 function templateFunction(data) {
    var spritesheet = data.spritesheet # 精灵图对象
    var unit = 'px' # 单位
    var w = spritesheet.width # 精灵图宽
    var h  = spritesheet.height # 精灵图高
    var shared = `.ico { 
          background-image: url(${spritesheet.image});
          background-size: ${w}${unit} ${h}${unit} 
    }`
    
    #循环雪碧图并且设置对应图标样式的宽度与高度
    # data.sprites: 精灵图小图标遍历
    var perSprite = data.sprites.map(function (sprite) {
        var offsetX = sprite.offset_x # 距离左上角的x轴距离, 这里为负数,已经默认算好
        var offsetY = sprite.offset_y # 距离左上角的y轴距离, 这里为负数,已经默认算好
        # 需要return出去,并且返回一个默认样式,因为到了rem会有一些偏差,所以需要设置padding:1px
        return `.ico-N {position:relative; top: 2${unit};padding: 1${unit}; width: ${sprite.width}${unit}; height: ${sprite.width}${unit}; background-position: X${unit} Y${unit}; display: inline-block;}`
            .replace('N', sprite.name)
            .replace('X', offsetX)
            .replace('Y', offsetY)
    }).join('\n')

    return shared + '\n' + perSprite
}



module.exports = {
        ...
        plugins: [
            new SpritesmithPlugin({
                src: {
                    cwd: path.resolve(__dirname, 'src/icons'), #图标所在的位置
                    glob: '*.png' #图标的格式
                },
                target: {
                    image: path.resolve(__dirname, 'src/spritesmith-generated/sprite.png'), #生成指定位置的雪碧图
                    css: [
                        # 引入样式模板
                        [path.resolve(__dirname, 'src/spritesmith-generated/sprite.[styl|css|stylus|scss|less]'), {
                            format: 'function_based_template' #这里的样式模板通过customTemplates指定
                        }]
                    ]
                },
                apiOptions: {
                    cssImageRef: './sprite.png'
                },
                customTemplates: {
                    'function_based_template':  templateFunction, # 指定样式模板,这里可以是函数,可以是handlebars后台模板
                },
                spritesmithOptions: {
                    #针对上面生成的样式表的相对路径sprite.[styl|css|stylus|scss|less]
                    algorithm: 'top-down', # 从上到下排列雪碧图
                    padding: 20, # 雪碧图的间距
                }
            })
        ]
}

4. 在标签中使用

  • 打开你的图标看看相应需要的图标名称
  • 复制你需要图标的名称,打开刚才生成的sprite.[styl|css|stylus|scss|less]
  • 引入sprite.[styl|css|stylus|scss|less]到src/main.js,主目录下 例如:
  • 在.vue - template 中使用
    <div class="ico ico-icon_14"></div>
    <div class="ico ico-icon_13"></div>
    <div class="ico ico-icon_16"></div>
    <div class="ico ico-icon_17"></div>
    <div class="ico ico-icon_18"></div>
    <div class="ico ico-icon_19"></div>
    <div class="ico ico-icon_20"></div>
    <div class="ico ico-icon_21"></div>

这里相当于引入你刚才自定义样式表中的模板

5. 大功告成,成功使用,非常方便

以后只需要npm run dev 启动则自动进行生成,建议如果之后加图,可以分出来,或者是按需要加在后面,更好的维护

6. 适配rem的坑

我是用750设计稿 用的75像素的 , 所以转换成 10rem = 750px / 1rem = 75px

  • 坑1: 单位与位置转换问题 项目中使用了 postcss-px2rem-exclude 或者一些其他的转换rem工具

ps: 这些工具设置之后,都会导致rem自动转换,雪碧图位置与样式表提供的无法对应上, 所以需要针对正个雪碧图设置background-size 其实相当于,你雪碧图多少background-size就设置多少,例如雪碧图300px * 300px 那么background-size: 300px 300px, 通过设置之后,就不会遇到对应不上的问题

  • 坑2: 安卓-微信内部QQ浏览器边边角角会偏差1px

好好的配了半天的我,遇到这个坑,解决了差不多6-7小时,这个问题主要是因为rem转换之后,计算值有所偏差导致的,在外部浏览器打开都没问题,安卓微信内部QQ浏览器就出现偏差问题,用别的手机也会出现,我试图用%代替,但是未能成功,尝试过用精确的函数去计算精度问题。发现还是会偏差,总的来说不知道哪个环节出了问题。那么,我就不纠结这些事了。

其一 在配置样式表中加入 padding: 1px 去解决转回的时候弥补了一些计算js存在的精度

其二就是把 配置中spritesmithOptions.padding 间距设置大一点,雪碧图方向修改掉top-down,就解决了rem在微信内部QQ浏览器

spritesmithOptions: {
    #针对上面生成的样式表的相对路径sprite.[styl|css|stylus|scss|less]
    algorithm: 'top-down', # 从上到下排列雪碧图
    padding: 20, # 雪碧图的间距
}

总结: 已经无法思考了...