无法从另一个函数恢复画布上下文

时间:2017-11-30 00:08:33

标签: javascript html5 canvas

我担心我在理解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;
&#13;
&#13;

1 个答案:

答案 0 :(得分:1)

在内部,saverestore共享一个共同的状态堆栈,您可以将其视为数组。

每次调用save时,上下文的所有当前默认属性都在此堆栈中推送
每次调用restore时,最后一个状态都会从堆栈中弹出,并且所有保存的属性都将设置为上下文。

这意味着每个保存的状态只能恢复一次。

在您的代码中,您只能在初始化时调用save一次。然后在第一个输入的change事件中恢复此状态,同时删除此初始状态。您对restore的所有后续调用都将无效,因为已保存的状态堆栈中没有任何内容。

一般来说,我建议不要在这种情况下使用保存和恢复,而是在每次需要时设置和重置每个属性;但由于问题是关于这些方法,这里有一种方法可以让您的代码有效:

在每个更改事件中恢复并在之后保存。这样,您将始终使用默认属性。

&#13;
&#13;
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;
&#13;
&#13;

另请注意,您的代码中有很多内容需要重构,但这不是问题的重点...