在非游戏领域其实也是存在很多细小的图标需求的。但是找遍全网却没有方便简洁的图集插件。
所以在此特写一个属于前端的图集插件,该插件使用非常简单。
首先准备需要打成图集的小图标若干
再使用国际标准的图集打包软件【texturepacker】,软件长这样
然后将图片拖进软件内,
注意数据格式要选择【JSON(Array)】类型该插件才能解析成功,就是这个东西
接下来将生成的图集文件拖入工程内,比如我刚刚生成的文件分别是【asd.json 和 asd.png】
asd.json 是图集描述文件,里面记录着每个小图标的信息,位置大小等等
asd.png 是多个小图的合并生成的大图
json 文件长这样
好了准备工作做好了,接下来就来写代码
首先引入插件代码
然后在需要使用图集的【img】标签上添加属性【frame】
最后启动图集插件即可,如图所示
这是效果
这是全部代码
/*
* @Author: Summer
* @LastEditors: Summer
* @Description:
* @Date: 2022-04-13 18:07:24 +0800
* @LastEditTime: 2022-04-15 08:52:08 +0800
* @FilePath: \test\atlas.js
*/
(function () {
/**
* 加载 Image 图片元素
* @param {*} url
* @returns
*/
function loadImage(url) {
return new Promise((resolve, reject) => {
let image = new Image();
image.onerror = reject;
image.onload = () => resolve(image);
image.src = url;
})
}
/**
* Canvas 转 Image
* @param {*} canvas
* @returns
*/
function createImageByCanvas(canvas) {
return new Promise((resolve, reject) => canvas.toBlob(blob => {
let image = new Image();
image.onerror = reject;
image.onload = () => { URL.revokeObjectURL(image.src); resolve(image) }
image.src = URL.createObjectURL(blob);
}))
}
function createFrameByCanvas(canvas) {
return new Promise(resolve => canvas.toBlob(blob => resolve(new Frame(blob))));
}
class Frame {
constructor(blob) {
this.data = blob;
this.image = new Image();
this.url = this.image.src = URL.createObjectURL(blob);
Object.defineProperties(this, {
data: { writable: false, configurable: false },
image: { writable: false, configurable: false },
url: { writable: false, configurable: false },
})
}
get width() { return this.image.naturalWidth }
get height() { return this.image.naturalHeight }
}
class EventClass extends Event {
constructor(type, data) {
super(type);
this.data = data;
}
}
/**
* 图集对象
*/
window.Atlas = class Atlas extends EventTarget {
_frames = new Map();
on = this.addEventListener;
once(type, callback) { this.addEventListener(type, callback, { once: true }) }
emit(type, data) { this.dispatchEvent(new EventClass(type, data)) }
constructor() {
super()
Object.defineProperty(this, "_frames", { writable: false, configurable: false });
document.body.addEventListener("DOMSubtreeModified", this.scanning.bind(this), false);
}
/**
* 初始化图集数据
* @param {*} urls 图集的路径
*/
async init(urls = {}) {
let container = document.createElement("canvas");
let scene = document.createElement("canvas");
let s2d = scene.getContext("2d");
let c2d = container.getContext("2d");
let total = 0;
let count = 0;
for (let name in resources) {
try {
let atlas = await fetch(urls[name] + ".json").then(body => body.json());
let boot = await loadImage(urls[name] + ".png");
scene.width = boot.naturalWidth; scene.height = boot.naturalHeight;
///
s2d.clearRect(0, 0, boot.naturalWidth, boot.naturalHeight);
s2d.drawImage(boot, 0, 0, boot.naturalWidth, boot.naturalHeight);
for (let { filename, sourceSize: { w: sw, h: sh }, spriteSourceSize: { x: sx, y: sy }, frame: { x, y, w, h }, rotated } of atlas.frames) {
if (rotated) [w, h, sx, sy, sw, sh] = [h, w, sy, sx, sh, sw];
container.width = sw, container.height = sh;
c2d.clearRect(0, 0, sw, sh);
c2d.putImageData(s2d.getImageData(x, y, w, h), sx, sy, 0, 0, sw, sh);
if (rotated) {
let image = await createImageByCanvas(container);
[sw, sh] = [sh, sw];
container.width = sw, container.height = sh;
c2d.clearRect(0, 0, sw, sh);
// 先位移坐标到中心
c2d.translate(sw / 2, sh / 2);
// 旋转90度
c2d.rotate(-90 * Math.PI / 180);
// 此时按照旋转后的尺寸
// 把定位中心移动到左上角
c2d.translate(-1 * sh / 2, -1 * sw / 2);
// 绘制图片
c2d.drawImage(image, 0, 0, sh, sw, 0, 0, sh, sw)
// 坐标系还原到初始
c2d.setTransform(1, 0, 0, 1, 0, 0);
}
this._frames.set(`${name}/${filename}`, await createFrameByCanvas(container));
this.emit("process", { name:`${name}/${filename}`, total:total+=atlas.frames.length, count:++count });
}
} catch (error) {
console.error(error);
}
}
return this;
}
/**
* 获取图集中的某个图片元素
* @param {*} name 图集名称/图片名称
* @returns
*/
getFrame(name) { return this._frames.get(name) }
/**
* 获取图集中的某个图片地址
* @param {*} name 图集名称/图片名称
* @returns
*/
getFrameUrl(name) { return this._frames.get(name)?.url; }
/**
* 获取图集中所有的图片元素
* @returns
*/
getFrames() { return [...this._frames.values()] };
/**
* 开始扫描文档中使用图集规则的【Image】元素, 进行图集映射
* 规则就是在 Img 标签添加属性【frame="图集名称/图片名称"】
*/
scanning() {
/// 扫描所有的 Image 节点,实现图集映射
for (let ele of document.body.querySelectorAll("img")) if (ele.getAttribute("frame") || ele.frame) {
ele.src = this.getFrameUrl(ele.getAttribute("frame") || ele.frame)
}
}
}
})()