公司项目性能调优,要把小图标合成一张大图片,以减少服务器请求。也就是要制作传说中的CSS图像精灵。
上百张图片,美工mm在photoshop里一个个的拖,太慢太累。也不是很复杂的工作,不如写个程序来做吧。
看Demo点这里
思路
- 选择磁盘上的小图标,支持多选。
- 把选中的小图标一张张的绘制到
canvas
上。
简单起见,纵向排列,每个小图标占一行,也就是最终canvas
上绘制了老长老长的内容。 canvas
导出png图片。
Show Me the Code
1. 支持多选的file input
<input type="file"
accept="image/gif,image/jpg,image/jpeg,image/png,image/bmp"
multiple>
type="file"
指定该input为文件选择框accept
限制可选文件类型multiple
表示支持多选
2. 绘制图标到canvas上
canvas
上绘制图片,要用到这个方法:
CanvasRenderingContext2d.drawImage(image, dx, dy)
- 参数
image
是绘制到canvas
上的元素
支持HTMLImageElement
、SVGImageElement
、HTMLCanvasElement
等。
在这次的程序中,我传入的是一个HTMLImageElement
,即dom元素img
。 - 参数
dx
指定小图标左上角在canvas
X轴上的位置 - 参数
dy
指定小图标左上角在canvas
Y轴上的位置
有了这个方法,我们就可以把一个
img
元素绘制到canvas
上了。但是怎么把fileInput
选中的文件转成img
元素呢?
读取选中的文件
fileInput
选完文件后会触发change
事件,事件监听器里,通过files
属性可以得到选中的文件。
fileInput.addEventListener('change', () => {
let selectedFiles = fileInput.files;
...
});
这里要注意一下:
selectedFiles
不是Array
对象,而是FileList
对象。
如果你想用Array
的方法操作,例如想按文件名排个序什么的,可以用下面的方法将它转化为Array
对象。
let selectedFiles = Array.prototype.slice.call(selectedFiles);
转化成img
元素
要使用到FileReader
去读取文件内容,以DataURL
的形式输出,设置给img.src
。
以selectedFiles
中的一个File
对象为例。
let fileReader = new FileReader();
let img = document.createElement('img');
fileReader.onload = () => {
img.src = fileReader.result;
img.onload = () => {
// onload触发以后才能读到img的宽和高
console.log(`${file.name}: ${img.width}px * ${img.height}`);
};
};
fileReader.readAsDataURL(file);
3.canvas导出图片
简单的话,chrome右键canvas->图片另存为...
就可以了。
但这样不够直观,我想加一个导出图片的按钮。
首先来定义一个导出按钮。
<a id="exportBtn" class="button" download="compressed.png">导出图像精灵</a>
download="compressed.png"
这个属性可以让链接点击后,下载文件,并且以compressed.png
为文件名保存。
前面说到FileReader
读取文件内容,可以以DataURL
形式输出。
canvas.toDataURL(type, encoderOptions)
也可以把canvas
的内容以DataURL
形式输出。
Canvas
绘制完所有图标后,将DataURL
设置给导出按钮的href
即可。
exportBtn.href = canvas.toDataURL('png');