使用 Canvas 撤消/重做绘画程序 [英] Undo/Redo for paint program using Canvas

查看:49
本文介绍了使用 Canvas 撤消/重做绘画程序的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要为我的绘画程序实现一个撤销/重做系统:http://www.taffatech.com/Paint.html

I need to implement a undo/redo system for my paint program: http://www.taffatech.com/Paint.html

我的想法是有 2 个数组堆栈,一个用于撤消,1 个用于重做.每当您绘制并释放鼠标时,它都会通过推送将画布图像保存到撤消数组堆栈中.如果你画别的东西然后释放它也会做同样的事情.但是,如果您单击撤消,它将弹出撤消数组的顶部图像并将其打印到画布上,然后将其推送到重做堆栈中.

My idea that I came up with is to have 2 arrays stacks, one for undo and 1 for redo. Anytime you draw and release the mouse it saves the canvas image to the undo array stack by push. if you draw something else and release it will do the same. However if you click undo it will pop the top image of undo array and print that to canvas and then push it onto the redo stack.

点击时重做会从自身弹出并推送以撤消.每次鼠标关闭后都会打印撤销的顶部.

redo when clicked will pop from itself and push to undo. the top of undo will be printed after each mouse off.

这是正确的方法还是有更好的方法?

Is this the right way or is there a better one?

推荐答案

警告!

将整个画布保存为图像以进行撤消/重做会占用大量内存,而且会影响性​​能.

但是,您将用户的绘图逐步保存在数组中的想法仍然是一个好主意.

However, your idea of progressively saving the user’s drawings in an array is still a good idea.

不要将整个画布保存为图像,只需创建一个点数组来记录用户在绘图时所做的每一次鼠标移动.这是您的绘图数组",可用于完全重绘画布.

Instead of saving the whole canvas as an image, just create an array of points to record every mousemove the user makes as they are drawing. This is your "drawing array" that can be used to fully redraw your canvas.

每当用户拖动鼠标时,他们就会创建一条多段线(一组连接的线段).当用户拖动以创建一条线时,将该鼠标移动点保存到您的绘图数组并将其折线延伸到当前鼠标移动位置.

Whenever the user drags the mouse they are creating a polyline (a group of connected line segments). When the user drags to create a line, save that mousemove point to your drawing array and extend their polyline to the current mousemove position.

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();
}

重做逻辑上更棘手.

最简单的重做是用户只能在撤销后立即重做.将每个撤消"点保存在单独的重做"数组中.然后如果用户想要重做,你可以将重做位添加回主数组.

The simplest Redo is when the user can only redo immediately after an undo. Save each "undo" point in your separate "redo" array. Then if the user wants to redo, you can just add the redo bits back to the to the main array.

复杂的是,如果您让用户在完成更多绘图后重做".

The complication is if you let the user "redo" after they have done more drawing.

例如,你最终可能得到一条有两条尾巴的狗:一条新画的尾巴和第二条重做"的尾巴!

For example, you could end up with a dog with 2 tails: a newly drawn tail and a second "redo" tail !

因此,如果您在额外绘制后允许重做,则需要一种方法来防止用户在重做期间感到困惑.Matt Greer 的分层"重做的想法是一种好方法.只需通过保存重做点来改变这个想法,而不是整个画布图像.然后用户可以打开/关闭重做,看看他们是否愿意保留他们的重做.

So if you allow redo’s after additional drawing, you would need a way to keep the user from being confused during redo. Matt Greer’s idea of "layering" redos is one good way. Just alter that idea by saving the redo points, not the entire canvas image. Then the user could toggle the redo on/off to see if they would like to keep their redo.

以下是使用我为上一个问题创建的撤消数组的示例:像在油漆中一样在画布上绘图

Here is an example of using an undo array I created for a previous question: 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>

这篇关于使用 Canvas 撤消/重做绘画程序的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆