繁体   English   中英

如何使用 JavaScript 为 HTML canvas 实现撤消 function?

[英]How to implement undo function for HTML canvas using JavaScript?

我正在使用 JavaScript 和 Flask-Python 在 HTML 制作一个绘画应用程序。 目前,我可以毫无问题地绘制许多铅笔画和矩形/圆形等形状。 我试图为此应用程序实现的以下功能是撤消function。

我按以下方式将笔画和 canvas 绘图数据存储在 JS object 中:

canvas_data = { "pencil": [], "line": [], "rectangle": [], "circle": [], "eraser": [], "last_action": -1 };

除了last_action之外,所有键名都应该是不言自明的。 我使用这个last_action变量来了解用户上次使用的类别,以便我以后可以使用此信息来实现撤消 function。

 var canvas = document.getElementById("paint"); var ctx = canvas.getContext("2d"); var pi2 = Math.PI * 2; var resizerRadius = 8; var rr = resizerRadius * resizerRadius; var width = canvas.width; var height = canvas.height; var curX, curY, prevX, prevY; var hold = false; ctx.lineWidth = 2; var fill_value = true; var stroke_value = false; var canvas_data = { "pencil": [], "line": [], "rectangle": [], "circle": [], "eraser": [], "last_action": -1 }; // //connect to postgres client // var pg = require('pg'); // var conString = "postgres://postgres:database1@localhost:5432/sketch2photo"; // client = new pg.Client(conString); function color(color_value) { ctx.strokeStyle = color_value; ctx.fillStyle = color_value; } function add_pixel() { ctx.lineWidth += 1; } function reduce_pixel() { if (ctx.lineWidth == 1) { ctx.lineWidth = 1; } else { ctx.lineWidth -= 1; } } function fill() { fill_value = true; stroke_value = false; } function outline() { fill_value = false; stroke_value = true; } function reset() { ctx.clearRect(0, 0, canvas.width, canvas.height); canvas_data = { "pencil": [], "line": [], "rectangle": [], "circle": [], "eraser": [], "last_action": -1 }; } // pencil tool function pencil(data, targetX, targetY, targetWidth, targetHeight) { canvas.onmousedown = function(e) { curX = e.clientX - canvas.offsetLeft; curY = e.clientY - canvas.offsetTop; hold = true; prevX = curX; prevY = curY; ctx.beginPath(); ctx.moveTo(prevX, prevY); }; canvas.onmousemove = function(e) { if (hold) { curX = e.clientX - canvas.offsetLeft; curY = e.clientY - canvas.offsetTop; draw(); } }; canvas.onmouseup = function(e) { hold = false; }; canvas.onmouseout = function(e) { hold = false; }; function draw() { ctx.lineTo(curX, curY); ctx.stroke(); canvas_data.pencil.push({ "startx": prevX, "starty": prevY, "endx": curX, "endy": curY, "thick": ctx.lineWidth, "color": ctx.strokeStyle }); canvas_data.last_action = 0; } } // line tool function line() { canvas.onmousedown = function(e) { img = ctx.getImageData(0, 0, width, height); prevX = e.clientX - canvas.offsetLeft; prevY = e.clientY - canvas.offsetTop; hold = true; }; canvas.onmousemove = function linemove(e) { if (hold) { ctx.putImageData(img, 0, 0); curX = e.clientX - canvas.offsetLeft; curY = e.clientY - canvas.offsetTop; ctx.beginPath(); ctx.moveTo(prevX, prevY); ctx.lineTo(curX, curY); ctx.stroke(); canvas_data.line.push({ "startx": prevX, "starty": prevY, "endx": curX, "endY": curY, "thick": ctx.lineWidth, "color": ctx.strokeStyle }); ctx.closePath(); canvas_data.last_action = 1; } }; canvas.onmouseup = function(e) { hold = false; }; canvas.onmouseout = function(e) { hold = false; }; } // rectangle tool function rectangle() { canvas.onmousedown = function(e) { img = ctx.getImageData(0, 0, width, height); prevX = e.clientX - canvas.offsetLeft; prevY = e.clientY - canvas.offsetTop; hold = true; }; canvas.onmousemove = function(e) { if (hold) { ctx.putImageData(img, 0, 0); curX = e.clientX - canvas.offsetLeft - prevX; curY = e.clientY - canvas.offsetTop - prevY; ctx.strokeRect(prevX, prevY, curX, curY); if (fill_value) { ctx.fillRect(prevX, prevY, curX, curY); } canvas_data.rectangle.push({ "startx": prevX, "starty": prevY, "width": curX, "height": curY, "thick": ctx.lineWidth, "stroke": stroke_value, "stroke_color": ctx.strokeStyle, "fill": fill_value, "fill_color": ctx.fillStyle }); canvas_data.last_action = 2; } }; canvas.onmouseup = function(e) { hold = false; }; canvas.onmouseout = function(e) { hold = false; }; } // circle tool function circle() { canvas.onmousedown = function(e) { img = ctx.getImageData(0, 0, width, height); prevX = e.clientX - canvas.offsetLeft; prevY = e.clientY - canvas.offsetTop; hold = true; }; canvas.onmousemove = function(e) { if (hold) { ctx.putImageData(img, 0, 0); curX = e.clientX - canvas.offsetLeft; curY = e.clientY - canvas.offsetTop; ctx.beginPath(); ctx.arc(Math.abs(curX + prevX) / 2, Math.abs(curY + prevY) / 2, Math.sqrt(Math.pow(curX - prevX, 2) + Math.pow(curY - prevY, 2)) / 2, 0, Math.PI * 2, true); ctx.closePath(); ctx.stroke(); if (fill_value) { ctx.fill(); } canvas_data.circle.push({ "startx": prevX, "starty": prevY, "radius": curX - prevX, "thick": ctx.lineWidth, "stroke": stroke_value, "stroke_color": ctx.strokeStyle, "fill": fill_value, "fill_color": ctx.fillStyle }); canvas_data.last_action = 3; } }; canvas.onmouseup = function(e) { hold = false; }; canvas.onmouseout = function(e) { hold = false; }; } // eraser tool function eraser() { canvas.onmousedown = function(e) { curX = e.clientX - canvas.offsetLeft; curY = e.clientY - canvas.offsetTop; hold = true; prevX = curX; prevY = curY; ctx.beginPath(); ctx.moveTo(prevX, prevY); }; canvas.onmousemove = function(e) { if (hold) { curX = e.clientX - canvas.offsetLeft; curY = e.clientY - canvas.offsetTop; draw(); } }; canvas.onmouseup = function(e) { hold = false; }; canvas.onmouseout = function(e) { hold = false; }; function draw() { ctx.lineTo(curX, curY); var curr_strokeStyle = ctx.strokeStyle; ctx.strokeStyle = "#ffffff"; ctx.stroke(); canvas_data.pencil.push({ "startx": prevX, "starty": prevY, "endx": curX, "endy": curY, "thick": ctx.lineWidth, "color": ctx.strokeStyle }); canvas_data.last_action = 4; ctx.strokeStyle = curr_strokeStyle; } } // Function to undo the last action by the user function undo_pixel() { // Print that function has been called console.log("undo_pixel() called"); // Print the last action that was performed console.log(canvas_data.last_action); switch (canvas_data.last_action) { case 0: case 4: console.log("Case 0 or 4"); canvas_data.pencil.pop(); canvas_data.last_action = -1; break; case 1: //Undo the last line drawn console.log("Case 1"); canvas_data.line.pop(); canvas_data.last_action = -1; break; case 2: //Undo the last rectangle drawn console.log("Case 2"); canvas_data.rectangle.pop(); canvas_data.last_action = -1; break; case 3: //Undo the last circle drawn console.log("Case 3"); canvas_data.circle.pop(); canvas_data.last_action = -1; break; default: break; } // Redraw the canvas redraw_canvas(); } // Function to redraw all the shapes on the canvas function redraw_canvas() { // Redraw all the shapes on the canvas ctx.clearRect(0, 0, canvas.width, canvas.height); // Redraw the pencil data canvas_data.pencil.forEach(function(p) { ctx.beginPath(); ctx.moveTo(p.startx, p.starty); ctx.lineTo(p.endx, p.endy); ctx.lineWidth = p.thick; ctx.strokeStyle = p.color; ctx.stroke(); }); // Redraw the line data canvas_data.line.forEach(function(l) { ctx.beginPath(); ctx.moveTo(l.startx, l.starty); ctx.lineTo(l.endx, l.endy); ctx.lineWidth = l.thick; ctx.strokeStyle = l.color; ctx.stroke(); }); // Redraw the rectangle data canvas_data.rectangle.forEach(function(r) { ctx.beginPath(); ctx.rect(r.startx, r.starty, r.width, r.height); ctx.lineWidth = r.thick; ctx.strokeStyle = r.color; if (r.fill) { ctx.fillStyle = r.fill_color; ctx.fillRect(startx, starty, width, height); } ctx.stroke(); }); // Redraw the circle data canvas_data.circle.forEach(function(c) { // "startx": prevX, "starty": prevY, "radius": curX - prevX, "thick": ctx.lineWidth, "stroke": stroke_value, "stroke_color": ctx.strokeStyle, "fill": fill_value, "fill_color": ctx.fillStyle ctx.beginPath(); ctx.arc(c.startx, c.starty, c.radius, 0, 2 * Math.PI); ctx.closePath(); ctx.stroke(); if (c.fill) { ctx.fillStyle = c.fill_color; ctx.fill(); } }); } $("#paint1").mousedown(function(e) { handleMouseDown(e); }); $("#paint1").mouseup(function(e) { handleMouseUp(e); }); $("#paint1").mouseout(function(e) { handleMouseOut(e); }); $("#paint1").mousemove(function(e) { handleMouseMove(e); });
 html { min-width: 1500px; position: relative; } #toolset { width: 100px; height: 340px; position: absolute; left: 0px; top: 50px; background: #35d128; } #paint { position: absolute; left: 130px; top: 50px; } #colorset { position: absolute; left: 0px; top: 450px; width: 300px; } #title { position: absolute; left: 500px; } #penciltool { background: #358128; color: #f3f3f3; width: 80px; height: 25px; border: 1px solid #33842a; -webkit-border-radius: 0 15px 15px 0; -moz-border-radius: 0 15px 15px 0; box-shadow: rgba(0, 0, 0, .75) 0 2px 6px; } #l.netool { background: #358128; color: #f3f3f3; width: 80px; height: 25px; border: 1px solid #33842a; -webkit-border-radius: 0 15px 15px 0; -moz-border-radius: 0 15px 15px 0; box-shadow: rgba(0, 0, 0, .75) 0 2px 6px; } #rectangletool { background: #358128; color: #f3f3f3; width: 80px; height: 25px; border: 1px solid #33842a; -webkit-border-radius: 0 15px 15px 0; -moz-border-radius: 0 15px 15px 0; box-shadow: rgba(0, 0, 0, .75) 0 2px 6px; } #circletool { background: #358128; color: #f3f3f3; width: 80px; height: 25px; border: 1px solid #33842a; -webkit-border-radius: 0 15px 15px 0; -moz-border-radius: 0 15px 15px 0; box-shadow: rgba(0, 0, 0, .75) 0 2px 6px; } #erasertool { background: #358128; color: #f3f3f3; width: 80px; height: 25px; border: 1px solid #33842a; -webkit-border-radius: 0 15px 15px 0; -moz-border-radius: 0 15px 15px 0; box-shadow: rgba(0, 0, 0, .75) 0 2px 6px; } #resettool { background: #358128; color: #f3f3f3; width: 80px; height: 25px; border: 1px solid #33842a; -webkit-border-radius: 0 15px 15px 0; -moz-border-radius: 0 15px 15px 0; box-shadow: rgba(0, 0, 0, .75) 0 2px 6px; }
 <html> <head> <title>Paint App</title> </head> <body> <p style="text-align:left; font: bold 35px/35px Georgia, serif;"> PaintApp <div align="right"> <link rel="stylesheet" type="text/css" href="style.css"> <body onload="pencil(`{{ data }}`, `{{ targetx }}`, `{{ targety }}`, `{{ sizex }}`, `{{ sizey }}`)"> <p> <table> <tr> <td> <fieldset id="toolset" style="margin-top: 3%;"> <br> <br> <button id="penciltool" type="button" style="height: 15px; width: 100px;" onclick="pencil()">Pencil</button> <br> <br> <br> <button id="l.netool" type="button" style="height: 15px; width: 100px;" onclick="line()">Line</button> <br> <br> <br> <button id="rectangletool" type="button" style="height: 15px; width: 100px;" onclick="rectangle()">Rectangle</button> <br> <br> <br> <button id="circletool" type="button" style="height: 15px; width: 100px;" onclick="circle()">Circle</button> <br> <br> <br> <button id="erasertool" type="button" style="height: 15px; width: 100px;" onclick="eraser()">Eraser</button> <br> <br> <br> <button id="resettool" type="button" style="height: 15px; width: 100px;" onclick="reset()">Reset</button> </fieldset> </td> <td> <canvas id="paint" width="500vw" height="350vw" style="border: 5px solid #000000; margin-top: 3%;"></canvas> </td> </tr> </table> </p> <fieldset id="colorset" style="margin-top: 1.8%;"> <table> <tr> <td><button style="height: 15px; width: 80px;" onclick="fill()">Fill</button> <td><button style="background-color: #000000; height: 15px; width: 15px;" onclick="color('#000000')"></button> <td><button style="background-color: #B0171F; height: 15px; width: 15px;" onclick="color('#B0171F')"></button> <td><button style="background-color: #DA70D6; height: 15px; width: 15px;" onclick="color('#DA70D6')"></button> <td><button style="background-color: #8A2BE2; height: 15px; width: 15px;" onclick="color('#8A2BE2')"></button> <td><button style="background-color: #0000FF; height: 15px; width: 15px;" onclick="color('#0000FF')"></button> <td><button style="background-color: #4876FF; height: 15px; width: 15px;" onclick="color('#4876FF')"></button> <td><button style="background-color: #CAE1FF; height: 15px; width: 15px;" onclick="color('#CAE1FF')"></button> <td><button style="background-color: #6E7B8B; height: 15px; width: 15px;" onclick="color('#6E7B8B')"></button> <td><button style="background-color: #00C78C; height: 15px; width: 15px;" onclick="color('#00C78C')"></button> <td><button style="background-color: #00FA9A; height: 15px; width: 15px;" onclick="color('#00FA9A')"></button> <td><button style="background-color: #00FF7F; height: 15px; width: 15px;" onclick="color('#00FF7F')"></button> <td><button style="background-color: #00C957; height: 15px; width: 15px;" onclick="color('#00C957')"></button> <td><button style="background-color: #FFFF00; height: 15px; width: 15px;" onclick="color('#FFFF00')"></button> <td><button style="background-color: #CDCD00; height: 15px; width: 15px;" onclick="color('#CDCD00')"></button> <td><button style="background-color: #FFF68F; height: 15px; width: 15px;" onclick="color('#FFF68F')"></button> <td><button style="background-color: #FFFACD; height: 15px; width: 15px;" onclick="color('#FFFACD')"></button> <td><button style="background-color: #FFEC8B; height: 15px; width: 15px;" onclick="color('#FFEC8B')"></button> <td><button style="background-color: #FFD700; height: 15px; width: 15px;" onclick="color('#FFD700')"></button> <td><button style="background-color: #F5DEB3; height: 15px; width: 15px;" onclick="color('#F5DEB3')"></button> <td><button style="background-color: #FFE4B5; height: 15px; width: 15px;" onclick="color('#FFE4B5')"></button> <td><button style="background-color: #EECFA1; height: 15px; width: 15px;" onclick="color('#EECFA1')"></button> <td><button style="background-color: #FF9912; height: 15px; width: 15px;" onclick="color('#FF9912')"></button> <td><button style="background-color: #8E388E; height: 15px; width: 15px;" onclick="color('#8E388E')"></button> <td><button style="background-color: #7171C6; height: 15px; width: 15px;" onclick="color('#7171C6')"></button> <td><button style="background-color: #7D9EC0; height: 15px; width: 15px;" onclick="color('#7D9EC0')"></button> <td><button style="background-color: #388E8E; height: 15px; width: 15px;" onclick="color('#388E8E')"></button> </tr> <tr> <td><button style="height: 15px; width: 80px" onclick="outline()">Outline</button> <td><button style="background-color: #71C671; height: 15px; width: 15px;" onclick="color('#71C671')"></button> <td><button style="background-color: #8E8E38; height: 15px; width: 15px;" onclick="color('#8E8E38')"></button> <td><button style="background-color: #C5C1AA; height: 15px; width: 15px;" onclick="color('#C5C1AA')"></button> <td><button style="background-color: #C67171; height: 15px; width: 15px;" onclick="color('#C67171')"></button> <td><button style="background-color: #555555; height: 15px; width: 15px;" onclick="color('#555555')"></button> <td><button style="background-color: #848484; height: 15px; width: 15px;" onclick="color('#848484')"></button> <td><button style="background-color: #F4F4F4; height: 15px; width: 15px;" onclick="color('#F4F4F4')"></button> <td><button style="background-color: #EE0000; height: 15px; width: 15px;" onclick="color('#EE0000')"></button> <td><button style="background-color: #FF4040; height: 15px; width: 15px;" onclick="color('#FF4040')"></button> <td><button style="background-color: #EE6363; height: 15px; width: 15px;" onclick="color('#EE6363')"></button> <td><button style="background-color: #FFC1C1; height: 15px; width: 15px;" onclick="color('#FFC1C1')"></button> <td><button style="background-color: #FF7256; height: 15px; width: 15px;" onclick="color('#FF7256')"></button> <td><button style="background-color: #FF4500; height: 15px; width: 15px;" onclick="color('#FF4500')"></button> <td><button style="background-color: #F4A460; height: 15px; width: 15px;" onclick="color('#F4A460')"></button> <td><button style="background-color: #FF8000; height: 15px; width: 15px;" onclick="color('FF8000')"></button> <td><button style="background-color: #FFD700; height: 15px; width: 15px;" onclick="color('#FFD700')"></button> <td><button style="background-color: #8B864E; height: 15px; width: 15px;" onclick="color('#8B864E')"></button> <td><button style="background-color: #9ACD32; height: 15px; width: 15px;" onclick="color('#9ACD32')"></button> <td><button style="background-color: #66CD00; height: 15px; width: 15px;" onclick="color('#66CD00')"></button> <td><button style="background-color: #BDFCC9; height: 15px; width: 15px;" onclick="color('#BDFCC9')"></button> <td><button style="background-color: #76EEC6; height: 15px; width: 15px;" onclick="color('#76EEC6')"></button> <td><button style="background-color: #40E0D0; height: 15px; width: 15px;" onclick="color('#40E0D0')"></button> <td><button style="background-color: #9B30FF; height: 15px; width: 15px;" onclick="color('#9B30FF')"></button> <td><button style="background-color: #EE82EE; height: 15px; width: 15px;" onclick="color('#EE82EE')"></button> <td><button style="background-color: #FFC0CB; height: 15px; width: 15px;" onclick="color('#FFC0CB')"></button> <td><button style="background-color: #7CFC00; height: 15px; width: 15px;" onclick="color('#7CFC00')"></button> </tr> <tr> <td><label>Line Width</label></td> <td><button id="pixel_plus" type="button" onclick="add_pixel()" style="width: 25px;">+</button></td> <td><button id="pixel_minus" type="button" onclick="reduce_pixel()" style="width: 25px;">-</button></td> <td><button id="undo" type="button" onclick="undo_pixel()" style="width: 75px;">Undo</button></td> </tr> </table> <br> </fieldset> <script src="//code.jquery.com/jquery-1.8.3.js"></script> <script src="script.js"></script> </body> </html>

这是我尝试过的:

  1. 首先,我制作了一个undo_pixel() function,它使用last_action变量弹出上一个动作的相应堆栈中输入的最后一个元素

  2. 然后我使用清除 canvas 的redraw_canvas() function 重新绘制 canvas,然后使用存储在canvas_data object 中的所有数据点重新绘制它。

但这导致了一些我无法完全理解的意外行为。 这是正在发生的事情:

撤消前草图: 在此处输入图像描述

撤消后草图: 在此处输入图像描述

我认为这可能是因为在所有循环的点之间绘制了直线,但我不确定 go 还有什么其他方法。 如何正确实现重绘/撤消 function?

导致您观察到的扇形的直接问题是由于startxstarty对于canvas_data.pencil数组中的每个 object 条目保持相同。 这是因为, startxstarty被分配了prevXprevY中保存的值,但这些值是在mousedown事件侦听器中设置的,并且不会在mousemove事件侦听器中更新。

这是固定的,如下所示:

首先,虽然对于功能来说不是绝对必要的,但是从铅笔 function 中的mousedown事件侦听器中删除对prevXprevY的引用并设置ctx.moveTo(curX, curY) - 这有点不那么令人困惑,因为 mousedown 是绘制开始的地方从:

// inside pencil function;

  canvas.onmousedown = function(e) {
    curX = e.clientX - canvas.offsetLeft;
    curY = e.clientY - canvas.offsetTop;
    hold = true;

    ctx.beginPath();
    ctx.moveTo(curX, curY); // prev -> cur;
  };

接下来,在mousemove侦听器中,添加行以更新prevXprevY

// inside pencil function;

  canvas.onmousemove = function(e) {
    if (hold) {
      curX = e.clientX - canvas.offsetLeft;
      curY = e.clientY - canvas.offsetTop;
      draw();
      prevX = curX; // new
      prevY = curY; // new
    }
  };

这些更改形成了正确的数据 object 数组,每个对象的startxstarty值现在根据需要设置为前一个对象的endxendy值。

然而,还有另一个紧迫的问题: undo_pixel function 仅在第一次单击时完全执行以删除铅笔数组的最后一个 object(导致最后一个mousemove事件捕获的最后一条线按预期消失),但随后的点击(到删除该行的连续部分)被中止。

我假设undo_pixel function 旨在为每次点击删除一条条子而不是整条铅笔线(如果不是,请稍后查看)。

进程中止的原因是因为重置了undo_pixel中的canvas_data.last_action标志:

canvas_data.last_action = -1`

因为-1的 switch 语句中没有case块,所以在随后单击undo按钮时没有任何反应;

相反,铅笔的案例块应该是:

case 4:
canvas_data.pencil.pop();
canvas_data.last_action = 4; // not -1;
break;

您必须将其保留在 4,直到选择另一个操作来撤消或直到整个铅笔线被删除,然后移动到紧接在 lencil 线之前绘制的 object(为此您需要跟踪项目的顺序画)。

我根据上述建议制作了一个工作片段。 您需要将其显示为整页,因为预览 window 太小而无法在您单击撤消按钮时看到该行。 如果你画一个短的铅笔波浪形并反复按下撤销按钮,你会看到这条线被“取消”。

(我不得不删除你的一些其他功能,因为我超出了允许的答案字符限制)

 var canvas = document.getElementById("paint"); var ctx = canvas.getContext("2d"); var pi2 = Math.PI * 2; var resizerRadius = 8; var rr = resizerRadius * resizerRadius; var width = canvas.width; var height = canvas.height; var curX, curY, prevX, prevY; var hold = false; ctx.lineWidth = 2; var fill_value = true; var stroke_value = false; var canvas_data = { "pencil": [], "line": [], "rectangle": [], "circle": [], "eraser": [], "last_action": -1 }; // //connect to postgres client // var pg = require('pg'); // var conString = "postgres://postgres:database1@localhost:5432/sketch2photo"; // client = new pg.Client(conString); function color(color_value) { ctx.strokeStyle = color_value; ctx.fillStyle = color_value; } function add_pixel() { ctx.lineWidth += 1; } function reduce_pixel() { if (ctx.lineWidth == 1) { ctx.lineWidth = 1; } else { ctx.lineWidth -= 1; } } function fill() { fill_value = true; stroke_value = false; } function outline() { fill_value = false; stroke_value = true; } function reset() { ctx.clearRect(0, 0, canvas.width, canvas.height); canvas_data = { "pencil": [], "line": [], "rectangle": [], "circle": [], "eraser": [], "last_action": -1 }; } // pencil tool function pencil(data, targetX, targetY, targetWidth, targetHeight) { //prevX = 0; // new //prevY = 0; // new canvas.onmousedown = function(e) { curX = e.clientX - canvas.offsetLeft; curY = e.clientY - canvas.offsetTop; hold = true; ctx.beginPath(); ctx.moveTo(curX, curY); // prev -> cur; }; canvas.onmousemove = function(e) { if (hold) { curX = e.clientX - canvas.offsetLeft; curY = e.clientY - canvas.offsetTop; draw(); prevX = curX; // new prevY = curY; // new } }; canvas.onmouseup = function(e) { hold = false; }; canvas.onmouseout = function(e) { hold = false; }; function draw() { ctx.lineTo(curX, curY); ctx.stroke(); canvas_data.pencil.push({ "startx": prevX, "starty": prevY, "endx": curX, "endy": curY, "thick": ctx.lineWidth, "color": ctx.strokeStyle }); canvas_data.last_action = 0; } } function undo_pixel() { switch (canvas_data.last_action) { case 0: case 4: canvas_data.pencil.pop(); canvas_data.last_action = 4; // not -1; break; case 1: //Undo the last line drawn console.log("Case 1"); canvas_data.line.pop(); canvas_data.last_action = -1; break; case 2: //Undo the last rectangle drawn console.log("Case 2"); canvas_data.rectangle.pop(); canvas_data.last_action = -1; break; case 3: //Undo the last circle drawn console.log("Case 3"); canvas_data.circle.pop(); canvas_data.last_action = -1; break; default: break; } redraw_canvas(); } function redraw_canvas() { // Redraw all the shapes on the canvas ctx.clearRect(0, 0, canvas.width, canvas.height); // Redraw the pencil data canvas_data.pencil.forEach(function(p) { ctx.beginPath(); ctx.moveTo(p.startx, p.starty); ctx.lineTo(p.endx, p.endy); ctx.lineWidth = p.thick; ctx.strokeStyle = p.color; ctx.stroke(); }); // Redraw the line data canvas_data.line.forEach(function(l) { ctx.beginPath(); ctx.moveTo(l.startx, l.starty); ctx.lineTo(l.endx, l.endy); ctx.lineWidth = l.thick; ctx.strokeStyle = l.color; ctx.stroke(); }); }
 <html> <head> <title>Paint App</title> <script src="main.js" defer></script> <link rel="stylesheet" href="styles.css"> </head> <body> <body> <p style="text-align:left; font: bold 35px/35px Georgia, serif;"> PaintApp </body> </body> <div align="right"> <link rel="stylesheet" type="text/css" href="{{ url_for('static', filename='style.css') }}"> <body onload="pencil(`{{ data }}`, `{{ targetx }}`, `{{ targety }}`, `{{ sizex }}`, `{{ sizey }}`)"> <p> <table> <tr> <td> <fieldset id="toolset" style="margin-top: 3%;"> <br> <br> <button id="penciltool" type="button" style="height: 15px; width: 100px;" onclick="pencil()">Pencil</button> <br> <br> <br> <button id="l.netool" type="button" style="height: 15px; width: 100px;" onclick="line()">Line</button> <br> <br> <br> <button id="rectangletool" type="button" style="height: 15px; width: 100px;" onclick="rectangle()">Rectangle</button> <br> <br> <br> <button id="circletool" type="button" style="height: 15px; width: 100px;" onclick="circle()">Circle</button> <br> <br> <br> <button id="erasertool" type="button" style="height: 15px; width: 100px;" onclick="eraser()">Eraser</button> <br> <br> <br> <button id="resettool" type="button" style="height: 15px; width: 100px;" onclick="reset()">Reset</button> </fieldset> </td> <td> <canvas id="paint" width="500vw" height="350vw" style="border: 5px solid #000000; margin-top: 3%;"></canvas> </td> </tr> </table> </p> <fieldset id="colorset" style="margin-top: 1.8%;"> <table> <tr> <td><button style="height: 15px; width: 80px;" onclick="fill()">Fill</button> <td><button style="background-color: #000000; height: 15px; width: 15px;" onclick="color('#000000')"></button> <td><button style="background-color: #B0171F; height: 15px; width: 15px;" onclick="color('#B0171F')"></button> <td><button style="background-color: #DA70D6; height: 15px; width: 15px;" onclick="color('#DA70D6')"></button> <td><button style="background-color: #8A2BE2; height: 15px; width: 15px;" onclick="color('#8A2BE2')"></button> <td><button style="background-color: #0000FF; height: 15px; width: 15px;" onclick="color('#0000FF')"></button> <td><button style="background-color: #4876FF; height: 15px; width: 15px;" onclick="color('#4876FF')"></button> <td><button style="background-color: #CAE1FF; height: 15px; width: 15px;" onclick="color('#CAE1FF')"></button> <td><button style="background-color: #6E7B8B; height: 15px; width: 15px;" onclick="color('#6E7B8B')"></button> <td><button style="background-color: #00C78C; height: 15px; width: 15px;" onclick="color('#00C78C')"></button> <td><button style="background-color: #00FA9A; height: 15px; width: 15px;" onclick="color('#00FA9A')"></button> <td><button style="background-color: #00FF7F; height: 15px; width: 15px;" onclick="color('#00FF7F')"></button> <td><button style="background-color: #00C957; height: 15px; width: 15px;" onclick="color('#00C957')"></button> <td><button style="background-color: #FFFF00; height: 15px; width: 15px;" onclick="color('#FFFF00')"></button> <td><button style="background-color: #CDCD00; height: 15px; width: 15px;" onclick="color('#CDCD00')"></button> <td><button style="background-color: #FFF68F; height: 15px; width: 15px;" onclick="color('#FFF68F')"></button> <td><button style="background-color: #FFFACD; height: 15px; width: 15px;" onclick="color('#FFFACD')"></button> <td><button style="background-color: #FFEC8B; height: 15px; width: 15px;" onclick="color('#FFEC8B')"></button> <td><button style="background-color: #FFD700; height: 15px; width: 15px;" onclick="color('#FFD700')"></button> <td><button style="background-color: #F5DEB3; height: 15px; width: 15px;" onclick="color('#F5DEB3')"></button> <td><button style="background-color: #FFE4B5; height: 15px; width: 15px;" onclick="color('#FFE4B5')"></button> <td><button style="background-color: #EECFA1; height: 15px; width: 15px;" onclick="color('#EECFA1')"></button> <td><button style="background-color: #FF9912; height: 15px; width: 15px;" onclick="color('#FF9912')"></button> <td><button style="background-color: #8E388E; height: 15px; width: 15px;" onclick="color('#8E388E')"></button> <td><button style="background-color: #7171C6; height: 15px; width: 15px;" onclick="color('#7171C6')"></button> <td><button style="background-color: #7D9EC0; height: 15px; width: 15px;" onclick="color('#7D9EC0')"></button> <td><button style="background-color: #388E8E; height: 15px; width: 15px;" onclick="color('#388E8E')"></button> </tr> <tr> <td><button style="height: 15px; width: 80px" onclick="outline()">Outline</button> <td><button style="background-color: #71C671; height: 15px; width: 15px;" onclick="color('#71C671')"></button> <td><button style="background-color: #8E8E38; height: 15px; width: 15px;" onclick="color('#8E8E38')"></button> <td><button style="background-color: #C5C1AA; height: 15px; width: 15px;" onclick="color('#C5C1AA')"></button> <td><button style="background-color: #C67171; height: 15px; width: 15px;" onclick="color('#C67171')"></button> <td><button style="background-color: #555555; height: 15px; width: 15px;" onclick="color('#555555')"></button> <td><button style="background-color: #848484; height: 15px; width: 15px;" onclick="color('#848484')"></button> <td><button style="background-color: #F4F4F4; height: 15px; width: 15px;" onclick="color('#F4F4F4')"></button> <td><button style="background-color: #EE0000; height: 15px; width: 15px;" onclick="color('#EE0000')"></button> <td><button style="background-color: #FF4040; height: 15px; width: 15px;" onclick="color('#FF4040')"></button> <td><button style="background-color: #EE6363; height: 15px; width: 15px;" onclick="color('#EE6363')"></button> <td><button style="background-color: #FFC1C1; height: 15px; width: 15px;" onclick="color('#FFC1C1')"></button> <td><button style="background-color: #FF7256; height: 15px; width: 15px;" onclick="color('#FF7256')"></button> <td><button style="background-color: #FF4500; height: 15px; width: 15px;" onclick="color('#FF4500')"></button> <td><button style="background-color: #F4A460; height: 15px; width: 15px;" onclick="color('#F4A460')"></button> <td><button style="background-color: #FF8000; height: 15px; width: 15px;" onclick="color('FF8000')"></button> <td><button style="background-color: #FFD700; height: 15px; width: 15px;" onclick="color('#FFD700')"></button> <td><button style="background-color: #8B864E; height: 15px; width: 15px;" onclick="color('#8B864E')"></button> <td><button style="background-color: #9ACD32; height: 15px; width: 15px;" onclick="color('#9ACD32')"></button> <td><button style="background-color: #66CD00; height: 15px; width: 15px;" onclick="color('#66CD00')"></button> <td><button style="background-color: #BDFCC9; height: 15px; width: 15px;" onclick="color('#BDFCC9')"></button> <td><button style="background-color: #76EEC6; height: 15px; width: 15px;" onclick="color('#76EEC6')"></button> <td><button style="background-color: #40E0D0; height: 15px; width: 15px;" onclick="color('#40E0D0')"></button> <td><button style="background-color: #9B30FF; height: 15px; width: 15px;" onclick="color('#9B30FF')"></button> <td><button style="background-color: #EE82EE; height: 15px; width: 15px;" onclick="color('#EE82EE')"></button> <td><button style="background-color: #FFC0CB; height: 15px; width: 15px;" onclick="color('#FFC0CB')"></button> <td><button style="background-color: #7CFC00; height: 15px; width: 15px;" onclick="color('#7CFC00')"></button> </tr> <tr> <td><label>Line Width</label></td> <td><button id="pixel_plus" type="button" onclick="add_pixel()" style="width: 25px;">+</button></td> <td><button id="pixel_minus" type="button" onclick="reduce_pixel()" style="width: 25px;">-</button></td> <td><button id="undo" type="button" onclick="undo_pixel()" style="width: 75px;">Undo</button></td> </tr> </table> <br> </fieldset> <script src="//code.jquery.com/jquery-1.8.3.js"></script> <script src=" {{ url_for('static', filename='script.js') }}"></script> </body> </html>

关于预期撤消的注意事项 function如果我误解了undo操作应该通过单击删除整个最后一个铅笔形状,则您必须更改pencil object 数组的结构方式。

目前,数组的结构是这样的:

[
  {
    "startx": 148,
    "starty": 281,
    "endx": 148,
    "endy": 280,
    "thick": 2,
    "color": "#000000"
  },

// more data objects for each mousemove captured;
  {
    "startx": 148,
    "starty": 281,
    "endx": 148,
    "endy": 280,
    "thick": 2,
    "color": "#000000"
  }
]

这意味着删除最后一个 object(您的.popcase 4中删除)仅删除在最后一个 mousemove 事件期间捕获的铅笔线的条子。 如果您想微调一条线(我认为这是一个很好的功能,并假设您想要那样),这没问题,但如果绘制了不止一条单独的铅笔线,则会导致问题。 如果有两条铅笔线,它们将在pencil数据数组中合并为一条。

要解决这个问题,您必须重新构造pencil数组,以像这样为每一行保留离散的内部 arrays:

[ 
 // first line array:
  [
  {
    "startx": 148,
    "starty": 281,
    "endx": 148,
    "endy": 280,
    "thick": 2,
    "color": "#000000"
  },

// more data objects for each mousemove captured;
  {
    "startx": 148,
    "starty": 281,
    "endx": 148,
    "endy": 280,
    "thick": 2,
    "color": "#000000"
  }
  ], 

// more line arrays;

// last line array:
  [
  {
    "startx": 148,
    "starty": 281,
    "endx": 148,
    "endy": 280,
    "thick": 2,
    "color": "#000000"
  },

// more data objects for each mousemove captured;
  {
    "startx": 148,
    "starty": 281,
    "endx": 148,
    "endy": 280,
    "thick": 2,
    "color": "#000000"
  }
  ] 

] // end of pencil array;

在这种结构下, .pop将删除整行(这可能是您想要的)。 它还将解决当前重绘会将任何单独的行合并为一行的错误。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM