You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何为HTML Canvas实现撤销(Undo)与重做(Redo)功能?

Fixing Undo/Redo for Your Canvas Drawing App

Hey there! No worries about your English at all—let's get that undo/redo functionality working for your canvas app. Looking at your code, I notice a few key pieces missing that are keeping things from working as expected. Let's walk through the fixes step by step.

The Core Issues in Your Current Code

  • You're not actually storing the drawing paths anywhere (the points variable isn't defined or populated)
  • The redrawAll() function is missing entirely—this is critical for rebuilding the canvas when undoing/redoing
  • Your current drawing logic draws directly to the canvas without saving path data, so you can't "undo" individual strokes later

Updated Full Code with Working Undo/Redo

First, let's update your HTML to hook up the redo button:

<th><img src="images/undo.png" onclick="undoLastPoint()"></th>
<th><img src="images/redo.png" onclick="redoLastPoint()"></th>

Now here's the revised JavaScript with all the necessary pieces:

var canvas = document.getElementById('mycanvas');
var ctx = canvas.getContext('2d');
var isDrawing = false;
// Store all completed drawing strokes
var drawingHistory = [];
// Store strokes that were undone (for redo)
var redoStack = [];
// Track the current stroke being drawn
var currentStroke = [];

function pencil() {
    canvas.onmousedown = function(e) {
        var pos = getMousePos(canvas, e);
        isDrawing = true;
        // Start a new stroke
        currentStroke = [{x: pos.x, y: pos.y}];
        ctx.beginPath();
        ctx.moveTo(pos.x, pos.y);
    };

    canvas.onmousemove = function(e) {
        var pos = getMousePos(canvas, e);
        if (isDrawing) {
            // Add the current point to the active stroke
            currentStroke.push({x: pos.x, y: pos.y});
            ctx.lineTo(pos.x, pos.y);
            ctx.stroke();
        }
    };

    canvas.onmouseup = function() {
        if (isDrawing) {
            isDrawing = false;
            // Save the completed stroke to history
            drawingHistory.push(currentStroke);
            // Clear redo stack when a new stroke is drawn
            redoStack = [];
            currentStroke = [];
        }
    };
}

function undoLastPoint() {
    if (drawingHistory.length > 0) {
        // Move the last stroke from history to redo stack
        var undoneStroke = drawingHistory.pop();
        redoStack.push(undoneStroke);
        // Redraw the canvas with remaining strokes
        redrawAll();
    }
}

function redoLastPoint() {
    if (redoStack.length > 0) {
        // Move the last undone stroke back to history
        var redoneStroke = redoStack.pop();
        drawingHistory.push(redoneStroke);
        // Redraw the canvas with restored strokes
        redrawAll();
    }
}

function redrawAll() {
    // Clear the entire canvas first
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    
    // Redraw every stroke in the history
    drawingHistory.forEach(stroke => {
        if (stroke.length > 0) {
            ctx.beginPath();
            ctx.moveTo(stroke[0].x, stroke[0].y);
            stroke.forEach(point => {
                ctx.lineTo(point.x, point.y);
            });
            ctx.stroke();
        }
    });
}

function getMousePos(canvas, evt) {
    var rect = canvas.getBoundingClientRect();
    return {
        x: evt.clientX - rect.left,
        y: evt.clientY - rect.top
    };
}

Key Changes Explained

  1. Tracking Strokes: We now use drawingHistory to store every completed stroke (each stroke is an array of points). currentStroke tracks the line you're actively drawing.
  2. Redraw Logic: redrawAll() clears the canvas and redraws every stroke from the history—this is how undo/redo "resets" the canvas to the correct state.
  3. Undo/Redo Flow:
    • When you undo, we take the last stroke from drawingHistory and move it to redoStack, then redraw.
    • When you redo, we take the last stroke from redoStack and move it back to drawingHistory, then redraw.
    • Any new drawing clears the redo stack (since you can't redo after starting a new stroke—this matches standard undo/redo behavior).

Just call your pencil() function to initialize the drawing tool, and your undo/redo buttons should work perfectly now!

内容的提问来源于stack exchange,提问作者wendykr

火山引擎 最新活动