本文已参与「新人创作礼」活动,一起开启掘金创作之路。 点击查看活动详情
写在前面
前面我们实现了一个简单的canvas库 100行代码写个canvas库 现在我们试一下这个库实际用起来怎么样,能不能满足常规的开发要求,雪碧图是前端提升性能一个常见的操作了,原理就是把多张图片合成为一张,通过css定位来取到具体的图像,减少http的请求,达到优化的目的。但是我在使用的时候发现了一些不友好的地方(可能是我用的工具比较low)
- 合成的时候不能调整某个图片的位置,我想把某几个同类型的图片放在一起
- 某几个图片加进去之后我想删掉
- 合成完之后又有新的图片要添加进去,进行编辑
所以我们自己来写个工具,实现这些功能。
封装一个icon类
根据参数绘制图片
class Icon extends BasicElement {
constructor(options) {
this.x = options.x
this.y = options.y
this.w = options.w
this.h = options.h
this.offsetX = options.offsetX
this.offsetY = options.offsetY
this.image = new Image()
this.image.src = options.src
}
// 结合icon的xywh和偏移量绘制图片
draw(ctx) {
ctx.drawImage(this.image, this.x + this.offsetX, this.y + this.offsetY, this.w, this.h)
}
// 点是否在icon图标上
pointInElement(x, y) {
return this.x + this.offsetX <= x &&
this.y + this.offsetY <= y &&
this.x + this.offsetX + this.w >= x &&
this.y + this.offsetY + this.h >= y;
}
// 其他要配合Stage的省略,继承BasicElement即可
}
组合删除icon
每张图片都要内置一个删除按钮,放在图片的右上角,点击删除加到画布上的图片,因此我们在组合Container里放2个icon,定义一个新的类ImageElement
class ImageElement {
constructor(options) {
this.x = options.x
this.y = options.y
this.w = options.w
this.h = options.h
// 初始化一个容器
this.container = new Container({
x: this.x,
y: this.y,
w: this.w,
h: this.h
});
// 直接初始化2个icon,一个放图片,一个作为删除按钮
this.image = new Icon({
offsetX: 0,
offsetY: 0,
w: this.w,
h: this.h,
src: options.src,
})
this.del = new Icon({
// 定位到容器的右上角
offsetX: this.w - 5,
offsetY: -5,
w: this.w,
h: this.h,
src: "./close.png",
})
// 给删除按钮添加点击事件,删除整个container,包括del
this.del.addEvent("click", (t) => {
this.container.destory()
});
this.container.add(this.image)
this.container.add(this.del)
return this.container
}
}
这样一个自带删除和拖拽的图片组件就写好了,通过input选取图片,再通过FileReader拿到图片信息,初始化到画布上,最后页面随便放个按钮根据canvas信息生成一个代码和导出文件就可以了,看一下应用代码
这里解释一个如何完成编辑功能的,在第一次导出图片后,可以把每个图片的信息也生成导出,在后续编辑的时候只要把配置信息拿到就可以了(读取文件或者输入都可以)
let config = document.getElementById("configinfo").value;
如果有配置信息,在初始化图片的时候就还原一下就可以了。
let s2 = new Stage(document.getElementById("stage"));
let data = [];
document.getElementById("upload_file").addEventListener("change", function (e: any) {
let files = e.target.files
let config = document.getElementById("configinfo").value;
config = JSON.parse(config || "[]")
// 没有配置文件也可以,就当新增了
for(let i = 0; i < files.length; i++) {
let reader = new FileReader();
reader.onloadstart = function () {};
reader.readAsDataURL(files[i]);
reader.onloadend = function (e) {
var base64 = this.result;
var image = new Image();
image.src = base64;
let cur = config.find(item => item.name == files[i].name)
// 如果存在,就返显xy,否则初始化为0
image.onload = function (r) {
data.push({
name: files[i].name,
x: cur ? cur.x : 0,
y: cur ? cur.y : 0,
w: this.width,
h: this.height,
src: base64,
});
s2.add(
new ImageElement({
name: files[i].name,
x: cur ? cur.x : 0,
y: cur ? cur.y : 0,
w: this.width,
h: this.height,
src: base64,
})
);
};
};
reader.onerror = function () {};
}
})