경고 단어!
실행 취소/다시 실행을위한 이미지로 전체 캔버스를 저장하는 것은 메모리 집약적이며 성능 저하 요인입니다.
그러나 배열에서 사용자의 그림을 점차적으로 저장한다는 생각은 여전히 좋습니다.
전체 캔버스를 이미지로 저장하는 대신 사용자가 그리는 모든 mousemove를 기록하는 점 배열을 만들면됩니다. 이것은 캔버스를 완전히 다시 그리는 데 사용할 수있는 "그리기 배열"입니다.
사용자가 마우스를 끌 때마다 폴리 라인 (연결된 선분 그룹의 그룹)이 생성됩니다. 사용자가 드래그하여 선을 만들면 해당 mousemove 점을 드로잉 배열에 저장하고 폴리 라인을 현재 mousemove 위치로 확장합니다.
function handleMouseMove(e) {
// calc where the mouse is on the canvas
mouseX = parseInt(e.clientX - offsetX);
mouseY = parseInt(e.clientY - offsetY);
// if the mouse is being dragged (mouse button is down)
// then keep drawing a polyline to this new mouse position
if (isMouseDown) {
// extend the polyline
ctx.lineTo(mouseX, mouseY);
ctx.stroke();
// save this x/y because we might be drawing from here
// on the next mousemove
lastX = mouseX;
lastY = mouseY;
// Command pattern stuff: Save the mouse position and
// the size/color of the brush to the "undo" array
points.push({
x: mouseX,
y: mouseY,
size: brushSize,
color: brushColor,
mode: "draw"
});
}
}
사용자가 바로 도면 배열 오프 마지막 점을 팝업, "취소"하고 싶은 경우
:
function undoLastPoint() {
// remove the last drawn point from the drawing array
var lastPoint=points.pop();
// add the "undone" point to a separate redo array
redoStack.unshift(lastPoint);
// redraw all the remaining points
redrawAll();
}
다시 실행은 논리적으로 더 까다 롭습니다.
가장 간단한 재실행은 사용자가 실행 취소 후에 즉시 다시 실행할 수있는 경우입니다. 각 "실행 취소"지점을 별도의 "재실행"배열에 저장하십시오. 그런 다음 다시 실행하려는 경우 다시 배열 비트를 다시 기본 배열에 추가하면됩니다.
더 많은 그림을 그린 후에 사용자가 "다시 실행"하도록하면 복잡합니다.
예를 들어 새로 꼬리와 두 번째 "다시 실행"꼬리가있는 꼬리가 2 개있는 강아지로 끝날 수 있습니다.
그래서 다시 그리기 후에 다시 실행을 허용하면 재실행 중에 사용자가 혼동을 피할 수있는 방법이 필요합니다. 매트 그리어 (Matt Greer)의 재 설계 계층화 아이디어는 좋은 방법입니다. 전체 캔버스 이미지가 아니라 재실행 점을 저장하여 아이디어를 변경하십시오. 그런 다음 사용자는 다시 실행을 유지할지 여부를 확인하기 위해 다시 실행을 설정/해제 할 수 있습니다. 그 코드와 바이올린 여기 Drawing to canvas like in paint
됩니다 : http://jsfiddle.net/m1erickson/AEYYq/
<!doctype html>
<html>
<head>
<link rel="stylesheet" type="text/css" media="all" href="css/reset.css" /> <!-- reset css -->
<script type="text/javascript" src="http://code.jquery.com/jquery.min.js"></script>
<!--[if lt IE 9]><script type="text/javascript" src="../excanvas.js"></script><![endif]-->
<style>
body{ background-color: ivory; }
canvas{border:1px solid red;}
</style>
<script>
$(function(){
var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var lastX;
var lastY;
var mouseX;
var mouseY;
var canvasOffset=$("#canvas").offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
var isMouseDown=false;
var brushSize=20;
var brushColor="#ff0000";
var points=[];
function handleMouseDown(e){
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// Put your mousedown stuff here
ctx.beginPath();
if(ctx.lineWidth!=brushSize){ctx.lineWidth=brushSize;}
if(ctx.strokeStyle!=brushColor){ctx.strokeStyle=brushColor;}
ctx.moveTo(mouseX,mouseY);
points.push({x:mouseX,y:mouseY,size:brushSize,color:brushColor,mode:"begin"});
lastX=mouseX;
lastY=mouseY;
isMouseDown=true;
}
function handleMouseUp(e){
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// Put your mouseup stuff here
isMouseDown=false;
points.push({x:mouseX,y:mouseY,size:brushSize,color:brushColor,mode:"end"});
}
function handleMouseMove(e){
mouseX=parseInt(e.clientX-offsetX);
mouseY=parseInt(e.clientY-offsetY);
// Put your mousemove stuff here
if(isMouseDown){
ctx.lineTo(mouseX,mouseY);
ctx.stroke();
lastX=mouseX;
lastY=mouseY;
// command pattern stuff
points.push({x:mouseX,y:mouseY,size:brushSize,color:brushColor,mode:"draw"});
}
}
function redrawAll(){
if(points.length==0){return;}
ctx.clearRect(0,0,canvas.width,canvas.height);
for(var i=0;i<points.length;i++){
var pt=points[i];
var begin=false;
if(ctx.lineWidth!=pt.size){
ctx.lineWidth=pt.size;
begin=true;
}
if(ctx.strokeStyle!=pt.color){
ctx.strokeStyle=pt.color;
begin=true;
}
if(pt.mode=="begin" || begin){
ctx.beginPath();
ctx.moveTo(pt.x,pt.y);
}
ctx.lineTo(pt.x,pt.y);
if(pt.mode=="end" || (i==points.length-1)){
ctx.stroke();
}
}
ctx.stroke();
}
function undoLast(){
points.pop();
redrawAll();
}
ctx.lineJoin = "round";
ctx.fillStyle=brushColor;
ctx.lineWidth=brushSize;
$("#brush5").click(function(){ brushSize=5; });
$("#brush10").click(function(){ brushSize=10; });
// Important! Brush colors must be defined in 6-digit hex format only
$("#brushRed").click(function(){ brushColor="#ff0000"; });
$("#brushBlue").click(function(){ brushColor="#0000ff"; });
$("#canvas").mousedown(function(e){handleMouseDown(e);});
$("#canvas").mousemove(function(e){handleMouseMove(e);});
$("#canvas").mouseup(function(e){handleMouseUp(e);});
// hold down the undo button to erase the last line segment
var interval;
$("#undo").mousedown(function() {
interval = setInterval(undoLast, 100);
}).mouseup(function() {
clearInterval(interval);
});
}); // end $(function(){});
</script>
</head>
<body>
<p>Drag to draw. Use buttons to change lineWidth/color</p>
<canvas id="canvas" width=300 height=300></canvas><br>
<button id="undo">Hold this button down to Undo</button><br><br>
<button id="brush5">5px Brush</button>
<button id="brush10">10px Brush</button>
<button id="brushRed">Red Brush</button>
<button id="brushBlue">Blue Brush</button>
</body>
</html>
당신은 [fabric.js]로 시도해 볼 수도를 (여기
내가 이전 질문에 대해 작성된 실행 취소 배열을 사용하는 예입니다 http://fabricjs.com/)을 사용하면 자유롭게 그림을 그릴 수 있으며 각 도형을 객체로 감쌀 수 있습니다 ([here] (http://fabricjs.com/fabric-intro-part-4/) 참조). 수행 할 작업 – Jacopofar실행 취소 스택에 새로운 작업이 저장 될 때 다시 실행 스택을 지우는 것을 잊지 마십시오. – Bergi
전체 이미지를 저장하는 데 메모리가 많이 소모 될 수 있습니다. 스택 크기를 조정하거나 이미지간에 변경 사항을 저장해보십시오 (기본적으로 모든 획). –