正在覆盖HTML5画布绘图
我有一个HTML5画布,允许用户绘制各种形状,还有一个画笔功能,允许用户手绘。我正在为这些实现使用命令模式。我面临的问题是“撤销”功能。它对所有其他命令都非常有效,但是当涉及到“笔刷”时,它似乎有问题 画笔的工作方式是每次拖动鼠标都会存储点,一旦添加了一个新点,整个点阵列就会在屏幕上重新绘制。只有当用户释放鼠标时,图形才会停止。您可能会立即看到问题,点越老,屏幕上重新绘制的点越多。这会导致线条的颜色看起来比实际颜色深得多 我的解决方案是只连接最后一个点n-1和点n-2,但实际上只会重新绘制最后两个点。我不完全理解画布是如何工作的,为什么这种方法不起作用,但重绘覆盖点似乎起作用 下面是一些概述关键部分的代码正在覆盖HTML5画布绘图,html,canvas,drawing,Html,Canvas,Drawing,我有一个HTML5画布,允许用户绘制各种形状,还有一个画笔功能,允许用户手绘。我正在为这些实现使用命令模式。我面临的问题是“撤销”功能。它对所有其他命令都非常有效,但是当涉及到“笔刷”时,它似乎有问题 画笔的工作方式是每次拖动鼠标都会存储点,一旦添加了一个新点,整个点阵列就会在屏幕上重新绘制。只有当用户释放鼠标时,图形才会停止。您可能会立即看到问题,点越老,屏幕上重新绘制的点越多。这会导致线条的颜色看起来比实际颜色深得多 我的解决方案是只连接最后一个点n-1和点n-2,但实际上只会重新绘制最后两
BrushStrat.prototype.mousemove=function(event){
if(this.command!=null){
//add this point to the list
this.command.addPoint({x:event.canvasX, y:event.canvasY});
//redraw all points
this.command.draw(this.paint.context);
}
}
BrushCommand.prototype.draw=function(context){
if(this.points.length==0){
return;
}
context.beginPath();
context.strokeStyle = this.strokeStyle;
context.lineWidth=this.lineWidth;
//start from the first point
context.moveTo(this.points[0].x,this.points[0].y);
//redraw all subsequent points
for(var i=1;i<this.points.length;i++){
context.lineTo(this.points[i].x, this.points[i].y);
}
context.stroke();
}
brushtrast.prototype.mousemove=函数(事件){
if(this.command!=null){
//将此点添加到列表中
addPoint({x:event.canvasX,y:event.canvasY});
//重新绘制所有点
this.command.draw(this.paint.context);
}
}
BrushCommand.prototype.draw=函数(上下文){
if(this.points.length==0){
返回;
}
context.beginPath();
context.strokeStyle=this.strokeStyle;
context.lineWidth=this.lineWidth;
//从第一点开始
context.moveTo(this.points[0].x,this.points[0].y);
//重新绘制所有后续点
对于(var i=1;i在重绘每个阵列点之前,清除画布或至少清除绘图区域
编辑
是的,对不起,我假设你有一个游戏循环。但是,这仍然是一个有效的选项:在绘图区域使用两个画布。一个用于正在绘制的“当前”形状/曲线(在绘制每个点之前清除),另一个具有所有已完成形状/曲线的持久层
总而言之,当用户点击并拖动该形状时,该形状被绘制到当前层。当用户释放鼠标时,图像现在被“锁定”并传输到持久层
希望这是有意义的。在重新绘制每个阵列点之前,请清除画布,或至少清除绘图区域
编辑
是的,对不起,我假设你有一个游戏循环。但是,这仍然是一个有效的选项:在绘图区域使用两个画布。一个用于正在绘制的“当前”形状/曲线(在绘制每个点之前清除),另一个具有所有已完成形状/曲线的持久层
总而言之,当用户点击并拖动该形状时,该形状被绘制到当前层。当用户释放鼠标时,图像现在被“锁定”并传输到持久层
希望有意义。使用命令模式的徒手画笔笔划
在实现“命令模式”以跟踪用户的自由式笔刷笔划时,您做了一个很好的选择
mouseDown和mouseUp之间的每个图形都被视为“拖动组”
每个“拖动组”都添加到主阵列(CommandStack[])
然后,只需删除CommandStack[]上的最后一个组,即可轻松撤消最后一个图形
这是用户在拖动循环期间发生的情况:
鼠标向下:
- 为这组拖动的线设置起点X,Y
- 创建专用于这组拖曳线的新点阵列(newPoints[])
鼠标移动:
- 将每个鼠标位置点添加到新点[]
鼠标:
- 拖动结束--停止向newPoints[]添加点
- 将起始X、Y和新点[]存储到CommandStack[]数组中
然后,您可以简单有效地撤消笔划:
- 从CommandStack[]中删除最后的newPoints[],如下所示:CommandStack.pop()
- 在CommandStack[]中重新绘制所有剩余笔划
- 图形在视觉上与用户最后一次笔划前相同(快速+高效)
- 通过从CommandStack[]中执行更多的弹出操作,可以删除更多行
- 您还可以通过保存弹出的newPoints[]轻松实现重做
下面是代码和小提琴:
正文{背景色:象牙;}
画布{边框:1px纯红;}
$(函数(){
var canvas=document.getElementById(“canvas”);
var ctx=canvas.getContext(“2d”);
var lastX;
血管成形术;
var strokeColor=“红色”;
var strokeWidth=2;
canMouseX变种;
var canMouseY;
var canvasOffset=$(“#画布”).offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
var isMouseDown=错误;
//命令模式--撤消
var commandStack=新数组();
var newStart;
var newPoints=新数组();
功能手柄向下(e){
canMouseX=parseInt(e.clientX-offsetX);
canMouseY=parseInt(e.clientY-offsetY);
$(“#downlog”).html(“Down:“+canMouseX+”/“+canMouseY”);
//把你的鼠标下的东西放在这里
lastX=canMouseX;
拉斯蒂=坎穆西;
isMouseDown=真;
//命令模式材料
newStart={x:canMouseX,y:canMouseY};
newPoints=新数组();
}
功能handleMouseUp(e){
canMouseX=parseInt(e.clientX-offsetX);
canMouseY=parseInt(e.clientY-offsetY);
$(“#uplog”).html(“Up:“+canMouseX+”/“+canMouseY”);
//把你的鼠标放在这里
isMouseDown=错误;
//命令模式材料
push({moveTo:newStart,points:newPoints});
}
函数handleMouseOut(e){
canMouseX=parseInt(e.clientX-offsetX);
canMouseY=parseInt(e.clientY-offsetY);
$(“#outlog”).html(“Out:+canMouseX+”/“+canMouseY);
//把你的老鼠屎放在这里
isMouseDown=fa
<!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 strokeColor="red";
var strokeWidth=2;
var canMouseX;
var canMouseY;
var canvasOffset=$("#canvas").offset();
var offsetX=canvasOffset.left;
var offsetY=canvasOffset.top;
var isMouseDown=false;
// command pattern -- undo
var commandStack=new Array();
var newStart;
var newPoints=new Array();
function handleMouseDown(e){
canMouseX=parseInt(e.clientX-offsetX);
canMouseY=parseInt(e.clientY-offsetY);
$("#downlog").html("Down: "+ canMouseX + " / " + canMouseY);
// Put your mousedown stuff here
lastX=canMouseX;
lastY=canMouseY;
isMouseDown=true;
// command pattern stuff
newStart={x:canMouseX,y:canMouseY};
newPoints=new Array();
}
function handleMouseUp(e){
canMouseX=parseInt(e.clientX-offsetX);
canMouseY=parseInt(e.clientY-offsetY);
$("#uplog").html("Up: "+ canMouseX + " / " + canMouseY);
// Put your mouseup stuff here
isMouseDown=false;
// command pattern stuff
commandStack.push({moveTo:newStart,points:newPoints});
}
function handleMouseOut(e){
canMouseX=parseInt(e.clientX-offsetX);
canMouseY=parseInt(e.clientY-offsetY);
$("#outlog").html("Out: "+ canMouseX + " / " + canMouseY);
// Put your mouseOut stuff here
isMouseDown=false;
}
function handleMouseMove(e){
canMouseX=parseInt(e.clientX-offsetX);
canMouseY=parseInt(e.clientY-offsetY);
$("#movelog").html("Move: "+ canMouseX + " / " + canMouseY);
// Put your mousemove stuff here
if(isMouseDown){
ctx.beginPath();
ctx.moveTo(lastX,lastY);
ctx.lineTo(canMouseX,canMouseY);
ctx.stroke();
lastX=canMouseX;
lastY=canMouseY;
// command pattern stuff
newPoints.push({x:canMouseX,y:canMouseY});
}
}
$("#canvas").mousedown(function(e){handleMouseDown(e);});
$("#canvas").mousemove(function(e){handleMouseMove(e);});
$("#canvas").mouseup(function(e){handleMouseUp(e);});
$("#canvas").mouseout(function(e){handleMouseOut(e);});
$("#undo").click(function(e){ undoLast(); });
function undoLast(){
commandStack.pop();
redrawAll();
}
function redrawAll(){
// prep for commandStack redraws
ctx.clearRect(0,0,canvas.width,canvas.height);
ctx.save()
ctx.strokeStyle="blue";
ctx.beginPath();
// loop through the commandStack and draw all nodes
for(var s=0;s<commandStack.length;s++){
// move to the starting point of this node
var start=commandStack[s].moveTo;
ctx.moveTo(start.x,start.y);
// draw each line segment in this node
var pts=commandStack[s].points;
for(var p=0;p<pts.length;p++){
ctx.lineTo(pts[p].x,pts[p].y);
} // end for(p)
} // end for(s)
// actually draw the lines
ctx.stroke();
ctx.restore();
}
}); // end $(function(){});
</script>
</head>
<body>
<p id="downlog">Down</p>
<p id="movelog">Move</p>
<p id="uplog">Up</p>
<p id="outlog">Out</p>
<canvas id="canvas" width=300 height=300></canvas>
<button id="undo">Undo</button>
</body>
</html>