基于Webpack的css sprites实现方案

波利尼西亚天空

一、前言

关于css sprites(雪碧图/精灵图)的几种实现方案可以参考浅谈 CSS Sprites 雪碧图应用。本文主要讨论基于webpack的css sprites实现方案。

由于使用webpack时会涉及到其他插件,没有相关基础的可以参考我之前的一篇文章使用Webpack对CSS文件进行后处理先进行配置,不过目录结构和该篇会有点差别。

二、 目录结构预览

+ 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

三、 安装webpack-spritesmith插件

npm install webpack-spritesmith --save-dev

四、 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的具体参数设置参考1、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

六、后记

在研究雪碧图的过程中也有在思考在项目中雪碧图的必要性,经常看到网上说HTTP/2即将到来,压缩代码、文件合并、雪碧图都是多余的操作,但是看到这篇文章HTTP/2 下提高网站加载速度的资源打包指南,结论是:尽管 HTTP/2 被设计成一个可以高效传输许多小文件的协议,但当需要传输的文件数达到一定规模后,每个文件带来的额外开销也会积少成多,影响效率。所以至少在还有很长一段的时间内雪碧图还是很有必要的。

Image placeholder
peterq
未设置
  49人点赞

没有讨论,发表一下自己的看法吧

推荐文章
基于Redis实现Spring Cloud Gateway的动态管理

引言:SpringCloudGateway是当前使用非常广泛的一种API网关。它本身能力并不能完全满足企业对网关的期望,人们希望它可以提供更多的服务治理能力。但SpringCloudGateway并不

Stylus系列——webpack-spritesmith配合stylus使用示例

一、前言基于Webpack的CSSSprites实现方案,若是直接在html中调用雪碧图图标已经很方便,但是实际开发过程可能遇到需要在伪元素中使用雪碧图,或者需要hover切换另一个图标,这种情况下就

不一样的css,sass(scss)的基本使用

前言此文主要记录sass的scss语法的基本使用。sass是css的预编译器,它扩展一些css所没有的变量的定义、条件控制、循环、自定义方法等。基础内容1.变量/*scss*/ //声明变量 $pri

Ant Design Vue 中a-upload组件通过axios实现文件列表上传与更新回显的前后端处理方案

前言在企业应用的快速开发中,我们需要尽快的完成一些功能。如果您使用了AntDesignVue,在进行表单的文件上传相关功能开发的时候,您肯定迫不及待地需要找到一篇包治百病的文章,正是如此,才有了该文的

Libra:Facebook的”野心”?

2008年11月1日,有一个传说叫中本聪的日裔美国人,发表了一篇名为《比特币:一种点对点式的电子现金系统》的论文。2009年,比特币出世,从此开启了电子货币这个收割机器,全世界的韭菜都是一个味。200

基于Pandas+ECharts的金融大数据可视化实现方案

前言最近无意中看到一篇文章,介绍的是在IPythonNotebook里实现ECharts的可视化效果。我个人对ECharts一直是推崇有加,是baidu发布的开源项目中我比较喜欢的一个,绝对是良心之作

CSS实现自适应分隔线的N种方法

分割线是网页中比较常见的一类设计了,比如说知乎的更多回答这里的自适应是指两边的横线会随着文字的个数和父级的宽度自适应偷偷的看了一下知乎的实现,很显然是用一块白色背景覆盖的,加一点背景就露馅了心想:知乎

怎样用css实现图片不间断滚动

怎样用css实现图片不间断滚动效果图:思路分析:第一步,定义div>ul>li*7,因为有7张图片。第二步,设置div的宽度高度以及li的浮动,保证它们全部在div内的同一行。第三步,实现滚动。用到了

CSS实现多行省略

什么是多行省略?当字数多到一定程度就显示省略号点点点。最初只是简单的点点点,之后花样越来越多,点点点加下箭头,点点点加更多,点点点加更多加箭头...。多行省略就是大段文字后面的花式点点点。同行这么做:

webpack中css的url报错?

webpack中css的url报错?css-loader://打包样式中背景图 { test:/\.(png|jpg)$/, loader:"url-loader?limit=8192&name=im

css如何实现文字颜色渐变?3种实现方法

基础样式:.gradient-text{ text-align:left; text-indent:30px; line-height:50px; font-size:40px; font-

jsp中的css写在哪儿

jsp中的css写在哪儿jsp中css写在style标签中,style标签放在head标签里。 .input_textarea{ color:#f00; } 在jsp中加入css样式,就跟ht

Spring-SpringAOP原理,手写Spring事务框架

一、Spring核心知识Spring是一个开源框架,Spring是于2003年兴起的一个轻量级的Java开发框架,由RodJohnson在其著作ExpertOne-On-OneJ2EEDevelopm

css水平垂直居中有几种实现方式?

css水平垂直居中有几种实现方式?1、水平对齐+行高【思路一】text-align+line-height实现单行文本水平垂直居中 .f10.test{ text-align:center; line

如何删除多余的css

如何删除多余的css1、chrome浏览器F12审查元素的Audits说明:使用Audits,会检测出页面中没有用到的css,需要手动删除多余的css;同时需要说明的是检测出多余无用的css块,而不是

如何用别人的css样式

如何用别人的css样式使用别人的css样式首先需要引入,引入到自己的HTML文档中,主要有两种方式。1、下载别人的css样式,通过link标签引入。打开网站,按下f12打开开发者工具面板,选中Sour

仿站复制的别人的css违法吗

仿站复制的别人的css违法吗通常来说仿站复制别人的css是不违法的,若css中存在具有版权的图片,则是属于侵权行为,需要更换为自己的图片或者是无版权的图片。仿站违法侵权范畴:页面完全一致,标题企业名称

dw里怎样设置图片的css样式

dw里怎样设置图片的css样式1、首先在文件内插入一张图片,点击插入\图像,选择一张图片即可。2、选中图像,点击css样式窗口的+号,新建一个CSS(相关课程推荐:css视频教程)样式,打开对话框。选

react中的webpack是什么?

Webpack是一个开源的前端打包工具。Webpack提供了前端开发缺乏的模块化开发方式,将各种静态资源视为模块,并从它生成优化过的代码。Webpack可以从终端、或是更改webpack.config

使用html-webpack-plugin对HTML文件进行预处理

一、前言先整理一波之前和webpack相关的文章: 使用Webpack对CSS文件进行后处理 基于Webpack的CSSSprites实现方案 Stylus系列——webpack-spritesmit

redis实践及思考

导语:当面临存储选型时是选择关系型还是非关系型数据库?如果选择了非关系型的redis,redis常用数据类型占用内存大小如何估算的?redis的性能瓶颈又在哪里?背景前段时间接手了一个业务,响应时间达

基于 Hyperf 实现 RabbitMQ + WebSocket 消息推送

#介绍 基于Hyperf+WebSocket+RabbitMQ实现的一个简单大屏幕的消息推送。 #思路 利用WebSocket协议让客户端和服务器端保持有状态的长链接,保存链接上来的客户端id。订阅发

RTSP网络摄像头/海康大华硬盘录像机网页无插件直播方案EasyNVR如何实现RTMP/FLV/HLS/RTSP直播流分发

背景需求对于摄像机直播,客户反馈的最多就是实现web直播、摆脱插件,可以自定义集成等问题。我们熟悉的EasyNVR已经完美的解决了这些问题。然而对于web播放也存在一些问题,通常我们web播放RTMP

GoWeb教程_08.2. WebSocket

WebSocket是HTML5的重要特性,它实现了基于浏览器的远程socket,它使浏览器和服务器可以进行全双工通信,许多浏览器(Firefox、GoogleChrome和Safari)都已对此做了支

【CSS全解01】CSS基础-体系化学CSS

大纲 基础部分学习占比:HTML1%`CSS19%Javascript80%`(`基础部分?%框架?%`项目?%) CSS历史 AcidTestforbrowser CSS是艺术(非逻辑,用测试经验来