使用 HTML5 Canvas 创建交互式橡皮擦工具 🚀

使用 HTML5 Canvas 创建交互式橡皮擦工具

您是否曾经想过构建一个工具,让用户可以在您的网站上以交互方式擦除图片的某些部分?在本文中,我将向您详细介绍如何使用 **HTML5 Canvas**、**JavaScript** 和一些 **事件处理魔法** 创建 **橡皮擦工具**。

这个项目是关于什么的?

该项目涉及开发基于网络的橡皮擦工具,让用户可以:

  • 上传图片。
  • 使用鼠标或触摸手势擦除部分内容。
  • 下载修改后的图像。
  • 此工具非常适合需要创造性用户输入的网络应用程序,例如数字白板、绘图应用程序或交互式教育工具。

    工作原理

    橡皮擦功能是使用“canvas”元素和 JavaScript 的“globalCompositeOperation”构建的。这使我们能够通过混合像素来“擦除”画布的某些部分。

    主要特点包括:

  • 支持鼠标和触摸事件。
  • 动态画布调整大小以实现响应行为。
  • 使用节流更新实现高效渲染,实现顺畅的交互。
  • 代码:快速概览

    1.设置 HTML

    以下是该页面的基本结构:

    
    
    
        
        
        Interactive Eraser Tool
        
    
    
        
        
        
        
        
    
    

    2. Eraser 类

    核心功能封装在模块化的“Eraser”类中。以下是对其功能的高级解释:

  • 初始化画布:根据上传的图像尺寸动态调整画布大小。
  • 处理事件:绑定 touchstart、mousemove 和 mouseup 事件进行擦除。
  • 主要逻辑如下:

    ((exports) => {
        const { document } = exports;
        const hastouch = 'ontouchstart' in exports;
        const tapstart = hastouch ? 'touchstart' : 'mousedown';
        const tapmove = hastouch ? 'touchmove' : 'mousemove';
        const tapend = hastouch ? 'touchend' : 'mouseup';
    
        let x1, y1, x2, y2;
    
        const options_type = {
            tap_start_x1: 400,
            tap_start_y1: 30,
            tap_move_x2: 900,
            tap_move_y2: 25
        };
    
        class Eraser {
            constructor(canvas, imgUrl, options = options_type) {
                this.canvas = canvas;
                this.ctx = canvas.getContext('2d');
                this.imgUrl = imgUrl;
                this.timer = null;
                this.lineWidth = 55;
                this.gap = 10;
                this.options = options;
            }
    
            init(args) {
                Object.assign(this, args);
                const img = new Image();
    
                this.canvasWidth = this.canvas.width = Math.min(document.body.offsetWidth, 640);
                img.crossOrigin = "*";
                img.onload = () => {
                    this.canvasHeight = (this.canvasWidth * img.height) / img.width;
                    this.canvas.height = this.canvasHeight;
                    this.ctx.drawImage(img, 0, 0, this.canvasWidth, this.canvasHeight);
                    this.initEvent();
                };
                img.src = this.imgUrl;
            }
    
            initEvent() {
                this.ctx.lineCap = 'round';
                this.ctx.lineJoin = 'round';
                this.ctx.lineWidth = this.lineWidth;
                this.ctx.globalCompositeOperation = 'destination-out';
    
                this.tapMoveHandler = this.onTapMove.bind(this);
                this.tapStartHandler = this.onTapStart.bind(this);
                this.tapEndHandler = this.onTapEnd.bind(this);
    
                this.tapStartHandler();
                this.tapEndHandler();
            }
    
            onTapStart() {
                x1 = this.options.tap_start_x1 - this.canvas.offsetLeft;
                y1 = this.options.tap_start_y1 - this.canvas.offsetTop;
    
                this.ctx.beginPath();
                this.ctx.arc(x1, y1, 1, 0, 2 * Math.PI);
                this.ctx.fill();
                this.ctx.stroke();
    
                this.tapMoveHandler();
            }
    
            onTapMove() {
                if (!this.timer) {
                    this.timer = setTimeout(() => {
                        x2 = this.options.tap_move_x2 - this.canvas.offsetLeft;
                        y2 = this.options.tap_move_y2 - this.canvas.offsetTop;
    
                        this.ctx.moveTo(x1, y1);
                        this.ctx.lineTo(x2, y2);
                        this.ctx.stroke();
    
                        x1 = x2;
                        y1 = y2;
                        this.timer = null;
                    }, 40);
                }
            }
    
            onTapEnd() {
                let count = 0;
                const imgData = this.ctx.getImageData(0, 0, this.canvasWidth, this.canvasHeight);
    
                for (let x = 0; x < imgData.width; x += this.gap) {
                    for (let y = 0; y < imgData.height; y += this.gap) {
                        const i = (y * imgData.width + x) * 4;
                        if (imgData.data[i + 3] > 0) {
                            count++;
                        }
                    }
                }
    
                if (count / (imgData.width * imgData.height / (this.gap ** 2)) < 0.6) {
                    setTimeout(() => {
                        this.removeEvent();
                        document.body.removeChild(this.canvas);
                        this.canvas = null;
                    }, 40);
                } else {
                    this.tapMoveHandler();
                }
            }
    
            removeEvent() {
                this.tapStartHandler();
                this.tapEndHandler();
                this.tapMoveHandler();
            }
        }
    
        exports.Eraser = Eraser;
    })(window);

    3. 综合起来

    我们将“Eraser”类与用户输入结合使用:

    为什么要使用此工具?

    此橡皮擦工具有多种用途:

  • 交互式图像编辑:允许用户直接在您的网站上自定义或修改图像。
  • 教育工具:为艺术或科学构建交互式学习模块。
  • 游戏:创建涉及触摸或拖动手势的简单游戏。
  • 未来的改进

    以下是一些可以添加的增强功能:

  • 撤消/重做功能。
  • 可定制的橡皮擦尺寸。
  • 适合移动设备的触摸手势。
  • 最后的想法

    构建像这样的交互式 **canvas** 工具是一个令人兴奋的项目,它融合了 **HTML5 Canvas**、**JavaScript** 和一点创造力。无论您是为了好玩还是作为大型 Web 应用程序的一部分构建它,可能性都是无穷无尽的!