一. 涂色功能的实现
- 用广度优先遍历算法
- 实现过程出现栈上溢,用数组代替递归
虽然最后衡量再三不想加了,但还是让我学到了 flood fill 这个词组,意思是画图工具的颜色填充。
给我帮助的链接
二. tutorial 添加
之前不知道准确表达这件事,导致卡了一圈。
还好后来找到了,就是 intro.js
关键代码
let tutorial = [
{
element: '#background',
intro: '临摹工具,可临摹本地图片'
},
{
element: '#openFile',
intro: '编辑工具,可编辑本地图片'
},
{
element: '.sizePad',
intro: '调节画笔粗细,也可在任意地方通过滑轮操作'
},
{
element: '.colorPad',
intro: '双击调色盘定义常用颜色,单击调色盘快捷使用'
},
{
element: '#pen',
intro: '按 Ctrl + Z 可撤销画笔或橡皮操作'
}
]
function startTutorial() {
let steps = tutorial
introJs()
.setOptions({steps})
.setOption("nextLabel", "下一个")
.setOption("prevLabel", "上一个")
.setOption("skipLabel", "跳过")
.setOption("doneLabel", "完成")
.setOption("overlayOpacity", 0)
/* 下面一行需要注意 */
.setOption("highlightClass", "intro-highlight")
.start()
}
let hinted = localStorage.getItem('hinted')
if(!hinted) {
localStorage.setItem('hinted', true)
startTutorial()
}
刚上手时,发现高亮的地方不透明。我想要的是透明效果,所以查了 stackoverflow,找到以下解法
/*
* @filename: intro-theme.css
*/
.intro-highlight {
background: transparent;
}
.intro-highlight:before {
opacity: 0;
content: '';
position: fixed;
width: inherit;
height: inherit;
border-radius: 0.5em;
box-shadow: 0 0 0 1000em rgba(0,0,0, .7);
opacity: 1;
}
.intro-highlight:after {
content: '';
left: 0;
right: 0;
top: 0;
bottom: 0;
position: fixed;
z-index: 1000;
}
这样就 ok 了
给我帮助的链接
三. 撤销功能的实现
思想是用一个 command 数组 push 用户每次操作,需要撤销时将画布清空,按照记录直到最后一步之前重新执行一次。
关键代码
/*
* @filename: recorder.js
*/
let commandStack = []
let undoing = false
// 实现撤销功能的函数
function undo() {
commandStack.pop()
undoing = true
redraw()
undoing = false
}
// fn, args 分别是用户单步操作调用的 api 函数名和传入参数
function addCommand(fn, args) {
// 注意这里应该用深拷贝,否则会踩对象引用的坑
args = JSON.parse(JSON.stringify([...args]))
// 这里用 undoing,如果没有它会使数组无限增长而导致 redraw 2-5 行无限循环
!undoing && commandStack.push({fn, args})
}
function redraw() {
clearCanvas()
for(let i=0; i<commandStack.length; i++) {
let {fn, args} = commandStack[i]
fn.apply(null, args)
}
}
同时,在用户每个单步操作调用的 api 上楔入 addCommand 函数
function drawLine([p1, p2, p3, p4], penWidth, penColor) {
// 记录用户动作
addCommand(drawLine, arguments)
// 原代码
}
function eraseCanvas({x, y}, size) {
// 记录用户动作
addCommand(wipeCanvas, arguments)
// 原代码
}
撤销操作的本质是:先写一个重现操作,画布清空后将以前的操作重做一遍,只不过这次在最后一步之前停手
其实写到这里我想到还有两个优化点。
- undo 应该做成一个防抖函数。按住 ctrl + Z,触发画板分别重做以前的 9999 步,9998 步,9997, 9996 步操作,不如过段时间再触发,画板只需重做以前 9996 步操作
- 而且这里可以进一步实现 redo。给定一个下标 i,每次 undo 的时候下标左移,redo 的时候下标右移。改为从 0 开始实现到下标 i 就可以了。这样一来放东西时不能用 push,改用 command[i+1] = {fn, args}
暂无评论