如何使用canvas绘制多个图形块,并可以拖动。

在日常项目开发中,我们或多或少会遇到使用canvas的场景,下面我分享一个使用canvas绘制多个图形,并可以进行拖动的案例,例如:

在这里插入图片描述

通过canvas手册,我们可以获取到绘制图形的方法:ctx.fillRect(x, y, width, height),其中,x, y 分别为绘制的起始坐标,width, height 分别为绘制的宽度和高度,但是这种方法有个弊端,如果我们是通过鼠标来绘制,点击开始,移动,鼠标抬起,绘制完成,那么,对于上述的方法来说,局限性就比较大。

当然,也不是说没有解决办法,canvas中提供了另外的一种绘制图形方式:例如:

var ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.moveTo(75, 50);
ctx.lineTo(100, 75);
ctx.lineTo(100, 25);
ctx.fill();

这种方式使我们的绘制更加灵活,我们可以多次使用ctx.lineTo(x, y) 来绘制我们想要的图形路径,最后进行填充,得到最终图形。

那么现在,我们就通过上述的方法,通过鼠标来绘制不同大小的长方形,并可以进行拖动更换位置。
按照通常惯例,我们需要获取canvas,并设置相关的宽高:

<!--
 * @Author: ChenJinZhu
 * @Date: 2023-04-15 15:55:29
 * @Contact:1483364913@qq.com
 * @FilePath: F:\桌面文件\demo.html
-->
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div>
        <canvas id="canvas"></canvas>
    </div>
</body>

<script>
    // 获取canvas
    const canvas = document.getElementById('canvas');
    const ctx = canvas.getContext('2d');

    // 设置宽高
    const w = 1200, h = 800;
    canvas.width = w * devicePixelRatio;
    canvas.height = h * devicePixelRatio;

</script>
</html>

接下来,就是使用canvas提供的方法,来绘制。
因为是需要绘制多个,所以,我定义了一个构造函数,使用这个函数,生成数组对象,通过调用对象内部的绘制方法就能绘制出相关图形:

class RectFactory {
    /**
     * @startX:鼠标 点击时 的坐标轴 X 值
     * @startY:鼠标 点击时 的坐标轴 Y 值
     * @endX:鼠标 抬起时 的坐标轴 X 值
     * @endY:鼠标 抬起时 的坐标轴 Y 值
     * */
    constructor(startX, startY, endX, endY) {
        this.startX = startX;
        this.startY = startY;
        this.endX = endX;
        this.endY = endY;

    }
    // 为了保持不同的鼠标绘制方向都能绘制出图形,对坐标进行处理。
    get minX() {
        return Math.min(this.startX, this.endX);
    }
    get maxX() {
        return Math.max(this.startX, this.endX);
    }
    get minY() {
        return Math.min(this.startY, this.endY);
    }
    get maxY() {
        return Math.max(this.startY, this.endY);
    }

    draw() {
        ctx.beginPath();
        ctx.moveTo(this.minX * devicePixelRatio, this.minY * devicePixelRatio);
        ctx.lineTo(this.maxX * devicePixelRatio, this.minY * devicePixelRatio);
        ctx.lineTo(this.maxX * devicePixelRatio, this.maxY * devicePixelRatio);
        ctx.lineTo(this.minX * devicePixelRatio, this.maxY * devicePixelRatio);
        ctx.lineTo(this.minX * devicePixelRatio, this.minY * devicePixelRatio);
        // 填充颜色
        ctx.fillStyle = '#ffb60f';
        ctx.fill();
        ctx.strokeStyle = '#fff';
        ctx.lineCap = 'square';
        ctx.lineWidth = 1 * devicePixelRatio;
        ctx.stroke();
    }
}

图形的绘制构造函数写完之后,我们只需要对对应的鼠标函数进行完善即可:

canvas.onmousedown = (e) => {
    // 鼠标按下时,需要获取canvas相对浏览器视窗的位置
    const rect = canvas.getBoundingClientRect();
    // 获取鼠标按下时,相对canvas的位置。
    // 因为canvas的坐标系是从左上角开始的,所以需要减去canvas相对浏览器视窗的位置。
    const clickX = e.clientX - rect.left;
    const clickY = e.clientY - rect.top;
    // 执行绘制方法
    drawRect(e, clickX, clickY, rect)
}

const drawRect = (e, clickX, clickY, rect) => {
    // 鼠标按下,立即通过构造函数创建新的对象
    const shape = new RectFactory(clickX, clickY)
    
    // 全局定义的数组,每次鼠标按下,就将创建好的对象推入数组中。
    shapeCollection.push(shape)
    
    // 执行鼠标移动事件,移动时,同时修改当前对象的结束坐标
    window.onmousemove = (evt) => {
        ctx.clearRect(0, 0, canvas.width, canvas.height);
        shape.endX = evt.clientX - rect.left;
        shape.endY = evt.clientY - rect.top;
    }
}

最后,我们只需要一直循环 shapeCollection,并调用里面的 draw() 方法,即可:

const draw = () => {
    requestAnimationFrame(draw)
    for (const pp of shapeCollection) {
        pp.draw()
    }
}
draw()

到此,我们就可以随便绘制图形咯。但是,此时功能还未结束,我们还需要拖动的功能,那么,在点击的时候,只需要判断下当前的位置是不是在图形的内部,此判断方法,需要加在构造函数内部,所以,只需要对代码进一步修改即可,以下是次功能的全部代码:

<!--
 * @Author: ChenJinZhu
 * @Date: 2023-04-15 15:55:29
 * @Contact:1483364913@qq.com
 * @FilePath: F:\桌面文件\demo.html
-->
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>
    <div>
        <canvas id="canvas" style="background-color: #94e8ea"></canvas>
    </div>
</body>

<script>
    // 获取canvas
    const canvas = document.getElementById('canvas');
    const ctx = canvas.getContext('2d');

    // 所有绘制的图形推入数组中。
    let shapeCollection = []

    // 设置宽高
    const w = 1200, h = 800;
    canvas.width = w * devicePixelRatio;
    canvas.height = h * devicePixelRatio;
    class RectFactory {
        /**
         * @startX:鼠标 点击时 的坐标轴 X 值
         * @startY:鼠标 点击时 的坐标轴 Y 值
         * @endX:鼠标 抬起时 的坐标轴 X 值
         * @endY:鼠标 抬起时 的坐标轴 Y 值
         * */
        constructor(startX, startY, endX, endY) {
            this.startX = startX;
            this.startY = startY;
            this.endX = endX;
            this.endY = endY;

        }
        // 为了保持不同的鼠标绘制方向都能绘制出图形,对坐标进行处理。
        get minX() {
            return Math.min(this.startX, this.endX);
        }
        get maxX() {
            return Math.max(this.startX, this.endX);
        }
        get minY() {
            return Math.min(this.startY, this.endY);
        }
        get maxY() {
            return Math.max(this.startY, this.endY);
        }

        draw() {
            ctx.beginPath();
            ctx.moveTo(this.minX * devicePixelRatio, this.minY * devicePixelRatio);
            ctx.lineTo(this.maxX * devicePixelRatio, this.minY * devicePixelRatio);
            ctx.lineTo(this.maxX * devicePixelRatio, this.maxY * devicePixelRatio);
            ctx.lineTo(this.minX * devicePixelRatio, this.maxY * devicePixelRatio);
            ctx.lineTo(this.minX * devicePixelRatio, this.minY * devicePixelRatio);
            ctx.fillStyle = '#ffb60f';
            ctx.fill();
            ctx.strokeStyle = '#fff';
            ctx.lineCap = 'square';
            ctx.lineWidth = 1 * devicePixelRatio;
            ctx.stroke();
        }

        // 判断当前点击位置是否在图形内部
        isInside(x, y) {
            return x >= this.minX && x <= this.maxX && y >= this.minY && y <= this.maxY
        }
    }

    canvas.onmousedown = (e) => {
        const rect = canvas.getBoundingClientRect();
        const clickX = e.clientX - rect.left;
        const clickY = e.clientY - rect.top;
        const shape = getRect(clickX, clickY)

        if (shape) {
            moveRect(e, clickX, clickY, rect, shape)
        } else {
            drawRect(e, clickX, clickY, rect)
        }

    }
    canvas.onmouseup = () => {
        canvas.onmousemove = null;
        window.onmousemove = null;
    };

    // 鼠标点击canvas查看是否点击到了已经绘制的路线,若是,则返回相关线的对象,若否,返回null
    const getRect = (x, y) => {
        for (let i = shapeCollection.length - 1; i >= 0; i--) {
            const element = shapeCollection[i];
            if (element.isInside(x, y)) {
                return element;
            }
        }
        return null
    }

    const drawRect = (e, clickX, clickY, rect) => {
        const shape = new RectFactory(clickX, clickY)
        shapeCollection.push(shape)
        window.onmousemove = (evt) => {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            shape.endX = evt.clientX - rect.left;
            shape.endY = evt.clientY - rect.top;
        }
    }

    const moveRect = (e, clickX, clickY, rect, shape) => {
        const { startX, startY, endX, endY } = shape;
        window.onmousemove = (evt) => {
            ctx.clearRect(0, 0, canvas.width, canvas.height);
            const distanceX = evt.clientX - rect.left - clickX;
            const distanceY = evt.clientY - rect.top - clickY;
            shape.startX = startX + distanceX;
            shape.startY = startY + distanceY;
            shape.endX = endX + distanceX;
            shape.endY = endY + distanceY;
        }
    };

    const draw = () => {
        requestAnimationFrame(draw)
        for (const pp of shapeCollection) {
            pp.draw()
        }
    }
    draw()
</script>
</html>

如果此篇文章对您有一定帮助,请给一个赞吧~,您的赞,是对我最大的鼓励 ٩(๑❛ᴗ❛๑)۶

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
第1章 基础知识 1 1.1 canvas元素 1 1.1.1 canvas元素的大小与绘图表面的大小 4 1.1.2 canvas元素的api 5 1.2 canvas的绘图环境 6 1.2.1 2d绘图环境 6 1.2.2 canvas状态的保存与恢复 8 1.3 本书程序清单的规范格式 9 1.4 开始学习html5 10 1.4.1 规范 10 1.4.2 浏览器 11 1.4.3 控制台与调试器 11 1.4.4 性能 13 1.5 基本的绘制操作 15 1.6 事件处理 18 1.6.1 鼠标事件 18 1.6.2 键盘事件 22 .1.6.3 触摸事件 23 1.7 绘制表面的保存与恢复 23 1.8 在canvas使用html元素 25 1.9 打印canvas的内容 32 1.10 离屏canvas 35 1.11 基础数学知识简介 37 1.11.1 求解代数方程 37 1.11.2 三角函数 38 1.11.3 向量运算 39 1.11.4 根据计量单位来推导等式 42 1.12 总结 44 第2章 绘制 45 2.1 坐标系统 46 2.2 canvas绘制模型 47 2.3 矩形的绘制 48 2.4 颜色与透明度 50 2 .5 渐变色与图案 52 2.5.1 渐变色 52 2.5.2 图案 54 2.6 阴影 57 2.7 路径、描边与填充 60 2.7.1 路径与子路径 63 2.7.2 剪纸效果 64 2.8 线段 69 2.8.1 线段与像素边界 70 2.8.2 网格的绘制 71 2.8.3 坐标轴的绘制 72 2.8.4 橡皮筋式的线条绘制 74 2.8.5 虚线的绘制 79 2.8.6 通过扩展canvasrenderingcontext2d来绘制虚线 80 2.8.7 线段端点与连接点的绘制 81 2.9 圆弧与圆形的绘制 83 2.9.1 arc()方法的用法 83 2.9.2 以橡皮筋式辅助线来协助用户画圆 85 2.9.3 arcto()方法的用法 86 2.9.4 刻度仪表盘的绘制 88 2.10 贝塞尔曲线 93 2.10.1 二次方贝塞尔曲线 93 2.10.2 三次方贝塞尔曲线 95 2.11 多边形的绘制 97 2.12 高级路径操作 102 2.12.1 拖动多边形对象 102 2.12.2 编辑贝塞尔曲线 107 2.12.3 自动滚动网页,使某段路径所对应的元素显示在视窗中 115 2.13 坐标变换 116 2.13.1 坐标系的平移、缩放与旋转 116 2.13.2 自定义的坐标变换 119 2.14 图像合成 123 2.15 剪辑区域 128 2.15.1 通过剪辑区域来擦除图像 128 2.15.2 利用剪辑区域来制作伸缩式动画 133 2.16 总结 135 第3章 文本 137 3.1 文本的描边与填充 137 3.2 设置字型属性 141 3.3 文本的定位 144 3.3.1 水平与垂直定位 144 3.3.2 将文本居中 146 3.3.3 文本的度量 147 3.3.4 绘制坐标轴旁边的文本标签 148 3.3.5 绘制数值仪表盘周围的文本标签 151 3.3.6 在圆弧周围绘制文本 152 3.4 实现文本编辑控件 154 3.4.1 指示文本输入位置的光标 154 3.4.2 在canvas中编辑文本 159 3.4.3 文本段的编辑 163 3.5 总结 174 第4章 图像与视频 175 4.1 图像的绘制 176 4.1.1 在canvas之中绘制图像 176 4.1.2 drawimage()方法的用法 177 4.2 图像的缩放 179 4.3 将一个canvas绘制到另一个canvas之中 183 4.4 离屏canvas 186 4.5 操作图像的像素 189 4.5.1 获取图像数据 189 4.5.2 修改图像数据 195 4.6 结合剪辑区域来绘制图像 208 4.7 以图像制作动画 211 4.8 图像绘制的安全问题 216 4.9 性能 216 4.9.1 对比drawimage(htmlimage)、drawimage(htmlcanvas)与putimagedata()的绘图效率 217 4.9.2 在canvas绘制另一个canvas绘制普通图像之间的对比;在绘制时缩放图像与保持原样之间的对比 217 4.9.3 遍历图像数据 218 4.10 放大镜 222 4.10.1 使用离屏canvas 224 4.10.2 接受用户从文件系统中拖放进来的图像 225 4.11 视频处理 227 4.11.1 视频格式 227 4.11.2 在canvas中播放视频 229 4.11.3 视频处理 230 4.12 总结 234 第5章 动画 235 5.1 动画循环 235 5.1.1 通过requestanimationframe()方法让浏览器来自行决定帧速率 237 5.1.2 internet explorer浏览器对requestanimationframe()功能的实现 241 5.1.3 可移植于各浏览器平台的动画循环逻辑 241 5.2 帧速率的计算 248 5.3 以不同的帧速率来执行各种任务 249 5.4 恢复动画背景 250 5.4.1 利用剪辑区域来处理动画背景 250 5.4.2 利用图复制技术来处理动画背景 252 5.5 利用双缓冲技术绘制动画 253 5.6 基于时间的运动 254 5.7 背景的滚动 257 5.8 视差动画 261 5.9 用户手势 264 5.10 定时动画 266 5.10.1 秒表 266 5.10.2 动画计时器 269 5.11 动画制作的最佳指导原则 270 5.12 总结 271 第6章 精灵 272 6.1 精灵概述 273 6.2 精灵绘制器 275 6.2.1 描边与填充绘制器 275 6.2.2 图像绘制器 279 6.2.3 精灵表绘制器 281 6.3 精灵对象的行为 284 6.3.1 将多个行为组合起来 285 6.3.2 限时触发的行为 287 6.4 精灵动画制作器 289 6.5 基于精灵的动画循环 293 6.6 总结 294 第7章 物理效果 295 7.1 重力 295 7.1.1 物体的下落 296 7.1.2 抛射体弹道运动 298 7.1.3 钟摆运动 307 7.2 时间轴扭曲 311 7.3 时间轴扭曲函数 315 7.4 时间轴扭曲运动 317 7.4.1 没有加速度的线性运动 319 7.4.2 逐渐加速的缓入运动 320 7.4.3 逐渐减速的缓出运动 322 7.4.4 缓入缓出运动 323 7.4.5 弹簧运动与弹跳运动 324 7.5 以扭曲后的帧速率播放动画 326 7.6 总结 332 第8章 碰撞检测 333 8.1 外接图形判别法 333 8.1.1 外接矩形判别法 333 8.1.2 外接圆判别法 334 8.2 碰到墙壁即被弹回的小球 336 8.3 光线投射法 337 8.4 分离轴定理(sat)与最小平移向量(mtv) 340 8.4.1 使用分割轴定理检测碰撞 340 8.4.2 根据最小平移向量应对碰撞 362 8.5 总结 373 第9章 游戏开发 374 9.1 游戏引擎 374 9.1.1 游戏循环 376 9.1.2 加载图像 382 9.1.3 同时播放多个声音 384 9.1.4 键盘事件 385 9.1.5 高分榜 386 9.1.6 游戏引擎源代码 387 9.2 游戏原型 395 9.2.1 游戏原型程序的html代码 396 9.2.2 原型程序的游戏循环 399 9.2.3 游戏原型程序的加载画面 400 9.2.4 暂停画面 402 9.2.5 按键监听器 404 9.2.6 游戏结束及高分榜 404 9.3 弹珠台游戏 407 9.3.1 游戏循环弹珠 408 9.3.2 弹珠精灵 410 9.3.3 重力与摩擦力 411 9.3.4 弹板的移动 412 9.3.5 处理键盘事件 413 9.3.6 碰撞检测 416 9.4 总结 425 第10章 自定义控件 426 10.1 圆角矩形控件 427 10.2 进度条控件 433 10.3 滑动条控件 437 10.4 图像查看器控件 446 10.5 总结 454 第11章 移动平台开发 455 11.1 移动设备的视窗 456 11.2 媒体特征查询技术 461 11.2.1 媒体特征查询与css 461 11.2.2 用javascript程序应对媒体特征的变化 462 11.3 触摸事件 464 11.3.1 touchevent对象 464 11.3.2 touchlist对象 465 11.3.3 touch对象 466 11.3.4 同时支持触摸事件与鼠标事件 466 11.3.5 手指缩放 468 11.4 ios5 469 11.4.1 应用程序图标及启动画面 469 11.4.2 利用媒体特征查询技术设置ios5系统的应用程序图标及启动画面 470 11.4.3 以不带浏览器饰件的全屏模式运行应用程序 471 11.4.4 应用程序的状态栏 471 11.5 虚拟键盘 472 11.6 总结 485
### 回答1: 您可以使用fabric.js提供的方法进行拖动选择多个元素,然后通过遍历所选择的元素并使用remove方法将它们删除。下面是一个示例代码: ```javascript // 选中多个元素 var selectedObjects = canvas.getActiveObjects(); // 遍历并删除选中的元素 for(var i = 0; i < selectedObjects.length; i++){ canvas.remove(selectedObjects[i]); } ``` 其中,canvas为您绘制图形的对象。您可以根据自己的情况修改代码。 ### 回答2: 在fabric.js中,可以通过拖动选中多个元素然后进行删除操作。具体步骤如下: 1. 首先,需要创建一个canvas对象来显示和操作元素。可以使用如下代码创建一个canvas对象: ```javascript var canvas = new fabric.Canvas('canvas'); ``` 2. 接下来,需要在canvas上添加一些元素。可以使用如下代码来添加一个矩形和一个圆形元素: ```javascript var rect = new fabric.Rect({ left: 100, top: 100, width: 100, height: 100, fill: 'red' }); var circle = new fabric.Circle({ left: 200, top: 200, radius: 50, fill: 'blue' }); canvas.add(rect, circle); ``` 3. 然后,需要启用多选功能,使得可以通过拖动选中多个元素。可以使用如下代码启用多选功能: ```javascript canvas.selection = true; ``` 4. 最后,可以监听canvas上的鼠标事件,当鼠标拖动选中多个元素时,可以获取到选中的元素,并进行删除操作。可以使用如下代码监听mouse:up事件: ```javascript canvas.on('mouse:up', function(event) { var selectedObjects = canvas.getActiveObjects(); if (selectedObjects.length > 1) { canvas.remove.apply(canvas, selectedObjects); } }); ``` 上述代码首先通过 `getActiveObjects` 方法获取到所有选中的元素,然后判断选中元素数量大于1时,使用 `remove` 方法将选中的元素从canvas中删除。 通过以上步骤,就可以实现在fabric.js中拖动选中多个元素然后进行删除操作。 ### 回答3: 使用 fabric.js 拖动选中多个元素删除,可以按照以下步骤进行操作: 1. 首先,需要设置 canvas 为可选中多个元素的模式。可以通过设置 canvas 的属性 `selection` 为 `true` 来实现,例如 `canvas.selection = true;`。 2. 接下来,需要在 canvas 上绑定鼠标按下和鼠标移动事件。具体可以使用 `canvas.on('mouse:down', function(event) {})` 和 `canvas.on('mouse:move', function(event) {})` 方法来实现。 3. 在鼠标按下事件中,可以获取鼠标按下的坐标 `event.pointer`。然后,需要创建一个矩形对象,用于表示选择区域。 4. 在鼠标移动事件中,可以获取鼠标移动的坐标 `event.pointer`。然后,根据鼠标按下的坐标和移动的坐标,计算出矩形的位置和大小。可以使用 `rectangle.set({left: x, top: y, width: width, height: height})` 方法来设置矩形的属性。 5. 形成矩形选区后,需要判断是否有元素被选中。可以使用 `canvas.getObjects()` 方法获取所有的元素对象,然后使用 `canvas.getItemsInArea(rectangle)` 方法获取在选区内的元素。 6. 最后,遍历被选中的元素,使用 `canvas.remove(object)` 方法将其从 canvas 上删除。 以上就是使用 fabric.js 实现拖动选中多个元素删除的步骤。这样,在拖动鼠标形成选区后,选中的元素将被删除。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值