Javascript与Canvas的绘图交互核心是通过监听Canvas元素的用户操作事件,结合Canvas的绘图API实现动态修改画布内容的效果,整个过程需要处理好事件坐标与画布坐标的映射、绘图状态的管理等问题。

Canvas交互的基础准备
首先需要在HTML中创建Canvas元素,并通过Javascript获取其上下文对象,后续的绘图操作都依赖这个上下文完成。
<canvas id="drawCanvas" width="800" height="500" style="border:1px solid #ccc"></canvas>
获取上下文的代码如下:
// 获取Canvas元素
const canvas = document.getElementById('drawCanvas');
// 获取2D绘图上下文
const ctx = canvas.getContext('2d');
事件监听与坐标转换
Canvas的交互依赖用户操作事件,最常用的是鼠标事件,包括点击、移动、按下、抬起等。需要注意的是,鼠标事件返回的坐标是相对于浏览器视口的位置,需要转换为Canvas画布内部的坐标才能正确对应绘图位置。
坐标转换方法
转换逻辑是获取Canvas元素相对于视口的偏移量,用鼠标事件的clientX、clientY减去偏移量即可得到画布内的坐标:
// 获取鼠标在Canvas内的坐标
function getCanvasPoint(e) {
// 获取Canvas元素的边界信息
const rect = canvas.getBoundingClientRect();
// 计算x坐标:鼠标x坐标减去Canvas左边界偏移
const x = e.clientX - rect.left;
// 计算y坐标:鼠标y坐标减去Canvas上边界偏移
const y = e.clientY - rect.top;
return { x, y };
}
常用事件绑定示例
我们可以给Canvas绑定鼠标按下、移动、抬起事件,实现基础的绘图交互逻辑:
// 标记是否处于按下状态
let isDrawing = false;
// 存储上一次的坐标
let lastPoint = null;
// 鼠标按下事件
canvas.addEventListener('mousedown', (e) => {
isDrawing = true;
lastPoint = getCanvasPoint(e);
});
// 鼠标移动事件
canvas.addEventListener('mousemove', (e) => {
if (!isDrawing) return;
const currentPoint = getCanvasPoint(e);
// 绘制从上次点到当前点的线段
ctx.beginPath();
ctx.moveTo(lastPoint.x, lastPoint.y);
ctx.lineTo(currentPoint.x, currentPoint.y);
ctx.strokeStyle = '#ff0000';
ctx.lineWidth = 2;
ctx.stroke();
// 更新上次坐标
lastPoint = currentPoint;
});
// 鼠标抬起事件
canvas.addEventListener('mouseup', () => {
isDrawing = false;
lastPoint = null;
});
常见交互功能实现
点击绘制固定图形
除了自由绘制线段,还可以实现点击画布任意位置绘制固定图形的功能,比如点击后绘制一个半径20的圆形:
// 点击绘制圆形
canvas.addEventListener('click', (e) => {
const point = getCanvasPoint(e);
ctx.beginPath();
// 绘制圆形,参数分别是圆心x、圆心y、半径、起始角度、结束角度
ctx.arc(point.x, point.y, 20, 0, Math.PI * 2);
ctx.fillStyle = '#00ff00';
ctx.fill();
});
清除画布功能
Canvas提供了clearRect方法可以清除指定区域的绘图内容,清除整个画布只需要传入画布宽高即可:
// 清除整个画布
function clearCanvas() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
}
// 可以绑定到按钮的点击事件上
document.getElementById('clearBtn').addEventListener('click', clearCanvas);
图形拖拽交互
实现图形拖拽需要先记录所有已绘制的图形信息,在鼠标按下时判断点击位置是否在某个图形内,移动时更新图形坐标并重绘画布:
// 存储所有绘制的圆形
let circleList = [];
// 当前拖拽的圆形索引
let dragIndex = -1;
// 拖拽偏移量
let dragOffset = { x: 0, y: 0 };
// 点击判断是否在圆形内
canvas.addEventListener('mousedown', (e) => {
const point = getCanvasPoint(e);
// 遍历所有圆形,判断是否被点击
for (let i = circleList.length - 1; i >= 0; i--) {
const circle = circleList[i];
// 计算点击位置和圆心的距离
const distance = Math.sqrt(
Math.pow(point.x - circle.x, 2) +
Math.pow(point.y - circle.y, 2)
);
// 距离小于半径说明点击到了圆形
if (distance <= circle.radius) {
dragIndex = i;
dragOffset.x = point.x - circle.x;
dragOffset.y = point.y - circle.y;
break;
}
}
});
// 拖拽移动
canvas.addEventListener('mousemove', (e) => {
if (dragIndex === -1) return;
const point = getCanvasPoint(e);
// 更新圆形坐标
circleList[dragIndex].x = point.x - dragOffset.x;
circleList[dragIndex].y = point.y - dragOffset.y;
// 重绘画布
redrawCanvas();
});
// 抬起结束拖拽
canvas.addEventListener('mouseup', () => {
dragIndex = -1;
});
// 重绘画布的方法
function redrawCanvas() {
// 先清除画布
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 重新绘制所有圆形
circleList.forEach(circle => {
ctx.beginPath();
ctx.arc(circle.x, circle.y, circle.radius, 0, Math.PI * 2);
ctx.fillStyle = circle.color;
ctx.fill();
});
}
// 修改之前的点击绘制圆形的逻辑,把圆形存入数组
canvas.addEventListener('click', (e) => {
// 如果正在拖拽则不绘制新图形
if (dragIndex !== -1) return;
const point = getCanvasPoint(e);
const newCircle = {
x: point.x,
y: point.y,
radius: 20,
color: '#00ff00'
};
circleList.push(newCircle);
redrawCanvas();
});
注意事项
- Canvas的width和height属性如果通过CSS设置,会导致画布内容拉伸变形,建议直接在属性中设置宽高。
- 频繁的重绘画布时,可以使用requestAnimationFrame来优化性能,避免页面卡顿。
- 如果画布内容需要缩放,坐标转换时还需要考虑缩放比例,否则交互位置会出现偏差。
JavascriptCanvas绘图交互事件监听修改时间:2026-06-11 02:18:39