用原生 Canvas 实现拖拽与缩放

https://youtu.be/Sj0sZU0eKx8

一个关于无限画布的教程,帮助开发者理解和实现无限画布的概念与功能。
无限画布是一种允许用户以非线性方式自由组织内容的界面,支持缩放、直观编辑基本图形(如移动、分组和修改样式)等功能。”

当然可以!我来给你分成三部分讲解 xiaoiver/infinite-canvas-tutorial 项目的核心内容,让你更清晰地理解它的设计思路:

1. 项目目的:打造一个基本的「无限画布」

  • 目标是让画布可以被拖动(pan)缩放(zoom),并且画面上的元素(比如点、矩形)随着拖拽或缩放正确跟随变换。
  • 重点是操作的是视图,而不是操作元素本身。也就是说,图形坐标是固定的,用户是在「移动摄像机」看画布。

2. 主要功能点讲解

功能 实现思路 细节
拖动画布 监听 mousedown, mousemove, mouseup 事件,计算鼠标移动量,更新平移值(offsetX, offsetY 用变量记录当前平移状态
缩放画布 监听 wheel 滚轮事件,根据滚轮方向放大或缩小,调整 scale 缩放比例 注意缩放时要以鼠标指针为中心调整偏移
绘制图形 每次 draw() 函数调用时,根据当前的 offsetX, offsetY, scale 重新绘制画布上的元素 要在绘制前使用 Canvas 的 ctx.setTransform() 应用变换
保持变换状态 使用 ctx.save()ctx.restore() 方法,确保每次绘制时 Canvas 状态正确 避免变换叠加出错

3. 核心代码逻辑梳理

一个标准的渲染流程大概是这样:

function draw() {
    ctx.save(); // 保存当前变换状态
    ctx.setTransform(scale, 0, 0, scale, offsetX, offsetY); // 应用平移和缩放
    ctx.clearRect(-offsetX/scale, -offsetY/scale, canvas.width/scale, canvas.height/scale); // 清除画布
    // 绘制固定在世界坐标下的图形
    drawAllElements();
    ctx.restore(); // 恢复变换状态
}
  • scale 控制缩放大小。
  • offsetXoffsetY 控制画布的平移。
  • 每次绘制前,先应用缩放和平移的变换,再绘制图形。

交互时,例如:

  • 鼠标拖动 -> 更新 offsetX/offsetY
  • 鼠标滚轮 -> 更新 scale
  • 然后重新调用 draw() 重绘

补充细节

  • 画布初始化时,需要根据设备像素比(DPR)调整 canvas.widthcanvas.height,保证高分屏下画质清晰。
  • 缩放时为了做到以鼠标指针为中心,需要用一些简单的坐标换算公式。

小结

这个项目可以看作是:

  • 极简版白板引擎
  • 交互式 2D 编辑器的基础
  • 理解 Canvas 变换矩阵的实践

它只是基础版,但如果想扩展,比如加上:

  • 元素选择、拖拽、删除
  • 连线、分组
  • 多人协作同步
  • 保存和恢复画布内容

就可以慢慢做成类似 Figma / Miro / Excalidraw 这样的复杂应用了!

GitHub:https://github.com/xiaoiver/infinite-canvas-tutorial

油管:https://youtu.be/Sj0sZU0eKx8

了解 Tarogo Cloud Bloger & Shop 的更多信息

立即订阅以继续阅读并访问完整档案。

继续阅读