JavaScript - canvas - 使用鼠标画线,带撤销与重做功能

文章目录

  • 预备知识
    • 画直线
      • 项目结构
      • HTML
      • CSS
      • JavaScript
      • 效果
  • 使用鼠标画线,带撤销与重做功能
    • 使用鼠标画线
      • HTML
      • JavaScript
      • 效果
    • 添加撤销与重做功能
      • HTML
      • JavaScript
      • 效果

预备知识

画直线

项目结构

JavaScript - canvas - 使用鼠标画线,带撤销与重做功能_第1张图片

HTML

index.html 文件:

DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>画直线title>
        <link href="css/main.css" rel="stylesheet">
    head>
    <body>
        <canvas id="canvas">canvas>
        <script src="js/main.js">script>
    body>
html>

CSS

main.css 文件:

#ccs {
    box-shadow: 0px 0px 2px 1px rgba(0, 0, 0, 0.4);
}

JavaScript

main.js 文件:

window.onload = (event) => {
    // console.log(event);
    main();
}

function main() {
    const canvas = document.getElementById("canvas");
    // 设置画布的宽高
    canvas.width = 400;
    canvas.height = 400;
    
    const ctx = canvas.getContext("2d");
    
    // 画直线
    ctx.beginPath();
    ctx.moveTo(0, 0); // 画笔的起点
    ctx.lineTo(100, 100); // 画笔的终点,第一个参数表示向左移动 100 像素,,第二个参数表示向下移动 100 像素
    ctx.stroke();
}

效果

JavaScript - canvas - 使用鼠标画线,带撤销与重做功能_第2张图片

使用鼠标画线,带撤销与重做功能

使用鼠标画线

HTML

index.html 文件:

DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>使用鼠标画线title>
        <link href="css/main.css" rel="stylesheet">
    head>
    <body>
        <canvas id="canvas">canvas>
        <script src="js/main.js">script>
    body>
html>

JavaScript

main.js 文件:

window.onload = (event) => {
    // console.log(event);
    main();
}

function main() {
    const canvas = document.getElementById("canvas");
    // 设置画布的宽高
    canvas.width = 400;
    canvas.height = 400;
    
    const ctx = canvas.getContext("2d");
    
    if (!ctx) return;
    
    const MOUSE_LEFT_BUTTON = 0; // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
    
    let isDrawing = false;
    let x = 0;
    let y = 0;
    
    canvas.addEventListener("mousedown", (event) => {
        // console.log(event);
        if (event.button !== MOUSE_LEFT_BUTTON) {
            return;
        }
        
        x = event.layerX;
        y = event.layerY;
        
        isDrawing = true;
    });
    
    canvas.addEventListener("mousemove", (event) => {
        // console.log(event);
        // console.log(event.layerX, event.layerY);
        if (event.button !== MOUSE_LEFT_BUTTON) {
            return;
        }
        
        if (isDrawing) {
            drawLine(ctx, "black", 1, x, y, event.layerX, event.layerY);
            x = event.layerX;
            y = event.layerY;
        }
    });
    
    canvas.addEventListener("mouseup", (event) => {
        // console.log(event);
        if (event.button !== MOUSE_LEFT_BUTTON) {
            return;
        }
        
        if (isDrawing) {
            drawLine(ctx, "black", 1, x, y, event.layerX, event.layerY);
            x = 0;
            y = 0;
            isDrawing = false;
        }
    });
}

function drawLine(context, strokeStyle, lineWidth, x0, y0, x1, y1) {
    context.beginPath();
    context.strokeStyle = strokeStyle;
    context.lineWidth = lineWidth;
    context.moveTo(x0, y0);
    context.lineTo(x1, y1);
    context.stroke();
}

效果

JavaScript - canvas - 使用鼠标画线,带撤销与重做功能_第3张图片

添加撤销与重做功能

HTML

index.html 文件:

DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8" />
        <title>使用鼠标画线,带撤销与重做功能title>
        <link href="css/main.css" rel="stylesheet">
    head>
    <body>
        <canvas id="canvas">canvas>
        <input type="button" id="undo" value="undo" />
        <input type="button" id="redo" value="redo" />
        
        <script src="js/pako.js">script> // 压缩与解压:http://nodeca.github.io/pako/
        <script src="js/main.js">script>
    body>
html>

JavaScript

main.js 文件:

window.onload = (event) => {
    // console.log(event);
    main();
}

function main() {
    const canvas = document.getElementById("canvas");
    const undo = document.querySelector("#undo");
    const redo = document.querySelector("#redo");
    
    // 设置画布的宽高
    canvas.width = 400;
    canvas.height = 400;
    
    // 禁用撤销与重做按钮
    undo.disabled = true;
    redo.disabled = true;
    
    const ctx = canvas.getContext("2d");
    
    if (!ctx) return;
    
    const undoHistory = [];
    const redoHistory = [];
    
    const MOUSE_LEFT_BUTTON = 0; // https://developer.mozilla.org/en-US/docs/Web/API/MouseEvent/button
    
    let isDrawing = false;
    let x = 0;
    let y = 0;
    
    // 鼠标按下事件
    canvas.addEventListener("mousedown", (event) => {
        // console.log(event);
        
        if (event.button !== MOUSE_LEFT_BUTTON) {
            return;
        }
        
        {
            // 保存画布的像素数据
            const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
            const compressed = pako.deflate(new Uint8Array(imageData.data)); // 压缩,减小存储开销
            undoHistory.push(compressed);
            
            redoHistory.splice(0, redoHistory.length); // 删除所有内容
            
            undo.disabled = false;
            redo.disabled = true;
        }
        
        {
            // 保存鼠标在画布中的坐标
            x = event.layerX;
            y = event.layerY;
            
            isDrawing = true;
        }
    });
    
    // 鼠标移动事件
    canvas.addEventListener("mousemove", (event) => {
        // console.log(event);
        // console.log(event.layerX, event.layerY);
        
        if (event.button !== MOUSE_LEFT_BUTTON) {
            return;
        }
        
        if (isDrawing) {
            drawLine(ctx, "black", 1, x, y, event.layerX, event.layerY);
            x = event.layerX;
            y = event.layerY;
        }
    });
    
    // 鼠标松开事件
    canvas.addEventListener("mouseup", (event) => {
        console.log(event);
        
        if (event.button !== MOUSE_LEFT_BUTTON) {
            return;
        }
        
        if (isDrawing) {
            drawLine(ctx, x, y, event.layerX, event.layerY);
            x = 0;
            y = 0;
            isDrawing = false;
        }
    });
    
    undo.onclick = (event) => {
        // console.log(event);
        
        // 在撤销之前,保存当前画布的像素数据
        {
            const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
            const compressed = pako.deflate(new Uint8Array(imageData.data));
            redoHistory.push(compressed);
            
            redo.disabled = false;
        }
        
        // 撤销
        {
            const compressed = undoHistory.pop();
            
            try {
                const decompressed = pako.inflate(compressed); // 解压
                const uint8ClampedArray = new Uint8ClampedArray(decompressed);
                const imageData = new ImageData(uint8ClampedArray, canvas.width, canvas.height);
                
                ctx.putImageData(imageData, 0, 0);
            } catch (error) {
                console.error(error);
            }
            
            if (undoHistory.length === 0) {
                undo.disabled = true;
            }
        }
    }
    
    redo.onclick = (event) => {
        // console.log(event);
        
        // 在重做之前,保存当前画布的像素数据
        {
            const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
            const compressed = pako.deflate(new Uint8Array(imageData.data));
            undoHistory.push(compressed);
            
            undo.disabled = false;
        }
        
        // 重做
        {
            const compressed = redoHistory.pop();
            
            try {
                const decompressed = pako.inflate(compressed);
                const uint8ClampedArray = new Uint8ClampedArray(decompressed);
                const imageData = new ImageData(uint8ClampedArray, canvas.width, canvas.height);
                
                ctx.putImageData(imageData, 0, 0);
            } catch (error) {
                console.log(error);
            }
            
            if (redoHistory.length === 0) {
                redo.disabled = true;
            }
        }
    }
}

function drawLine(context, strokeStyle, lineWidth, x0, y0, x1, y1) {
    context.beginPath();
    context.strokeStyle = strokeStyle;
    context.lineWidth = lineWidth;
    context.moveTo(x0, y0);
    context.lineTo(x1, y1);
    context.stroke();
}

效果

JavaScript - canvas - 使用鼠标画线,带撤销与重做功能_第4张图片

你可能感兴趣的:(HTML,&,CSS,&,JavaScript,javascript,css,canvas,撤销,重做)