├── .idea ├── drawingboard.iml ├── inspectionProfiles │ ├── Project_Default.xml │ └── profiles_settings.xml ├── misc.xml ├── modules.xml ├── vcs.xml ├── watcherTasks.xml └── workspace.xml ├── README.md ├── css └── style.css ├── index.html └── js └── main.js /.idea/drawingboard.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/Project_Default.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/inspectionProfiles/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/watcherTasks.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | undo 61 | eraser 62 | clear 63 | push 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | true 87 | DEFINITION_ORDER 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 1520428879010 149 | 150 | 151 | 1520428879010 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 312 | 313 | 314 | 315 | 316 | 317 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 前些天学习了`HTML5`的``元素,今天就来实践一下,用`canvas`做一个画板。 2 | 3 | 首先说一下要实现的功能: 4 | - 切换画笔颜色 5 | - 调整笔刷粗细 6 | - 清空画布 7 | - 橡皮擦擦除 8 | - 撤销操作 9 | - 保存成图片 10 | - 兼容移动端(支持触摸) 11 | 12 | 好了,废话少说,先看最终效果:[https://hiseanchow.github.io/drawingborad](https://hiseanchow.github.io/drawingborad) 13 | 14 | ### 准备工作 15 | 首先,准备个容器,也就是画板了。 16 | ``` HTML 17 | 18 | ``` 19 | 然后初始化js: 20 | ```JavaScript 21 | let canvas = document.getElementById("drawing-board"); 22 | let ctx = canvas.getContext("2d"); 23 | ``` 24 | 我想把画板做成全屏的,所以接下来设置一下`canvas`的宽高。 25 | ```JavaScript 26 | let pageWidth = document.documentElement.clientWidth; 27 | let pageHeight = document.documentElement.clientHeight; 28 | 29 | canvas.width = pageWidth; 30 | canvas.height = pageHeight; 31 | ``` 32 | 由于部分IE不支持`canvas`,如果要兼容IE,我们可以创建一个`canvas`,然后使用`excanvas`初始化,针对IE加上[exCanvas.js](http://code.google.com/p/explorercanvas/),这里我们暂时先不考虑IE。 33 | 34 | ### 实现一个简单的画板 35 | 实现思路:监听鼠标事件,用`drawCircle()`方法把记录的数据画出来。 36 | 37 | 1. 设置初始化当前画布功能为画笔状态,`painting = false`, 38 | 2. 当鼠标按下时(`mousedown`),把`painting`设为`true`,表示正在画,鼠标没松开。把鼠标点记录下来。 39 | 3. 当按下鼠标的时候,鼠标移动(`mousemove`)就把点记录下来并画出来。 40 | 4. 如果鼠标移动过快,浏览器跟不上绘画速度,点与点之间会产品间隙,所以我们需要将画出的点用线连起来(`lineTo()`)。 41 | 5. 鼠标松开的时候(`mouseup`),把`painting`设为`false`。 42 | 43 | 代码: 44 | ```JavaScript 45 | let painting = false; 46 | let lastPoint = {x: undefined, y: undefined}; 47 | 48 | //鼠标按下事件 49 | canvas.onmousedown = function (e) { 50 | painting = true; 51 | let x = e.clientX; 52 | let y = e.clientY; 53 | lastPoint = {"x": x, "y": y}; 54 | drawCircle(x, y, 5); 55 | }; 56 | 57 | //鼠标移动事件 58 | canvas.onmousemove = function (e) { 59 | if (painting) { 60 | let x = e.clientX; 61 | let y = e.clientY; 62 | let newPoint = {"x": x, "y": y}; 63 | drawLine(lastPoint.x, lastPoint.y, newPoint.x, newPoint.y,clear); 64 | lastPoint = newPoint; 65 | } 66 | }; 67 | 68 | //鼠标松开事件 69 | canvas.onmouseup = function () { 70 | painting = false; 71 | } 72 | 73 | // 画点函数 74 | function drawCircle(x, y, radius) { 75 | ctx.save(); 76 | ctx.beginPath(); 77 | ctx.arc(x, y, radius, 0, Math.PI * 2); 78 | ctx.fill(); 79 | } 80 | 81 | // 划线函数 82 | function drawLine(x1, y1, x2, y2) { 83 | ctx.lineWidth = 3; 84 | ctx.lineCap = "round"; 85 | ctx.lineJoin = "round"; 86 | ctx.moveTo(x1, y1); 87 | ctx.lineTo(x2, y2); 88 | ctx.stroke(); 89 | ctx.closePath(); 90 | } 91 | ``` 92 | 93 | ### 橡皮擦功能 94 | 实现思路 95 | 1. 获取橡皮擦元素 96 | 2. 设置橡皮擦初始状态,`clear = false`。 97 | 3. 监听橡皮擦`click`事件,点击橡皮擦,改变橡皮擦状态,`clear = true`。 98 | 4. `clear`为`true`时,移动鼠标使用`canvas`剪裁(`clip()`)擦除画布。 99 | 100 | ```JavaScript 101 | let eraser = document.getElementById("eraser"); 102 | let clear = false; 103 | 104 | eraser.onclick = function () { 105 | clear = true; 106 | }; 107 | 108 | ... 109 | if (clear) { 110 | ctx.save(); 111 | ctx.globalCompositeOperation = "destination-out"; 112 | ctx.stroke(); 113 | ctx.closePath(); 114 | ctx.clip(); 115 | ctx.clearRect(0,0,canvas.width,canvas.height); 116 | ctx.restore(); 117 | } 118 | ... 119 | ``` 120 | 注意,在`canvas`中的裁剪和平时的裁剪功能不一样在这里,裁剪是指在裁剪区域去显示我们所画的图 121 | 122 | ### 兼容移动端 123 | 实现思路: 124 | 1. 判断设备是否支持触摸 125 | 2. `true`,则使用`touch`事件;`false`,则使用`mouse`事件 126 | 127 | 代码: 128 | ```JavaScript 129 | ... 130 | if (document.body.ontouchstart !== undefined) { 131 | // 使用touch事件 132 | anvas.ontouchstart = function (e) { 133 | // 开始触摸 134 | } 135 | canvas.ontouchmove = function (e) { 136 | // 开始滑动 137 | } 138 | canvas.ontouchend = function () { 139 | // 滑动结束 140 | } 141 | }else{ 142 | // 使用mouse事件 143 | ... 144 | } 145 | ... 146 | ``` 147 | 这里需要注意的一点是,在`touch`事件里,是通过`.touches[0].clientX`和`.touches[0].clientY`来获取坐标的,这点要和`mouse`事件区别开。 148 | 149 | ### 切换画笔颜色 150 | 实现思路: 151 | 1. 获取颜色元素节点。 152 | 2. 点击颜色返回其颜色值,并赋给画布的`ctx.strokeStyle`。 153 | 154 | 代码: 155 | ```JavaScript 156 | let aColorBtn = document.getElementsByClassName("color-item"); 157 | 158 | for (let i = 0; i < aColorBtn.length; i++) { 159 | aColorBtn[i].onclick = function () { 160 | for (let i = 0; i < aColorBtn.length; i++) { 161 | activeColor = this.style.backgroundColor; 162 | ctx.strokeStyle = activeColor; 163 | } 164 | } 165 | ``` 166 | 167 | ### 清空画布 168 | 实现思路: 169 | 1. 获取元素节点。 170 | 2. 点击清空按钮清空canvas画布。 171 | 172 | 代码: 173 | ```JavsScript 174 | let reSetCanvas = document.getElementById("clear"); 175 | 176 | reSetCanvas.onclick = function () { 177 | ctx.clearRect(0, 0, canvas.width, canvas.height); 178 | }; 179 | ``` 180 | 181 | ### 调整笔刷粗细 182 | 实现思路: 183 | 1. 创建input[type=range] 184 | 2. 滑动range获取其value值,并赋给`ctx.lineWidth` 185 | 186 | 代码: 187 | ```JavaScript 188 | let range = document.getElementById("range"); 189 | 190 | range.onchange = function(){ 191 | lWidth = this.value; 192 | }; 193 | ``` 194 | 195 | ### 保存成图片 196 | 实现思路: 197 | 1. 获取canvas.toDateURL 198 | 2. 在页面里创建并插入一个a标签 199 | 3. a标签href等于canvas.toDateURL,并添加download属性 200 | 4. 点击保存按钮,a标签触发click事件 201 | 202 | 代码: 203 | ```JavaScript 204 | let save = document.getElementById("save"); 205 | 206 | save.onclick = function () { 207 | let imgUrl = canvas.toDataURL("image/png"); 208 | let saveA = document.createElement("a"); 209 | document.body.appendChild(saveA); 210 | saveA.href = imgUrl; 211 | saveA.download = "zspic" + (new Date).getTime(); 212 | saveA.target = "_blank"; 213 | saveA.click(); 214 | }; 215 | ``` 216 | 217 | ### 撤销 218 | 实现思路: 219 | 1. 准备一个数组记录历史操作 220 | 2. 每次使用画笔前将当前绘图表面储存进数组 221 | 3. 点击撤销时,恢复到上一步的绘图表面 222 | 223 | 代码: 224 | 225 | ``` 226 | canvas.ontouchstart = function (e) { 227 | // 在这里储存绘图表面 228 | this.firstDot = ctx.getImageData(0, 0, canvas.width, canvas.height); 229 | saveData(this.firstDot); 230 | ... 231 | } 232 | 233 | let undo = document.getElementById("undo"); 234 | let historyDeta = []; 235 | 236 | function saveData (data) { 237 | (historyDeta.length === 10) && (historyDeta.shift()); // 上限为储存10步,太多了怕挂掉 238 | historyDeta.push(data); 239 | } 240 | undo.onclick = function(){ 241 | if(historyDeta.length < 1) return false; 242 | ctx.putImageData(historyDeta[historyDeta.length - 1], 0, 0); 243 | historyDeta.pop() 244 | }; 245 | ``` 246 | 247 | 因为每次储存都是将一张图片存入内存,对性能影响较大,所以在这里设置了储存上限。 248 | 249 | ### 总结 250 | 这里用的知识点主要有:监听`mouse`、`touch`事件,以及`canvas`的`moveTo()`、`lineTo()`、`stroke()`、`clip()`、`clearRect()`等方法。我相信还有很多效果可以实现,比如说类似喷雾效果,铅笔字效果,艺术画效果,等等。日后有时间我会继续对这个画板进行优化,增加一些新的功能同时欢迎大家留言提问或者提出批评建议。 251 | 252 | -------------------------------------------------------------------------------- /css/style.css: -------------------------------------------------------------------------------- 1 | *{margin:0; padding: 0;user-select: none;} 2 | body{overflow:hidden} 3 | #drawing-board{background: white;position:fixed; display: block;cursor: crosshair;} 4 | .tools{position: fixed;left:0;bottom: 30px; width:100%;display: flex;justify-content: center;text-align: center} 5 | .tools button{border-radius: 50%;width: 50px;height: 50px; background-color: rgba(255,255,255,0.7);border: 1px solid #eee;outline: none;cursor: pointer;box-sizing: border-box;margin: 0 10px;text-align: center;color:#ccc;line-height: 50px;box-shadow:0 0 8px rgba(0,0,0,0.1); transition: 0.3s;} 6 | .tools button.active,.tools button:active{box-shadow: 0 0 15px #00CCFF; color:#00CCFF;} 7 | .tools button i{font-size: 24px;} 8 | .color-group{position:fixed;width: 30px;left: 30px;top:50%;transform: translate(0,-150px)} 9 | .color-group ul{list-style: none;} 10 | .color-group ul li{width: 30px;height: 30px;margin: 10px 0;border-radius: 50%;box-sizing: border-box;border:3px solid white;box-shadow: 0 0 8px rgba(0,0,0,0.2);cursor: pointer;transition: 0.3s;} 11 | .color-group ul li.active{box-shadow:0 0 15px #00CCFF;} 12 | #range-wrap{position: fixed;top: 50%;right:30px;width: 30px;height: 150px;margin-top: -75px;} 13 | #range-wrap input{transform: rotate(-90deg);width: 150px;height: 20px;margin: 0;transform-origin: 75px 75px; border-radius: 15px;-webkit-appearance: none;outline: none;position: relative;} 14 | #range-wrap input::after{display: block;content:"";width:0;height: 0;border:5px solid transparent; 15 | border-right:150px solid #00CCFF;border-left-width:0;position: absolute;left: 0;top: 5px;border-radius:15px; z-index: 0; } 16 | #range-wrap input[type=range]::-webkit-slider-thumb,#range-wrap input[type=range]::-moz-range-thumb{-webkit-appearance: none;} 17 | #range-wrap input[type=range]::-webkit-slider-runnable-track,#range-wrap input[type=range]::-moz-range-track {height: 10px;border-radius: 10px;box-shadow: none;} 18 | #range-wrap input[type=range]::-webkit-slider-thumb{-webkit-appearance: none;height: 20px;width: 20px;margin-top: -1px;background: #ffffff;border-radius: 50%;box-shadow: 0 0 8px #00CCFF;position: relative;z-index: 999;} 19 | 20 | @media screen and (max-width: 768px) { 21 | .tools{bottom:auto;top:20px;} 22 | .tools button{width: 35px;height: 35px;line-height: 35px;margin-bottom: 15px;box-shadow:0 0 5px rgba(0,0,0,0.1);} 23 | .tools button.active,.tools button:active{box-shadow: 0 0 5px #00CCFF;} 24 | .tools button i{font-size: 18px;} 25 | .tools #swatches{display: none} 26 | .color-group{left: 0;top:auto;bottom: 20px;display: flex;width:100%;justify-content: center;text-align: center;transform: translate(0,0)} 27 | .color-group ul li{display: inline-block;margin:0 5px;} 28 | .color-group ul li.active{box-shadow:0 0 10px #00CCFF;} 29 | #range-wrap{right:auto;left: 20px;} 30 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 7 | 8 | 涂鸦板 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /js/main.js: -------------------------------------------------------------------------------- 1 | let canvas = document.getElementById("drawing-board"); 2 | let ctx = canvas.getContext("2d"); 3 | let eraser = document.getElementById("eraser"); 4 | let brush = document.getElementById("brush"); 5 | let reSetCanvas = document.getElementById("clear"); 6 | let aColorBtn = document.getElementsByClassName("color-item"); 7 | let save = document.getElementById("save"); 8 | let undo = document.getElementById("undo"); 9 | let range = document.getElementById("range"); 10 | let clear = false; 11 | let activeColor = 'black'; 12 | let lWidth = 4; 13 | 14 | autoSetSize(canvas); 15 | 16 | setCanvasBg('white'); 17 | 18 | listenToUser(canvas); 19 | 20 | getColor(); 21 | 22 | window.onbeforeunload = function(){ 23 | return "Reload site?"; 24 | }; 25 | 26 | function autoSetSize(canvas) { 27 | canvasSetSize(); 28 | 29 | function canvasSetSize() { 30 | let pageWidth = document.documentElement.clientWidth; 31 | let pageHeight = document.documentElement.clientHeight; 32 | 33 | canvas.width = pageWidth; 34 | canvas.height = pageHeight; 35 | } 36 | 37 | window.onresize = function () { 38 | canvasSetSize(); 39 | } 40 | } 41 | 42 | function setCanvasBg(color) { 43 | ctx.fillStyle = color; 44 | ctx.fillRect(0, 0, canvas.width, canvas.height); 45 | ctx.fillStyle = "black"; 46 | } 47 | 48 | function listenToUser(canvas) { 49 | let painting = false; 50 | let lastPoint = {x: undefined, y: undefined}; 51 | 52 | if (document.body.ontouchstart !== undefined) { 53 | canvas.ontouchstart = function (e) { 54 | this.firstDot = ctx.getImageData(0, 0, canvas.width, canvas.height);//在这里储存绘图表面 55 | saveData(this.firstDot); 56 | painting = true; 57 | let x = e.touches[0].clientX; 58 | let y = e.touches[0].clientY; 59 | lastPoint = {"x": x, "y": y}; 60 | ctx.save(); 61 | drawCircle(x, y, 0); 62 | }; 63 | canvas.ontouchmove = function (e) { 64 | if (painting) { 65 | let x = e.touches[0].clientX; 66 | let y = e.touches[0].clientY; 67 | let newPoint = {"x": x, "y": y}; 68 | drawLine(lastPoint.x, lastPoint.y, newPoint.x, newPoint.y); 69 | lastPoint = newPoint; 70 | } 71 | }; 72 | 73 | canvas.ontouchend = function () { 74 | painting = false; 75 | } 76 | } else { 77 | canvas.onmousedown = function (e) { 78 | this.firstDot = ctx.getImageData(0, 0, canvas.width, canvas.height);//在这里储存绘图表面 79 | saveData(this.firstDot); 80 | painting = true; 81 | let x = e.clientX; 82 | let y = e.clientY; 83 | lastPoint = {"x": x, "y": y}; 84 | ctx.save(); 85 | drawCircle(x, y, 0); 86 | }; 87 | canvas.onmousemove = function (e) { 88 | if (painting) { 89 | let x = e.clientX; 90 | let y = e.clientY; 91 | let newPoint = {"x": x, "y": y}; 92 | drawLine(lastPoint.x, lastPoint.y, newPoint.x, newPoint.y,clear); 93 | lastPoint = newPoint; 94 | } 95 | }; 96 | 97 | canvas.onmouseup = function () { 98 | painting = false; 99 | }; 100 | 101 | canvas.mouseleave = function () { 102 | painting = false; 103 | } 104 | } 105 | } 106 | 107 | function drawCircle(x, y, radius) { 108 | ctx.save(); 109 | ctx.beginPath(); 110 | ctx.arc(x, y, radius, 0, Math.PI * 2); 111 | ctx.fill(); 112 | if (clear) { 113 | ctx.clip(); 114 | ctx.clearRect(0,0,canvas.width,canvas.height); 115 | ctx.restore(); 116 | } 117 | } 118 | 119 | function drawLine(x1, y1, x2, y2) { 120 | ctx.lineWidth = lWidth; 121 | ctx.lineCap = "round"; 122 | ctx.lineJoin = "round"; 123 | if (clear) { 124 | ctx.save(); 125 | ctx.globalCompositeOperation = "destination-out"; 126 | ctx.moveTo(x1, y1); 127 | ctx.lineTo(x2, y2); 128 | ctx.stroke(); 129 | ctx.closePath(); 130 | ctx.clip(); 131 | ctx.clearRect(0,0,canvas.width,canvas.height); 132 | ctx.restore(); 133 | }else{ 134 | ctx.moveTo(x1, y1); 135 | ctx.lineTo(x2, y2); 136 | ctx.stroke(); 137 | ctx.closePath(); 138 | } 139 | } 140 | 141 | range.onchange = function(){ 142 | lWidth = this.value; 143 | }; 144 | 145 | eraser.onclick = function () { 146 | clear = true; 147 | this.classList.add("active"); 148 | brush.classList.remove("active"); 149 | }; 150 | 151 | brush.onclick = function () { 152 | clear = false; 153 | this.classList.add("active"); 154 | eraser.classList.remove("active"); 155 | }; 156 | 157 | reSetCanvas.onclick = function () { 158 | ctx.clearRect(0, 0, canvas.width, canvas.height); 159 | setCanvasBg('white'); 160 | }; 161 | 162 | save.onclick = function () { 163 | let imgUrl = canvas.toDataURL("image/png"); 164 | let saveA = document.createElement("a"); 165 | document.body.appendChild(saveA); 166 | saveA.href = imgUrl; 167 | saveA.download = "zspic" + (new Date).getTime(); 168 | saveA.target = "_blank"; 169 | saveA.click(); 170 | }; 171 | 172 | function getColor(){ 173 | for (let i = 0; i < aColorBtn.length; i++) { 174 | aColorBtn[i].onclick = function () { 175 | for (let i = 0; i < aColorBtn.length; i++) { 176 | aColorBtn[i].classList.remove("active"); 177 | this.classList.add("active"); 178 | activeColor = this.style.backgroundColor; 179 | ctx.fillStyle = activeColor; 180 | ctx.strokeStyle = activeColor; 181 | } 182 | } 183 | } 184 | } 185 | 186 | let historyDeta = []; 187 | 188 | function saveData (data) { 189 | (historyDeta.length === 10) && (historyDeta.shift());// 上限为储存10步,太多了怕挂掉 190 | historyDeta.push(data); 191 | } 192 | 193 | undo.onclick = function(){ 194 | if(historyDeta.length < 1) return false; 195 | ctx.putImageData(historyDeta[historyDeta.length - 1], 0, 0); 196 | historyDeta.pop() 197 | }; --------------------------------------------------------------------------------