在线体验 | 自部署指南
我在 Gumroad 上的免费课程 ,欢迎评分!
什么是无限画布?infinitecanvas 对“无限”的描述如下:
高扩展性。用户可以以非线形的形式自由组织内容结构。
缩放。模拟真实世界中的“放大”纵览全局和“缩小”观察细节。
直接操作。提供对于基础图形的直观编辑能力,包括移动、成组、修改样式等。
实时协作。
你一定见过甚至使用过各种包含无限画布的应用,infinitecanvas 上就展示了从设计工具到创意画板在内的众多案例,其中不乏一些知名产品包括 Figma 、Modyfi 、Motiff 、rnote 、tldraw 、excalidraw 等等。
作为一个前端,我对其中涉及到的渲染技术很感兴趣。尽管 tldraw 、excalidraw 等普遍使用易用性更高的 Canvas2D / SVG 技术,但 JS 和 Rust 生态中也有很多编辑器、设计工具使用更底层的渲染技术对 2D 图形进行 GPU 加速,以获得更好的性能和体验:
Figma 使用 C++ 编写了一个 tile-based 的渲染引擎,编译成 WASM 后调用 WebGL 或 WebGPU 渲染
Motiff 同样使用 WebGL 实现了一个 tile-based 渲染引擎
Modyfi 使用了 Rust 生态中的 wgpu ,同样编译成 WASM 后调用 WebGL2 渲染
Zed 使用 GPUI 渲染矩形、阴影、文本、图片等 UI。
vello 和 xilem 实验性地使用了 Compute Shader 进行 2D 渲染。
因此在这个教程中,我希望实现以下特性:
未来我希望将画布的渲染部分用 Rust 重写,目前项目的完成度还比较低:
让我们开始吧!
课程项目使用了 pnpm workspace ,因此需要先安装 pnpm
运行 ECS 单元测试(pnpm test:ecs)需要 Node 20 与原生模块 canvas / gl,详见 docs/running-ecs-tests.md 。
进入课程目录后,启动 Vite 开发服务器
cd packages/lesson_001
pnpm run dev
或者你也可以本地运行这个教程站点
pnpm run build
cd packages/site
pnpm run dev
如果想在自己的项目中使用,可以参考:
基于 WebGL1/2 和 WebGPU 的硬件抽象层
画布 API 设计
实现一个简单的插件系统
基于硬件抽象层实现一个渲染插件
向画布中添加图形
使用 SDF 绘制一个圆形
反走样
变换。让图形支持平移、缩放、旋转、斜切变换
实现场景图
相机是什么
投影变换
相机变换。通过一个插件实现平移、旋转和缩放功能
相机动画。平滑过渡到任意相机状态
绘制直线网格。使用 Line Geometry 或者屏幕空间技术
绘制点网格
为 Geometry 绘制 wireframe
Grid
Wireframe
参考 DOM API 实现事件系统
如何拾取一个圆形
实现一个拖拽插件
支持双指缩放手势
使用 Lit 和 Shoelace 开发 Web UI
实现画布组件,监听页面宽高变换
实现缩放组件
实现明暗主题
应用 Lit 的国际化方案
什么是 Draw call
使用剔除减少 draw call
使用合批减少 draw call
使用空间索引提升拾取效率
推导椭圆和圆角矩形的 SDF 表示
为 SDF 增加外阴影和内阴影
如何判定任意点是否在椭圆或圆角矩形内
Drop Shadow
Inner Shadow
将画布内容导出成 PNG,JPEG 和 SVG 格式的图片
在画布中渲染图片
拓展 SVG 的能力,以 stroke-alignment 为例
支持 CSS 中的 object-fit 和 object-position 让图片以不同策略适应宽高
基于 Jest 的测试环境搭建,包含本地和 CI 环境
使用单元测试提升代码覆盖率
视觉回归测试
基于 headless-gl 的 WebGL1 服务端渲染方案
基于 Playwright 的 WebGL2 / WebGPU 端到端测试方案
E2E 测试
浏览器兼容性测试
在 WebWorker 中渲染画布
为什么不直接使用 gl.LINES?
在 CPU 或者 Shader 中构建 Mesh
在 Shader 中构建顶点与接头、反走样、绘制虚线
如何计算折线的包围盒?
尝试使用 SDF 绘制
通过三角化绘制填充部分,使用折线绘制描边部分
支持 earcut 和 libtess.js 两种三角化方案
正确处理路径中的孔洞
支持 fillRule 属性
实现一些手绘风格图形
实现水彩效果
Path and rough shapes
Fill rule
Watercolor
实现 zIndex 和 sizeAttenuation
增加手型和选择画布模式。
什么是 TextMetrics,如何在服务端和浏览器端获取
什么是 Shaping?实现 letterSpacing 与 kerning
处理段落
分段与自动换行
支持 BiDi,包括阿拉伯字符的特殊处理
处理复合字符
支持 text-align
如何生成 SDF atlas 并使用它绘制
如何使用 ESDT 和 MSDF 提升文本渲染质量
如何绘制 Bitmap 格式的字体
如何绘制 emoji
Render text with SDF atlas
Bitmap font, emoji, BiDi and clusters
使用贝塞尔曲线渲染文本,使用 OpenType 和 Harfbuzz 进行字形处理
渲染 TeX 公式
文本描边、装饰线、阴影以及沿路径布局
带有晕染效果的文本渲染
使用 Web Font Loader 加载 Web 字体
Shaping TeX with MathJax
Physical text
Text decoration
Text stroke
使用 CanvasGradient 实现渐变
命令式。使用 Device API 创建纹理
声明式。支持 CSS 渐变语法:linear-gradient、radial-gradient、conic-gradient
使用 Shoelace 实现渐变配置面板
使用 Shader 实现 Mesh 渐变
模拟随机
Value Noise 和 Gradient Noise
Voronoi、FBM 和 Domain Warping
在 AWS lambda function 中完成服务端渲染
导出 SVG
使用 CanvasPattern 实现重复图案
Linear, Radial, Conic Gradient
Mesh Gradient
Top nav and zoom toolbar
Layers and properties panels
CRDT 的分类与实现
基于 Loro 和 Yjs 在本地模拟协同编辑
基于 liveblocks 和 Yjs 实现真实的服务端客户端协同编辑
端到端加密的 CRDT
感知特性的实现:多人光标和评论的实现
BroadcastChannel 和 Loro / Yjs
使用 Liveblocks 作为服务端
多人光标
多人评论
实现 Resize,锁定长宽比与中心缩放
实现旋转,移动旋转中心
使用方向键移动图形
为直线、折线和 Path 提供交互式编辑能力
Rotate anchor
Resize anchor
Line transformer
Path transformer
SVG Path 的局限性
VectorNetwork 的拓扑定义
如何使用最小环路算法计算填充区域
分析 d3-tree, d3-flextree 和 @antv/hierarchy 的实现
D3 flextree
Mindmap
如何使用 Spectrum 实现上下文菜单
通过上移下移调整图形次序
写入并读取剪贴板内容,支持粘贴序列化图形、非矢量图片、SVG 和纯文本
从文件系统和页面中拖拽导入图片文件
Context menu
Drag'n'drop image
图形绘制模式:通过拖拽绘制矩形、椭圆、直线和箭头
铅笔模式,支持自由可变线宽
笔刷模式:绘制折线、贴图
橡皮擦工具,尝试支持非原子化效果
用于演示的激光笔工具
Draw rect mode
Draw arrow
Brush
Pencil tool
Pencil tool with freehand
Eraser
Marquee selection
Lasso selection
网格吸附
对象吸附
使用聊天框
使用 Nano banana 进行图片生成与编辑
通过 ONNX 的 WebGPU 运行时使用 SAM 在端侧进行推理
使用 Qwen-Image-Layered 分解图层
使用 UpscalerJS 提升图片分辨率
与聊天框结合
清除背景
在 WebWorker 中使用 ONNX 和 SAM 分割图片
使用 Qwen-Image-Layered 分解图层
使用 UpscalerJS 增强分辨率
创建 HTML 容器,同步相机
粘贴 URL,使用书签和 iframe 展示
粘贴 HTML 内容
导出图片
VS Code snippet
Embeded YouTube
图像后处理,例如噪声、亮度调整、halftone、液态玻璃、CRT、Glitch、Ascii 等效果
配套的可叠加的滤镜效果编辑面板
将时间动画导出成 GIF 和 WebM 格式动画
渲染图的设计思路与实现
使用 FXAA 进行快速抗锯齿
Image processing
Effects panel
分析 excalidraw、tldraw、drawio 和 g6 中的数据结构设计
连线边界算法,包括矩形和椭圆边界
连线的路由规则
Orth Connector
Segment Connector
Loop Connector
支持更多连线样式:圆角、二次和三次贝塞尔曲线
Bindings
Orth Connector
Loop Connector
解析 Mermaid 并绘制
解析 D2 并绘制
解析 draw.io 图表并绘制
实现附着在边上的标签,并提升可见性
Mermaid flowchart
D2 flowchart
Draw.io flowchart
基于 Yoga 实现 Flexbox 布局
AlignItems & JustifyContent
Gap
Flex Basis, Grow, and Shrink
Min/Max Width and Height
Gap
AlignItems & JustifyContent
Layout panel
基于 Stencil buffer 实现 clip 与 mask 两种效果
裁剪图片
非原子化橡皮擦
Crop an image
Brush with eraser
通过 WASM 使用 vello 作为渲染引擎,体验基于 WebGPU compute shader 的瓦片渲染
基于 parley 对文本进行 Shaping、BiDi 等处理
基于 kurbo 对 Polyline、Path 进行更精确的包围盒与拾取计算
Vello
如何设计动画 API
参考 Web Animation API 实现声明式 Keyframe 与控制器
实现路径、笔迹、形变等动画效果
支持 Lottie 等格式
动画与时间线面板
Morph
Dashoffset animation
Timeline panel
课程 37 - 基于 Radiance Cascades 的 GI 🔗
使用 WebGPU Compute Shader 实现 Radiance Cascades
使用解析几何生成距离场
计算 cascade:CPU 调度与 ping-pong、派发射线、raymarch 与合并
生成 mipmap
与 vello 渲染管线结合,合成最终结果
Radiance Cascades
with mermaid flowchart, mindmap, state diagram
实现变量与主题
支持 iconfont 渲染,在运行时引入 lucide、material 等图标集,支持应用 filter
引用与继承已声明的组件
Variables and themes
Components and instances
Iconfont(lucide, material and pixelarticons)
Iconfont with filters
独立的 3D 渲染管线
与 2D 画布的相机同步,支持正交和透视投影
使用 Gizmo 为 3D 图形添加平移、旋转交互
基于 raycast 的拾取
光照
Gizmo
Textured earth