踩坑小程序canvas,撤销操作实现
小程序搜索【涂图了】,可进行体验。
轨迹自动播放实现
在画布上绘制笔迹的时候,通过touchmove
事件将每一次绘制的点坐标都记录起来,同时也要将当前笔迹的颜色、宽度等数据记录起来。最终画布上所有的笔迹都转换成了一个数据列表,当播放的时候,再将列表里面的数据一条一条的在canvas
上重新绘制出来,通过setTimeout
进行自动循环不断的去绘制。
看代码:
class Paint {
// ...省略...
/**
* 从头绘制路径
* @param path 路径
* @param completed 完成回调
* @returns
*/
playPath(path: Path[], completed?: () => void) {
if (!path.length) return Promise.resolve();
this.path = path;
// 初始化
this.row = 0;
this.column = 0;
this.stop = false;
this.isComplete = false;
const { pos, color, width } = path[0];
this.start(pos[0], color, width);
this.run(completed);
if (completed) {
this.completed = completed;
}
}
private completed() {}
private run(completed = this.completed) {
// 结束绘制(下一次播放的时候要结束上一次播放)
if (this.stop) {
return;
}
if (this.column < this.path[this.row].pos.length) {
// 绘制第 n 条轨迹
this.drawLine(this.path[this.row].pos[this.column ]);
setTimeout(() => this.run(), 16.7);
} else {
// 一条轨迹制完成
if ( this.row < this.path.length) {
// 初始化下一条轨迹
this.column = 0;
const { pos, color, width } = this.path[this.row];
this.start(pos[0], color, width);
// 延时一会儿开始绘制下一条轨迹
setTimeout(() => {
setTimeout(() => this.run(), 16.7);
}, 240);
} else {
// 结束
this.isComplete = true;
completed();
}
}
}
/**
* 暂停播放
*/
pause() {
this.stop = true;
}
/**
* 继续播放
* @param completed 完成回调
*/
play(completed?: () => void) {
this.stop = false;
this.run(completed);
}
}
有些命名可能有点不规范,逻辑也有一点复杂。代码中path
数据列表保存的是每一次绘制的笔迹,每一条笔迹又包含了一组坐标点、颜色和宽度,类型定义如下:
export interface Dot {
x: number;
y: number;
}
export interface Path {
pos: Dot[];
color?: string;
width?: number;
}
最终绘制的时候都是在处理点坐标,就相当于是在处理一个二维数组,所有就通过row
、column
变量来进行区分。
撤销操作
主要思路就是:每在画布上进行一次绘制,在结束的时候touchend
就将当前画布上的数据保存起来,通过ctx.getImageData
api可以保存当前画布,然后放在一个列表中。同时再用一个变量作为指针来记录当前所在列表的位置,没点击一次撤销操作,指针就在列表中往前移动一步,同理点击恢复撤销时,指针往后移动一步。需要注意的一个点就是,当进行n次撤销操作后,指针不在列表最后的位置时,再进行绘制,此时应该清空列表中指针所在位置后面的数据,并保存当前画布数据。因为每次在画布上的操作,都应该是列表中的最后一条数据。
const handleTouchEnd = () => {
if (!painting) return;
painting = false;
// 步骤 1
commit(TypeKeys.OPERATION_ADD);
// 保存路径
const path = state.path.slice(0, state.currentPathIndex);
commit(TypeKeys.SET_PATH, path.concat(currentLine));
// 生成记录
const list = state.historyStepList.slice(0, state.currentStepIndex >= MAX_HISTORY_COUNT - 1 ? state.currentStepIndex 1 : state.currentStepIndex);
commit(TypeKeys.SET_HISTORY_STEP_LIST, list.concat(paint.value?.getImageData()));
};
这里主要的难点就是对指针的控制,尤其是在列表的临界点时的处理,因为我这里还需要控制路径的列表,所有更加复杂一些,这一块差不多花了我一天的时间,处理不好在撤销的时候就很容易出bug。
由于每次保存的画布数据是比较大的,必须对历史记录列表做一个最大长度的限制,否则就会撑爆内存,小程序闪退。getImageData
获取的画布数据大小取决于画布的width*height
的大小,由于我这个小程序是全屏的,所以在大屏幕上的数据会非常的大,我目前限制了最大历史记录为10,后期考虑固定画布的大小。
不同大小的画布还会存在一个问题,就是在小屏幕手机上,可能会展示不全大屏幕手机分享的内容。
未完待续
最后,再放出小程序码,欢迎大家进行体验。
这篇好文章是转载于:学新通技术网
- 版权申明: 本站部分内容来自互联网,仅供学习及演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,请提供相关证据及您的身份证明,我们将在收到邮件后48小时内删除。
- 本站站名: 学新通技术网
- 本文地址: /boutique/detail/tanhieghaj
-
photoshop保存的图片太大微信发不了怎么办
PHP中文网 06-15 -
photoshop扩展功能面板显示灰色怎么办
PHP中文网 06-14 -
word里面弄一个表格后上面的标题会跑到下面怎么办
PHP中文网 06-20 -
《学习通》视频自动暂停处理方法
HelloWorld317 07-05 -
TikTok加速器哪个好免费的TK加速器推荐
TK小达人 10-01 -
Android 11 保存文件到外部存储,并分享文件
Luke 10-12 -
excel下划线不显示怎么办
PHP中文网 06-23 -
微信公众号没有声音提示怎么办
PHP中文网 03-31 -
微信运动停用后别人还能看到步数吗
PHP中文网 07-22 -
excel打印预览压线压字怎么办
PHP中文网 06-22