如何使用JavaScript实现HTMLcanvas的撤销功能?
How to implement undo function for HTML canvas using JavaScript?
我正在使用 JavaScript 和 Flask-Python 在 HTML 中制作一个绘画应用程序。目前,我可以毫无问题地画出许多铅笔画和 rectangles/circles 之类的形状。我尝试为此应用程序实现的以下功能是 undo 函数。
我按以下方式将笔画和 canvas 绘图数据存储在 JS 对象中:
canvas_data = { "pencil": [], "line": [], "rectangle": [], "circle": [], "eraser": [], "last_action": -1 };
除了 last_action
之外,所有键名都应该是不言自明的。我使用这个 last_action
变量来了解用户最后使用的类别,以便我以后可以使用此信息来实现撤消功能。
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;
}
#linetool {
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="linetool" 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>
这是我尝试过的:
首先,我创建了一个 undo_pixel()
函数,该函数使用 last_action
变量弹出上一个操作的相应堆栈中输入的最后一个元素
然后我使用清除 canvas 的 redraw_canvas()
函数重新绘制 canvas,然后使用 [=22] 中存储的所有数据点重新绘制它=]对象。
但这导致了一些我无法完全理解的意外行为。这是正在发生的事情:
撤消前草图:
撤消后的草图:
我认为这可能是因为在所有被循环的点之间绘制了直线,但我不确定还有什么办法可以解决这个问题。如何正确实现redraw/undo功能?
导致您观察到的扇形的直接问题是由 startx
和 starty
对 canvas_data.pencil
数组中的每个对象条目保持相同引起的。这是因为,startx
和 starty
被分配了 prevX
和 prevY
中保存的值,但这些值是在 mousedown
事件侦听器中设置的并且不会更新在 mousemove
事件侦听器中。
固定如下:
首先,虽然对于功能来说不是绝对必要的,但从铅笔函数的 mousedown
事件侦听器中删除对 prevX
和 prevY
的引用并设置 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
侦听器中,添加行以更新 prevX
和 prevY
:
// 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
}
};
这些更改形成了正确的数据对象数组,每个对象的 startx
和 starty
值现在根据需要设置为前一个对象的 endx
和 endy
值.
但是,还有另一个紧迫的问题:undo_pixel
函数仅在第一次单击以删除铅笔数组的最后一个对象时完全执行(导致最后一个 mousemove
事件按预期消失),但随后的点击(删除该行的连续部分)被中止。
我假设 undo_pixel
函数旨在为每次点击删除一条条子而不是整条铅笔线(如果不是,请稍后查看)。
进程中止的原因是因为重置了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 线之前绘制的对象(为此您需要跟踪绘制项目的顺序)。
我根据上述建议制作了一个工作片段。您需要将其显示为整页,因为预览 window 太小,在您单击撤消按钮时看不到该行。如果你画一个短铅笔波浪形并反复按撤消按钮,你会看到这条线是 'undrawn'.
(我不得不删除你的一些其他功能,因为我超出了允许的答案字符限制)
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="linetool" 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>
关于预期撤消功能的注意事项
如果我误解了 undo
操作应该通过单击删除整个最后一个铅笔形状,则您必须更改 pencil
对象数组的结构方式。
目前数组结构如下:
[
{
"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"
}
]
这意味着删除最后一个对象(您的 .pop
在 case 4
中所做的)只会删除在最后一个 mousemove 事件期间捕获的铅笔线的条子。如果您想微调一条线(我认为这是一个很好的功能,并假设您想要那样),这没问题,但如果绘制了不止一条单独的铅笔线,则会导致问题。如果有两条铅笔线,它们将在 pencil
数据数组中合并为一条。
要解决这个问题,您必须 re-structure pencil
数组来为每一行保存离散的内部数组,如下所示:
[
// 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
将删除整行(这可能是您想要的)。它还将解决当前重绘会将任何单独的行合并为一行的错误。
我正在使用 JavaScript 和 Flask-Python 在 HTML 中制作一个绘画应用程序。目前,我可以毫无问题地画出许多铅笔画和 rectangles/circles 之类的形状。我尝试为此应用程序实现的以下功能是 undo 函数。
我按以下方式将笔画和 canvas 绘图数据存储在 JS 对象中:
canvas_data = { "pencil": [], "line": [], "rectangle": [], "circle": [], "eraser": [], "last_action": -1 };
除了 last_action
之外,所有键名都应该是不言自明的。我使用这个 last_action
变量来了解用户最后使用的类别,以便我以后可以使用此信息来实现撤消功能。
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;
}
#linetool {
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="linetool" 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>
这是我尝试过的:
首先,我创建了一个
undo_pixel()
函数,该函数使用last_action
变量弹出上一个操作的相应堆栈中输入的最后一个元素然后我使用清除 canvas 的
redraw_canvas()
函数重新绘制 canvas,然后使用 [=22] 中存储的所有数据点重新绘制它=]对象。
但这导致了一些我无法完全理解的意外行为。这是正在发生的事情:
撤消前草图:
撤消后的草图:
我认为这可能是因为在所有被循环的点之间绘制了直线,但我不确定还有什么办法可以解决这个问题。如何正确实现redraw/undo功能?
导致您观察到的扇形的直接问题是由 startx
和 starty
对 canvas_data.pencil
数组中的每个对象条目保持相同引起的。这是因为,startx
和 starty
被分配了 prevX
和 prevY
中保存的值,但这些值是在 mousedown
事件侦听器中设置的并且不会更新在 mousemove
事件侦听器中。
固定如下:
首先,虽然对于功能来说不是绝对必要的,但从铅笔函数的 mousedown
事件侦听器中删除对 prevX
和 prevY
的引用并设置 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
侦听器中,添加行以更新 prevX
和 prevY
:
// 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
}
};
这些更改形成了正确的数据对象数组,每个对象的 startx
和 starty
值现在根据需要设置为前一个对象的 endx
和 endy
值.
但是,还有另一个紧迫的问题:undo_pixel
函数仅在第一次单击以删除铅笔数组的最后一个对象时完全执行(导致最后一个 mousemove
事件按预期消失),但随后的点击(删除该行的连续部分)被中止。
我假设 undo_pixel
函数旨在为每次点击删除一条条子而不是整条铅笔线(如果不是,请稍后查看)。
进程中止的原因是因为重置了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 线之前绘制的对象(为此您需要跟踪绘制项目的顺序)。
我根据上述建议制作了一个工作片段。您需要将其显示为整页,因为预览 window 太小,在您单击撤消按钮时看不到该行。如果你画一个短铅笔波浪形并反复按撤消按钮,你会看到这条线是 'undrawn'.
(我不得不删除你的一些其他功能,因为我超出了允许的答案字符限制)
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="linetool" 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>
关于预期撤消功能的注意事项
如果我误解了 undo
操作应该通过单击删除整个最后一个铅笔形状,则您必须更改 pencil
对象数组的结构方式。
目前数组结构如下:
[
{
"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"
}
]
这意味着删除最后一个对象(您的 .pop
在 case 4
中所做的)只会删除在最后一个 mousemove 事件期间捕获的铅笔线的条子。如果您想微调一条线(我认为这是一个很好的功能,并假设您想要那样),这没问题,但如果绘制了不止一条单独的铅笔线,则会导致问题。如果有两条铅笔线,它们将在 pencil
数据数组中合并为一条。
要解决这个问题,您必须 re-structure pencil
数组来为每一行保存离散的内部数组,如下所示:
[
// 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
将删除整行(这可能是您想要的)。它还将解决当前重绘会将任何单独的行合并为一行的错误。