我担心我在理解canvas.getContext(" 2d")时遇到很多困难。在javascript中保存()end restore()函数。
我希望这个例子解释了这个问题。应该发生的是,画布上下文在创建时保存,当您在不同工具之间切换时,应该恢复已保存的状态,以便每次都重置上下文。
但是,如果你使用铅笔,然后选择橡皮擦,然后回到铅笔,会发生什么是context.globalCompositeOperation继续"目的地" :基本上铅笔继续擦除。
看起来任何难以理解的上下文参数都不会被改变,即。保存的上下文未被恢复。
你们中间有什么好心的人请帮助我了解我出错的地方吗?
document.querySelector("img").addEventListener("click",function(event) {
/* create a new canvas element in order to edit the image */
var canvas = document.createElement("canvas");
canvas.width = event.target.offsetWidth;
canvas.height = event.target.offsetHeight;
canvas.innerHTML = event.target.alt;
canvas.getContext("2d").drawImage(event.target,0,0);
/* here is where we save the context state */
canvas.getContext("2d").save();
/* replace the img element with canvas */
event.target.parentNode.replaceChild(canvas,event.target);
});
/*\
\*/
document.querySelector("aside").addEventListener("change",function(event) {
var canvas = document.querySelector("canvas");
if (!canvas) {
return;
}
var context = canvas.getContext("2d");
/* restore canvas state to reset context */
context.restore();
switch (event.target.value) {
case "pencil" :
context.lineJoin = "round";
context.lineCap = "round";
context.lineWidth = "6";
context.strokeStyle = "rgb(0,0,0)";
break;
case "eraser" :
context.globalCompositeOperation = "destination-out";
context.lineJoin = "round";
context.lineCap = "round";
context.lineWidth = "20";
break;
}
canvas.onmousedown = function(event) {
context.beginPath();
context.moveTo(event.offsetX,event.offsetY);
canvas.onmousemove = function(event) {
context.lineTo(event.offsetX,event.offsetY);
context.stroke();
}
document.querySelector("main").onmouseup = function(event) {
canvas.onmousemove = null;
this.onmouseup = null;
}
}
});

body {
display:flex;
}
main {
margin:0.5em;
}
main img,
main canvas {
display:inline-block;
width:200px;
height:200px;
text-align:center;
color:rgb(200,200,200);
background-color:rgb(250,250,250);
cursor:pointer;
}
aside {
margin:0.5em;
}
aside input[type='radio'] {
display:none;
}
aside input[type='radio'] + label {
display:block;
margin:0 auto;
box-sizing:border-box;
line-height:1.5em;
text-align:center;
padding:0 0.5em;
border-style:solid;
border-width:1px;
border-color:rgb(127,127,127);
border-radius:0.25em;
background:rgb(245,245,245);
cursor:pointer;
}
aside input[type='radio']:checked + label {
background:rgb(240,240,240);
}

<body>
<main>
<img width="200" height="200" alt="click here to start">
</main>
<aside>
<input id="canvas_context_pencil" name="canvas_context" type="radio" value="pencil">
<label for="canvas_context_pencil">pencil</label>
<input id="canvas_context_eraser" name="canvas_context" type="radio" value="eraser">
<label for="canvas_context_eraser">eraser</label>
<ol>
<li>click on image to create a canvas element</li>
<li>select either pencil or eraser to draw</li>
</ol>
</aside>
</body>
&#13;
答案 0 :(得分:1)
在内部,save
和restore
共享一个共同的状态堆栈,您可以将其视为数组。
每次调用save
时,上下文的所有当前默认属性都在此堆栈中推送。
每次调用restore
时,最后一个状态都会从堆栈中弹出,并且所有保存的属性都将设置为上下文。
这意味着每个保存的状态只能恢复一次。
在您的代码中,您只能在初始化时调用save
一次。然后在第一个输入的change
事件中恢复此状态,同时删除此初始状态。您对restore
的所有后续调用都将无效,因为已保存的状态堆栈中没有任何内容。
一般来说,我建议不要在这种情况下使用保存和恢复,而是在每次需要时设置和重置每个属性;但由于问题是关于这些方法,这里有一种方法可以让您的代码有效:
在每个更改事件中恢复并在之后保存。这样,您将始终使用默认属性。
document.querySelector("img").addEventListener("click", function(event) {
/* create a new canvas element in order to edit the image */
var canvas = document.createElement("canvas");
canvas.width = event.target.offsetWidth;
canvas.height = event.target.offsetHeight;
canvas.innerHTML = event.target.alt;
canvas.getContext("2d").drawImage(event.target, 0, 0);
/* replace the img element with canvas */
event.target.parentNode.replaceChild(canvas, event.target);
});
document.querySelector("aside").addEventListener("change", function(event) {
var canvas = document.querySelector("canvas");
if (!canvas) {
return;
}
var context = canvas.getContext("2d");
/* restore canvas state to reset context */
context.restore();
/* save now that it is default */
context.save();
switch (event.target.value) {
case "pencil":
context.lineJoin = "round";
context.lineCap = "round";
context.lineWidth = "6";
context.strokeStyle = "rgb(0,0,0)";
break;
case "eraser":
context.globalCompositeOperation = "destination-out";
context.lineJoin = "round";
context.lineCap = "round";
context.lineWidth = "20";
break;
}
canvas.onmousedown = function(event) {
context.beginPath();
context.moveTo(event.offsetX, event.offsetY);
canvas.onmousemove = function(event) {
// note that since you don't clear the canvas,
// you are actually drawing a lot of pixels over themselves,
// creating huge antialiasing artifacts.
context.lineTo(event.offsetX, event.offsetY);
context.stroke();
}
document.querySelector("main").onmouseup = function(event) {
canvas.onmousemove = null;
this.onmouseup = null;
}
}
});
&#13;
body {
display:flex;
}
main {
margin:0.5em;
}
main img,
main canvas {
display:inline-block;
width:200px;
height:200px;
text-align:center;
color:rgb(200,200,200);
background-color:rgb(250,250,250);
cursor:pointer;
}
aside {
margin:0.5em;
}
aside input[type='radio'] {
display:none;
}
aside input[type='radio'] + label {
display:block;
margin:0 auto;
box-sizing:border-box;
line-height:1.5em;
text-align:center;
padding:0 0.5em;
border-style:solid;
border-width:1px;
border-color:rgb(127,127,127);
border-radius:0.25em;
background:rgb(245,245,245);
cursor:pointer;
}
aside input[type='radio']:checked + label {
background:rgb(240,240,240);
}
&#13;
<body>
<main>
<img width="200" height="200" alt="click here to start" src="https://upload.wikimedia.org/wikipedia/commons/5/55/John_William_Waterhouse_A_Mermaid.jpg">
</main>
<aside>
<input id="canvas_context_pencil" name="canvas_context" type="radio" value="pencil">
<label for="canvas_context_pencil">pencil</label>
<input id="canvas_context_eraser" name="canvas_context" type="radio" value="eraser">
<label for="canvas_context_eraser">eraser</label>
<ol>
<li>click on image to create a canvas element</li>
<li>select either pencil or eraser to draw</li>
</ol>
</aside>
</body>
&#13;
另请注意,您的代码中有很多内容需要重构,但这不是问题的重点...