使用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_FIELD
和DATA_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
函数调用了initStyle
和bindEvent
函数来初始化画布样式并绑定事件监听器。
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——前端小野森森