不知不觉又到了周末,又到了Fly写文章的日子,今天给大家介绍下一个web中很常见的功能, 就是撤销和复原这样一个功能,对于任何一个画图软件,或者是建模软件。没有撤销和复原。这不是傻👁了对啊吧,所以本篇文章,可以说是基于上一篇文章**Canvas 事件系统**的下集,如果你没有看过,建议看完再去看这一篇文章。读完本篇文章你可以学习到什么??
- 给canvas 绑定键盘事件
- 实现undo 和 redo
- 批量回退
- 2d包围盒算法
- 局部渲染
绑定键盘事件
tabindex
很多人说绑定键盘事件,有什么好讲的。对虽然很简单,但是有点小坑, 首先直接对canvas 监听键盘事件,是❌不行的。 这里涉及到一个小技巧, 就是给canvasdom元素 加上 tabindex 属性 ,很多人说这是啥,我来看下官方文档。
tabindex 全局属性 指示其元素是否可以聚焦,以及它是否/在何处参与顺序键盘导航(通常使用Tab键,因此得名)。
tabindex 可以设置 正数 和负数
- tabindex=负值 (通常是tabindex=“-1”),表示元素是可聚焦的,但是不能通过键盘导航来访问到该元素,用JS做页面小组件内部键盘导航的时候非常有用。( 可聚焦, 但是不能输入键盘)
- tabindex=0,表示元素是可聚焦的,并且可以通过键盘导航来聚焦到该元素,它的相对顺序是当前处于的DOM结构来决定的。
- tabindex=正值,表示元素是可聚焦的,并且可以通过键盘导航来访问到该元素;它的相对顺序按照tabindex 的数值递增而滞后获焦。如果多个元素拥有相同的 tabindex,它们的相对顺序按照他们在当前DOM中的先后顺序决定
OK,这下你应该明白了,我们要想canvas 可以聚焦, 但是直接加 tabindex = 0。 我给出以下代码:
<canvas id="canvas" width="800" height="600" tabindex="0"></canvas>
this.canvas.addEventListener(keydown,()=>{
})
但是会有个问题, 你看下面图片。
有canvas有边框, 这个我们可以通过css 去解决, 不能让用户看到这个,好的交互是用户无感知。代码如下:
canvas {
background: red;
outline: 0px;
}
直接canvas 的外边框设置为0就OK了。
绑定事件
监听完成了之后,我开始对键盘事件进行处理, 首先无论是Mac 还是windows 一般用户的习惯就是 按 ctrl 或者 command, 加 z
和 y 之后进行回退, OK ,我们也这样去做。
首先定义两个变量:
export const Z = 'KeyZ'
export const Y = 'KeyY'
第二步就是写空的undo 和redo 方法
undo() {
console.log('走了undo')
}
redo() {
console.log('redo')
}
第三步开始绑定:
this.canvas.addEventListener(keydown, (e) => {
e.preventDefault()
if (e.ctrlKey || e.metaKey) {
if (e.code === Z) {
this.undo()
} else if (e.code === Y) {
this.redo()
}
}
})
这里需要讲解的就两个点哈,第一个就是 阻止事件的默认行为 , 因为,我按command + y 会打开新的标签页, 第二个就是兼容mac 和windows , 一个metaKey 一个是 ctrlKey. 看下结果:
实现undo和redo功能
撤销和复原 最主要的功能其实就是我们我们记录每一次往画布画图形的这个操作,因为我当前画布没有啥其他操作, 首先我们我用两个栈信息来,一个undo栈 一个 redo 栈。来记录每一次画布的信息。 我这里给大家画图演示:
我在画布中画了3个图形, 每一次添加瞬间我都对canvas 截图了, 并把这个信息,保存到undoStack 了。这时候我按下 ctrl + z 回退
undo栈中 只有rect 和circle,然后redo 栈 就有一个shape 了。如图:
如果在回退undo 就只有个cicrle, redo 中有 rect 和shape, 大概就是这么个过程。 原理搞清楚了直接看代码实现:
第一个先初始化属性:
this.undoStack = []
this.redoStack = []
第二个canvas实现截图功能主要是配合 使用 toDataUrl 这个api:
add(shape) {
shape.draw(</