使用Canvas制作画板

使用Canvas制作画板

在本篇技术博客中,我们将使用JavaScript和Canvas技术来创建一个简单的画板应用程序。这个画板将允许用户在一个画布上绘制线条,使用橡皮擦擦除绘制的内容,更改线条的颜色和宽度,并支持撤销和重做功能。

准备工作

在开始之前,我们需要一个HTML文件来设置画板的基本结构。请创建一个index.html文件,并将以下内容复制到其中:

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Canvas 画板</title>
</head>
<body><canvas id="can"></canvas><input type="color" id="colorInput"><input type="range" id="lineWidthRange" min="1" max="20" step="1"><span id="lineWidthValue">1</span><button id="clearAllBtn">清空画板</button><button id="eraserBtn">使用橡皮擦</button><input type="range" id="eraserLineWidthRange" min="1" max="20" step="1"><span id="eraserLineWidthValue">1</span><div id="eraserCircle"></div><script src="app.js"></script>
</body>
</html>

接下来,我们需要一个app.js文件,用于编写JavaScript代码。请在项目根目录下创建一个app.js文件,并将你提供的代码复制到其中。

实现功能

现在,我们将详细解释一下代码中的每个部分,并说明它们如何协同工作来创建一个完整的画板应用。

画布初始化

const oCan = document.getElementById('can');
const ctx = oCan.getContext('2d');const oColorInput = document.getElementById('colorInput');
const oLineWidthRange = document.getElementById('lineWidthRange');
const oLineWidthValue = document.getElementById('lineWidthValue');
const oClearAllBtn = document.getElementById('clearAllBtn');
const oEraserBtn = document.getElementById('eraserBtn');
const oEraserLineWidthRange = document.getElementById('eraserLineWidthRange');
const oEraserLineWidthValue = document.getElementById('eraserLineWidthValue');
const oEraserCircle = document.getElementById('eraserCircle');const clientWidth = document.documentElement.clientWidth;
const clientHeight = document.documentElement.clientHeight;oCan.width = clientWidth;
oCan.height = clientHeight;

以上代码片段获取了HTML元素和Canvas上下文对象,并设置了画布的宽度和高度等于视口的宽度和高度,以确保画布可以铺满整个屏幕。

状态和常量定义

const state = {initPos: null,eraserStatus: false,drewData: [],revokedData: []
}const DATA_FIELD = {DREW: 'drewData',REVOKED: 'revokedData'
}const DATA_TYPE = {MOVE_TO: 'moveTo',LINE_TO: 'lineTo'
}const CANVAS_VALUES = {DEFAULT_COLOR: '#000',DEFAULT_LINE_STYLE: 'round',DEFAULT_LINE_WIDTH: 1,ERASER_COLOR: '#fff'
}const KEYBOARD = {UNDO: 'z',REDO: 'b'
}

这部分代码定义了画板应用的状态和常量。state对象用于跟踪用户的操作和绘制数据。DATA_FIELDDATA_TYPE用于标识绘制数据的不同部分。CANVAS_VALUES定义了画布的一些默认值,包括默认颜色、线条样式和线条宽度。KEYBOARD定义了用于撤销和重做的键盘按键。

初始化和事件绑定

const init = () => {initStyle();bindEvent();
}function initStyle() {ctx.setColor(CANVAS_VALUES.DEFAULT_COLOR);ctx.setLineStyle(CANVAS_VALUES.DEFAULT_LINE_STYLE);ctx.setLineWidth(CANVAS_VALUES.DEFAULT_LINE_WIDTH);
}function bindEvent() {oCan.addEventListener('mousedown', handleCanvasMouseDown, false);oColorInput.addEventListener('click', handleColorInput, false);oColorInput.addEventListener('input', handleColorInput, false);oLineWidthRange.addEventListener('input', handleLineWidthRangeInput, false);oClearAllBtn.addEventListener('click', handleClearAllBtnClick, false);oEraserBtn.addEventListener('click', handleEraserBtnClick, false);oEraserLineWidthRange.addEventListener('input', handleEraserLineWidthRangeInput, false);document.addEventListener('keydown', handleKeyDown, false);
}

这部分代码定义了初始化和事件绑定函数。init函数调用了initStylebindEvent函数来初始化画布样式并绑定事件监听器。

initStyle函数设置了画布的默认颜色、线条样式和线条宽度。

bindEvent函数绑定了鼠标和键盘事件的处理函数,这些事件包括鼠标点击、颜色选择、线条宽度选择、清空画板、使用橡皮擦和撤销/重做操作。

绘制函数

function drawPoint(x, y) {ctx.beginPath();ctx.arc(x, y, ctx.lineWidth / 2, 0, 2 * Math.PI, false);ctx.fill();
}function drawLine({ x1, y1, x2, y2 }) {ctx.beginPath();ctx.moveTo(x1, y1);ctx.lineTo(x2, y2);ctx.stroke();
}function drawBatchLine() {clearAll();state[DATA_FIELD.DREW].forEach(item => {ctx.beginPath();const { moveTo: [x1, y1], lineTo, info: { color, width } } = item;ctx.setColor(color);ctx.setLineWidth(width);ctx.moveTo(x1, y1);lineTo.forEach(line => {ctx.lineTo(...line);});ctx.stroke();})
}function clearAll() {ctx.clearRect(0, 0, oCan.offsetWidth, oCan.offsetHeight);
}

以上代码片段包含了绘制相关的函数。drawPoint用于绘制一个点,drawLine用于绘制直线,drawBatchLine用于批量绘制用户的绘制数据,clearAll用于清空画布。

事件处理函数

// 省略其他事件处理函数...function handleKeyDown(e) {const key = e.key;console.log(key);if ((e.metaKey || e.ctrlKey) && (Object.values(KEYBOARD).includes(key))) {doDrewRecord(key);drawBatchLine();}if (!state[DATA_FIELD.DREW].length || !state[DATA_FIELD.REVOKED].length) {ctx.setColor(oColorInput.value);ctx.setLineWidth(oLineWidthRange.value);}
}function handleCanvasMouseDown(e) {// 省略其他事件处理代码...
}

以上代码片段包含了处理键盘事件和鼠标事件的函数。handleKeyDown函数用于处理键盘事件,当用户按下Ctrl键(或Cmd键在macOS上)加上Z或B键时,会触发撤销和重做操作。handleCanvasMouseDown函数用于处理鼠标按下事件,开始绘制操作。

辅助函数

// 省略其他辅助函数...function setDrewRecord(type, data) {switch (type) {case DATA_TYPE.MOVE_TO:state[DATA_FIELD.DREW].push({[DATA_TYPE.MOVE_TO]: [...data],[DATA_TYPE.LINE_TO]: [],info: {color: ctx.getColor(),width: ctx.getLineWidth()}})break;case DATA_TYPE.LINE_TO:const drewData = state[DATA_FIELD.DREW];drewData[drewData.length - 1][DATA_TYPE.LINE_TO].push([...data]);break;default:break;}
}function doDrewRecord(key) {switch (key) {case KEYBOARD.UNDO:state[DATA_FIELD.DREW].length > 0&&state[DATA_FIELD.REVOKED].push(state[DATA_FIELD.DREW].pop());break;case KEYBOARD.REDO:state[DATA_FIELD.REVOKED].length > 0&&state[DATA_FIELD.DREW].push(state[DATA_FIELD.REVOKED].pop());break;default:break;}
}
// 定义 EraserCircle 元素的显示与隐藏
oEraserCircle.setVisible = function (visible) {this.style.display = visible ? 'block' : 'none';
};// 定义 EraserCircle 元素的大小设置
oEraserCircle.setSize = function (size) {this.style.width = size + 'px';this.style.height = size + 'px';
};// 定义 EraserCircle 元素的位置设置
oEraserCircle.setPosition = function (x, y) {this.style.left = x - this.offsetWidth / 2 + 'px';this.style.top = y - this.offsetHeight / 2 + 'px';
};// 设置画笔颜色
ctx.setColor = function (color) {this.strokeStyle = color;this.fillStyle = color;
};// 获取画笔颜色
ctx.getColor = function () {return this.strokeStyle;
};// 设置线条样式(线帽和连接点)
ctx.setLineStyle = function (style) {this.lineCap = style;this.lineJoin = style;
};// 设置线条宽度
ctx.setLineWidth = function (width) {this.lineWidth = width;
};// 获取线条宽度
ctx.getLineWidth = function () {return this.lineWidth;
};

 这些函数是对代码中省略的部分进行了完善,主要用于设置橡皮擦元素的显示与隐藏,以及对画笔颜色、线条样式和线条宽度的设置与获取。这些函数在画板应用中起着重要的作用,使用户能够自由选择画笔颜色、线条样式和线条宽度,同时在使用橡皮擦功能时,橡皮擦元素能够正确显示在鼠标位置。

运行画板应用

将上述代码保存为app.js文件,并创建一个HTML文件将其引入。然后在浏览器中打开HTML文件,你就可以在画板上开始绘制、擦除、更改颜色和宽度,以及进行撤销和重做操作了。

 

 学习自B站up——前端小野森森

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/19504.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

检查 CPU 的上下文切换

一.什么是cpu上下文切换 CPU 上下文切换是操作系统在多任务环境下管理进程的一项关键任务。在现代计算机系统中&#xff0c;有多个进程同时运行&#xff0c;每个进程都需要一定的 CPU 时间来执行其任务。由于 CPU 在某一时刻只能执行一个进程的指令&#xff0c;因此操作系统需…

MySQL内置函数使用说明

MySQL函数使用说明 MySQL 是一个流行的关系型数据库管理系统&#xff0c;它提供了许多内置函数来处理和操作数据。这些函数可以简化数据库查询和操作的过程&#xff0c;提高代码的可读性和效率。以下是一些常见的 MySQL 内置函数及其使用说明和示例。 数值函数 ABS() 函数原…

静态资源导入探究

静态资源可以在哪里找呢&#xff1f;我们看看源码 从这个类进去 里面有个静态类 WebMvcAutoConfigurationAdapter 有个配置类&#xff0c;将这个类的对象创建并导入IOC容器里 这个静态类下有个方法 addResourceHandlers(ResourceHandlerRegistry registry)静态资源处理器 若自…

从零实现深度学习框架——Transformer从菜鸟到高手(一)

引言 &#x1f4a1;本文为&#x1f517;[从零实现深度学习框架]系列文章内部限免文章&#xff0c;更多限免文章见 &#x1f517;专栏目录。 本着“凡我不能创造的&#xff0c;我就不能理解”的思想&#xff0c;系列文章会基于纯Python和NumPy从零创建自己的类PyTorch深度学习框…

【环境配置】Windows下WSL将ubuntu挪位置-系统盘清理

问题–垃圾太多&#xff0c;系统盘空间占用太大 最近 C 盘空间暴涨&#xff0c;用工具 WinDirStat-强烈推荐的工具 查看发现 WSL 子系统占用了6个多 G 的空间&#xff0c;遂想办法挪个位置&#xff1b; 【关键字】将 Windows 里的子系统挪到非系统盘 D 盘&#xff1b; 解决 打…

生产服务器突然本机无法访问本机IP的端口

生产服务器突然本机无法访问本机IP的端口 一、现象描述 生产服务器突然无法访问自己本机IP地址的端口&#xff0c;通过localhost或者127.0.0.1都可以正常访问 二、问题分析 服务器是搭建在虚拟机上面&#xff0c;起初由于服务器内存不足的原因&#xff0c;导致了服务器故障无…

深度学习环境配置pytorch-GPU版本

一、下载与安装Anaconda 官网&#xff1a;https://www.anaconda.com/download 安装时添加环境变量勾选上&#xff0c;这样可以减少一步操作&#xff0c;不用再去自己手动添加了。 二、在anaconda里面创建虚拟环境 创建虚拟环境&#xff0c;其中pytorch为虚拟环境名&#xff0c;…

UNet 系列:做医学图像分割的任何人,都必须要会使用 nnU-Net

UNet 系列 UNet下采样和上采样跳跃连接 UNet&#xff1a;多层级和多尺度的密集链接nnUNet集成模型预处理训练过程推理后处理4行命令使用 nnUNet 训练自己的医学图像分割模型 UNet 经典的卷积神经网络都很深&#xff0c;越深的卷积层越适合处理大目标的东西&#xff0c;而医学病…

golang自带的命令行解析库flag库实践

1. 简介 flag用于解析命令行选项。有过类 Unix 系统使用经验的童鞋对命令行选项应该不陌生。例如命令ls -al列出当前目录下所有文件和目录的详细信息&#xff0c;其中-al就是命令行选项。 命令行选项在实际开发中很常用&#xff0c;特别是在写工具的时候。 指定配置文件的路径…

24考研数据结构-数组和特殊矩阵

目录 数据结构&#xff1a;数组与特殊矩阵数组数组的特点数组的用途 特殊矩阵对角矩阵上三角矩阵和下三角矩阵稀疏矩阵特殊矩阵的用途 结论 3.4 数组和特殊矩阵3.4.1数组的存储结构3.4.2普通矩阵的存储3.4.3特殊矩阵的存储1. 对称矩阵(方阵)2. 三角矩阵(方阵)3. 三对角矩阵(方阵…

图像处理之hough圆形检测

hough检测原理 点击图像处理之Hough变换检测直线查看 下面直接描述检测圆形的方法 基于Hough变换的圆形检测方法 对于一个半径为 r r r&#xff0c;圆心为 &#xff08; a , b &#xff09; &#xff08;a,b&#xff09; &#xff08;a,b&#xff09;的圆&#xff0c;我们将…

vscode里安装Go插件和配置Go环境

vscode是一款跨平台、轻量级、插件多的开源IDE&#xff0c;在vscode不仅可以配置C/C、Python、R、Ruby等语言的环境&#xff0c;还可以配置Go语言的环境。这里介绍在vscode里安装Go语言的插件和配置Go语言环境&#xff0c;系统是Win10 64位。 1、下载Go安装包和配置GOROOT、GO…

一年级数学 数一数(一到十)

今天我们来学习数一数 有一些老人 眼睛可能花了 需要我们在动物园数清楚是多少个动物 然后告诉他们 可能有的小朋友 不知道某些数字怎么读 您可以打开地址 https://fanyi.baidu.com/?aldtype16047#zh/en/ 将数字 输入到 输入框内 然后点击 下面的小话筒 系统就会读出来了 小…

高忆管理:多重利好共振 外资加码布局A股

资本商场活泼信号正在继续开释&#xff0c;内外资决心取得有力提振。以北向资金为代表的外资近来表现活泼&#xff0c;近六个买卖日已连续净买入超500亿元。多家外资组织近期表态称&#xff0c;伴跟着方针力度加强&#xff0c;我国经济有望继续复苏&#xff0c;活泼看好我国权益…

2023年8月美团外卖3-18元红包优惠券天天领取活动日历及美团外卖红包领取使用

2023年8月美团外卖3-18元红包天天领取活动日历 根据上图美团外卖红包领取活动时间表以下时间可以天天领取3-18元美团外卖红包优惠券&#xff1a; 1、2023年8月18日 可领取美团外卖18元神券节红包&#xff1b; 2、2023年8月每周六、周日每天可领取12元美团外卖节红包&#xff…

【C++】类和对象-继承

0.前言 1.基本语法 继承的用处就是极大的减少代码的重复性&#xff0c;如果没有用继承&#xff0c;看看以下代码&#xff0c;你知道了。。。。 基本实现代码&#xff1a; #include <iostream> using namespace std; /******************************************/void …

万界星空/推出生产制造执行MES系统/开源MES/免费下载

免费MES系统介绍 什么是MES系统呢&#xff1f;MES系统主要功能就是解决“如何生产”的问题。通过实施MES系统&#xff0c;一站式解决您所困扰的所有生产制作流程问题。 普通的免费MES系统只提供简单的基本功能让客户体验&#xff0c;而万界星空MES系统运用低代码的形式开发&a…

【二开】JeecgBoot-vue3二次开发 前端 扩展online表单js增强等-初始化列表之后执行

【二开】JeecgBoot-vue3二次开发 前端 扩展online表单js增强等-初始化列表之后执行 二开位置 OnlineAutoList.js.initAutoList 定义方法 /*** 初始化列表之后执行* js增强* param tableColumns* returns {Promise<void>|*}*/onlineTableContext["afterInitAutoList…

Codeforces Round 855 (Div. 3) E题题解

文章目录 [ Unforgivable Curse (hard version)](https://codeforces.com/contest/1800/problem/E2)问题建模问题分析方法1分析性质1.分析操作对元素位置的影响2.分析可以使用操作的元素可以与相邻元素交换位置的作用代码 方法2通过DFS得到相互可以交换位置的字符集合代码 方法…

Spring Boot的自动配置原理

一.原理解释 Spring Boot的自动配置是Spring框架的一个重要特性&#xff0c;它旨在简化应用程序的开发和部署过程。自动配置通过基于类路径中的依赖关系和配置文件内容来预先配置Spring应用程序的各种组件和功能。这样&#xff0c;我们可以在无需显式配置大量参数的情况下&…