konva 开源项目解读
项目信息
项目名称:Konva
项目描述:Konva 是一个 HTML5 Canvas 2D 图形框架(v10.3.0),采用保留模式图形树架构,提供 Stage → Layer → Group → Shape 的四层抽象。支持 19 种内置形状、20 种滤镜、拖拽、动画和事件系统。
官方文档:
解决的核心痛点:
- Canvas API 的底层复杂性: 原生 Canvas API 是无状态的"立即模式",Konva 提供"保留模式"的树形节点抽象
- 高性能互动图形的工程化需求: 内置图层批量渲染、离屏 Hit Canvas 命中检测、节点变换与嵌套
- 跨平台 2D 图形一致性: 通过抽象渲染后端(canvas-backend / skia-backend),浏览器和 Node.js 共用同一套 API
核心亮点:
- 保留模式图形树: Stage → Layer → Group → Shape 树形结构,类 DOM 操作体验;
- 像素级命中检测: 颜色编码 HitCanvas,GPU 加速,O(1) 查找;
- 图层系统: 每个 Layer 独立 Canvas,支持批量渲染和图层级性能优化;
- 滤镜管道: 20 种内置滤镜,支持 CSS filter 和 JS 滤镜函数双模式;
- 变换系统: 综合位置/旋转/缩放/倾斜/偏移的仿射变换矩阵;
- 动画引擎: RAF 循环 + 补间动画(Tween),支持 yoyo/play/pause/reverse;
- Tree-shaking: 支持 Core(~26KB)和 Full 两种导入方式;
- 跨平台: 浏览器 + Node.js(canvas/skia 双后端);
1. 项目概览
1.1 项目定位与核心价值
一句话定位:Konva 是一个面向前端开发者的高性能 HTML5 Canvas 2D 图形框架,通过树形节点结构提供对 Canvas 元素的高级抽象,使得在 Web 上构建交互式图形、编辑器和图表变得简单高效。
核心痛点解决:
- Canvas API 的底层复杂性 — 原生 Canvas API 是无状态的"立即模式",Konva 提供"保留模式"的树形节点抽象
- 高性能互动图形的工程化需求 — 内置图层批量渲染、离屏 Hit Canvas 命中检测、节点变换与嵌套
- 跨平台 2D 图形一致性 — 通过抽象渲染后端(canvas-backend / skia-backend),浏览器和 Node.js 共用同一套 API
1.2 目标用户与使用场景
- 前端 Web 开发者 — 图形编辑器、画布应用、数据可视化图表
- 图形编辑器/设计工具开发者 — Polotno 等设计编辑器 SDK 基于 Konva 构建
- 游戏开发者 — 2D 精灵、动画和交互的轻量级方案
- Node.js 开发者 — 服务端渲染 Canvas 图像(缩略图生成、PDF/图片合成)
1.3 核心技术亮点
| 特性 | 说明 |
|---|---|
| 保留模式图形树 | Stage → Layer → Group → Shape 树形结构,类 DOM 操作体验 |
| 像素级命中检测 | 颜色编码 HitCanvas,GPU 加速,O(1) 查找 |
| 图层系统 | 每个 Layer 独立 Canvas,支持批量渲染和图层级性能优化 |
| 滤镜管道 | 20 种内置滤镜,支持 CSS filter 和 JS 滤镜函数双模式 |
| 变换系统 | 综合位置/旋转/缩放/倾斜/偏移的仿射变换矩阵 |
| 动画引擎 | RAF 循环 + 补间动画(Tween),支持 yoyo/play/pause/reverse |
| Tree-shaking | 支持 Core(~26KB)和 Full 两种导入方式 |
| 跨平台 | 浏览器 + Node.js(canvas/skia 双后端) |
1.4 技术栈与选型对比
| 决策点 | 选择 | 替代方案 | 权衡 |
|---|---|---|---|
| 渲染范式 | 保留模式(Retained Mode) | 立即模式(Immediate Mode) | 牺牲了一定底层灵活性,换取了声明式 API 和自动重绘管理 |
| 命中检测 | 颜色编码 HitCanvas | 几何碰撞计算 | 像素级精度 + GPU 加速,但需要额外 Canvas 和内存开销 |
| 属性管理 | Factory 元编程 | 手动属性声明 | 极大减少样板代码,但 IDE 智能提示依赖类型导出 |
| 包设计 | Core/Full 分包 | 单一打包 | 支持按需加载和 Tree-shaking,但增加了构建配置复杂度 |
| Node.js 后端 | canvas + skia 双后端 | 单一后端 | 提供兼容性(canvas/Cairo)和性能(skia/Chrome 引擎)两种选择 |
2. 整体架构设计
2.1 架构概述
Konva 采用分层图形树架构(Layered Scene Graph Architecture),核心设计理念是"保留模式"(Retained Mode)——所有图形对象以树形节点组织,每个节点独立维护自身属性状态,渲染引擎遍历树进行绘制。
系统分为四个层次:
- 用户接口层:Konva 全局对象,提供创建 Stage/Shape/Animation/Tween 的入口
- 图形树管理层:Stage -> Layer -> Group -> Shape 的四层节点树
- 渲染引擎层:Canvas双缓冲(SceneCanvas + HitCanvas)、Context 包装、滤镜管线
- 事件与交互层:指针事件系统、拖拽引擎、命中检测
2.2 整体架构图
+=======================================================================+
| Konva 全局对象 (Namespace) |
| [Stage] [Layer] [Shape] [Tween] [Animation] [Filters] [Easings] |
+=====================================+=================================+
| (create/configure)
v
+=====================================+=================================+
| 图形树管理层 (Scene Graph) |
| |
| +-----------------------------+ |
| | Stage | DOM 绑定、事件分发、resize |
| | (root of scene graph) | |
| +-------------+---------------+ |
| | children[] |
| v |
| +-------------+---------------+ |
| | Layer / FastLayer | 批量绘制、离屏缓存、Hit检测 |
| | (rendering surface) | |
| +-------------+---------------+ |
| | children[] |
| v |
| +-------------+---------------+ |
| | Group (container) | 组织结构/变换层级、裁剪 |
| | (spatial grouping) | |
| +-------------+---------------+ |
| | children[] |
| +--------+--------+ |
| v v |
| +---------+ +---------+ |
| | Group | | Shape | 具体渲染单元 |
| | (nested) | | | |
| +---------+ +---------+ |
+=======================================================================+
+=====================================+=================================+
| 渲染引擎层 (Rendering Engine) |
| |
| +-------------------+ +-------------------+ |
| | SceneCanvas | | HitCanvas | |
| | (visible drawing) | | (color-coded hit) | |
| +--------+----------+ +--------+----------+ |
| | | |
| v v |
| +--------+----------+ +--------+----------+ |
| | SceneContext | | HitContext | |
| +-------------------+ +-------------------+ |
| |
| +----------------------------------------------------+ |
| | 滤镜管线 (Filter Pipeline) | |
| | Blur | Brightness | Contrast | HSL | Invert ... | |
| +----------------------------------------------------+ |
+=======================================================================+
+=====================================+=================================+
| 事件与交互层 (Events & Interaction) |
| |
| +-------------------+ +-------------------+ |
| | PointerEvents | | DragAndDrop (DD) | |
| | (hit detection) | | (drag constraints) | |
| +-------------------+ +-------------------+ |
| |
| +-------------------+ +-------------------+ |
| | Animation | | Tween | |
| | (RAF loop) | | (property easing) | |
| +-------------------+ +-------------------+ |
+=======================================================================+2.3 目录结构
konva/
├── src/ # 【核心基建】源代码主目录
│ ├── index.ts # 【核心基建】完整版入口(导入 _FullInternals,export default Konva)
│ ├── Core.ts # 【核心基建】精简版入口(导入 _CoreInternals,不含形状和滤镜)
│ ├── _CoreInternals.ts # 【核心基建】核心模块组装器 — 将核心基建类注入 Konva 全局对象
│ ├── _FullInternals.ts # 【核心基建】完整模块组装器 — 继承 Core 并注入所有 shapes 和 filters
│ ├── Global.ts # 【核心基建】全局 Konva 命名空间初始化 + _registerNode 注册机制
│ ├── Node.ts # 【核心基建】所有图形对象的抽象基类 — 事件系统、变换、缓存、滤镜管道
│ ├── Container.ts # 【核心基建】容器抽象类 — 子节点管理、树遍历、查找、裁剪
│ ├── Stage.ts # 【核心基建】舞台根节点 — DOM 容器绑定、原生事件→Konva事件转换、resize
│ ├── Layer.ts # 【核心基建】图层 — 维护 SceneCanvas+HitCanvas、批量绘制、Hit检测
│ ├── FastLayer.ts # 【核心基建】快速图层 — 继承 Layer,不维护 HitCanvas(无交互图层优化)
│ ├── Group.ts # 【核心基建】分组容器 — 嵌套变换层级、支持 clip
│ ├── Shape.ts # 【核心基建】形状抽象基类 — 填充/描边/阴影样式、sceneFunc/hitFunc 模板
│ ├── Factory.ts # 【核心基建】属性工厂 — 泛型 addGetterSetter/getterSetter/overloadedGetterSetter
│ ├── Validators.ts # 【工具集】属性校验器 — getNumberValidator、getStringValidator、getBooleanValidator 等
│ ├── Util.ts # 【工具集】通用工具函数 — Transform 矩阵类、颜色解析、Canvas 元素管理
│ ├── Canvas.ts # 【核心基建】画布抽象 — SceneCanvas(可见绘制) + HitCanvas(颜色编码命中)
│ ├── Context.ts # 【核心基建】渲染上下文包装 — SceneContext、HitContext 封装 CanvasRenderingContext2D
│ ├── DragAndDrop.ts # 【核心基建】拖拽引擎 — DD 单例、位置追踪、边界约束、事件触发
│ ├── Animation.ts # 【核心基建】动画引擎 — 全局 RAF 循环、animations[] 管理、帧计时
│ ├── Tween.ts # 【核心基建】补间动画 — TweenEngine 属性缓动、yoyo、play/pause/reverse
│ ├── PointerEvents.ts # 【核心基建】指针事件系统 — 相对坐标计算、事件对象构建、按钮/指针类型映射
│ ├── BezierFunctions.ts # 【工具集】贝塞尔曲线计算 — 点插值、长度、导数
│ ├── types.ts # 【核心基建】全局类型定义 — GetSet、IRect、Vector2d、AnimationFn 等
│ ├── canvas-backend.ts # 【配置】Node.js canvas 后端 — 注册 node-canvas 为默认渲染引擎
│ ├── skia-backend.ts # 【配置】Node.js skia-canvas 后端 — 注册 skia 为渲染引擎
│ ├── shapes/ # 【业务模块】19 个具体形状实现(均继承 Shape)
│ │ ├── Arc.ts # 【业务模块】弧形路径
│ │ ├── Arrow.ts # 【业务模块】箭头(基于 Line,添加箭头头部计算)
│ │ ├── Circle.ts # 【业务模块】圆形
│ │ ├── Ellipse.ts # 【业务模块】椭圆
│ │ ├── Image.ts # 【业务模块】图像 — 图片加载、裁剪、DPI 感知
│ │ ├── Label.ts # 【业务模块】标签 — Group + Tag 组合
│ │ ├── Line.ts # 【业务模块】线段/折线/多边形/贝塞尔曲线
│ │ ├── Path.ts # 【业务模块】SVG 路径解析和渲染
│ │ ├── Rect.ts # 【业务模块】矩形
│ │ ├── RegularPolygon.ts # 【业务模块】正多边形(三角形/五边形/六边形等)
│ │ ├── Ring.ts # 【业务模块】环形/同心圆
│ │ ├── Sprite.ts # 【业务模块】精灵动画 — spritesheet 帧播放
│ │ ├── Star.ts # 【业务模块】星形(内/外半径+顶点数)
│ │ ├── Text.ts # 【业务模块】文本渲染 — 多行、对齐、换行、文字装饰
│ │ ├── TextPath.ts # 【业务模块】沿路径排列文本
│ │ ├── Transformer.ts # 【业务模块】变换控件 — 选择框、旋转锚点、缩放手柄、边界保持
│ │ └── Wedge.ts # 【业务模块】楔形/扇形
│ └── filters/ # 【业务模块】20 个图像滤镜(操作 ImageData)
│ ├── Blur.ts # 【业务模块】模糊(Stack Blur 算法)
│ ├── Brighten.ts # 【业务模块】增亮
│ ├── Brightness.ts # 【业务模块】亮度调整
│ ├── Contrast.ts # 【业务模块】对比度调整
│ ├── Emboss.ts # 【业务模块】浮雕效果
│ ├── Enhance.ts # 【业务模块】增强
│ ├── Grayscale.ts # 【业务模块】灰度化
│ ├── HSL.ts # 【业务模块】HSL 色彩空间调整
│ ├── HSV.ts # 【业务模块】HSV 色彩空间调整
│ ├── Invert.ts # 【业务模块】反色
│ ├── Kaleidoscope.ts # 【业务模块】万花筒效果
│ ├── Mask.ts # 【业务模块】蒙版
│ ├── Noise.ts # 【业务模块】噪点
│ ├── Pixelate.ts # 【业务模块】像素化
│ ├── Posterize.ts # 【业务模块】色调分离
│ ├── RGB.ts # 【业务模块】RGB 通道调整
│ ├── RGBA.ts # 【业务模块】RGBA 通道调整
│ ├── Sepia.ts # 【业务模块】复古棕褐色
│ ├── Solarize.ts # 【业务模块】曝光过度
│ └── Threshold.ts # 【业务模块】二值化阈值
├── test/ # 【质量保证】测试文件目录
│ ├── unit/ # 【质量保证】单元测试(~45 个 .ts 文件,覆盖所有模块)
│ ├── manual/ # 【质量保证】手动滤镜测试(~20 个 .ts 文件)
│ ├── performance/ # 【质量保证】性能测试(创建元素、bunnies、jump-shape 等 HTML)
│ ├── typescript/ # 【质量保证】TypeScript 类型推导正确性测试
│ ├── assets/ # 【质量保证】测试资源(darth-vader.jpg、lion.png、bunny.png、SVG 数据)
│ ├── unit-tests.html # 【配置】Parcel 打包的单元测试入口
│ ├── manual-tests.html # 【配置】手动测试入口
│ └── sandbox.html # 【配置】开发沙盒
├── resources/ # 【配置】文档生成资源
│ ├── jsdoc.conf.json # 【配置】JSDoc 配置
│ └── doc-includes/ # 【配置】JSDoc 模板片段(NodeParams、ContainerParams、ShapeParams)
├── .github/ # 【配置】GitHub Actions CI/CD
│ ├── workflows/test-browser.yml # 【配置】浏览器端 CI 测试
│ ├── workflows/test-node.yml # 【配置】Node.js CI 测试
│ ├── workflows/build.yml # 【配置】构建验证
│ ├── workflows/release.yml # 【配置】自动发布流程
│ └── workflows/prettier.yml # 【配置】代码格式检查
├── rollup.config.mjs # 【配置】Rollup 打包配置
├── gulpfile.mjs # 【配置】Gulp 构建脚本(文档生成、构建 pipeline)
├── tsconfig.json # 【配置】TypeScript 编译配置(target: ES2015, module: ESNext)
├── tsconfig.test.json # 【配置】测试用 TypeScript 配置
├── package.json # 【配置】NPM 包配置(v10.3.0, MIT license)
├── release.sh # 【工具集】发布流程脚本
├── CHANGELOG.md # 【配置】版本变更日志
├── LICENSE # 【配置】MIT 许可证
└── README.md # 【配置】英文项目说明文档3. 模块依赖与调用关系
3.1 全局入口与核心路由
逻辑说明:Konva 提供了两个入口:
index.ts(完整版)和Core.ts(精简版)。两者都通过_assign将模块注入全局 Konva 对象,保证全局单例。用户通过new Konva.Stage()创建根节点后,整个应用以树形结构组织。调用拓扑 (plainText):
index.ts (完整版入口)
+--> _FullInternals.ts
+--> _CoreInternals.ts
| +--> Global.ts .............. Konva 全局命名空间初始化
| +--> Util.ts / Transform .... 工具函数和矩阵变换
| +--> Node.ts ................ 基类节点(事件、缓存、变换)
| +--> Container.ts ........... 容器抽象(子节点管理)
| +--> Stage.ts ............... 舞台(DOM绑定、事件分发)
| +--> Layer.ts ............... 图层(批量渲染)
| +--> FastLayer.ts ........... 快速图层(跳过Hit检测)
| +--> Group.ts ............... 分组(空间组织)
| +--> Shape.ts ............... 形状基类(Canvas绘制)
| +--> Canvas.ts .............. 画布抽象(Scene/Hit双缓冲)
| +--> Context.ts ............. 渲染上下文包装
| +--> Factory.ts ............. 属性工厂(getter/setter生成)
| +--> Validators.ts .......... 属性校验
| +--> DragAndDrop.ts ......... 拖拽引擎
| +--> Animation.ts ........... 动画引擎(RAF管理)
| +--> Tween.ts ............... 补间动画引擎
| +--> PointerEvents.ts ....... 指针事件系统
| +--> BezierFunctions.ts ..... 贝塞尔曲线
| +--> types.ts ............... 全局类型
|
+--> shapes/
| +--> Arc.ts
| +--> Arrow.ts
| +--> Circle.ts
| +--> Ellipse.ts
| +--> Image.ts
| +--> Label.ts / Tag
| +--> Line.ts
| +--> Path.ts
| +--> Rect.ts
| +--> RegularPolygon.ts
| +--> Ring.ts
| +--> Sprite.ts
| +--> Star.ts
| +--> Text.ts
| +--> TextPath.ts
| +--> Transformer.ts
| +--> Wedge.ts
|
+--> filters/
+--> Blur.ts, Brighten.ts, Brightness.ts, Contrast.ts
+--> Emboss.ts, Enhance.ts, Grayscale.ts, HSL.ts, HSV.ts
+--> Invert.ts, Kaleidoscope.ts, Mask.ts, Noise.ts
+--> Pixelate.ts, Posterize.ts, RGB.ts, RGBA.ts
+--> Sepia.ts, Solarize.ts, Threshold.ts
Core.ts (精简版入口)
+--> _CoreInternals.ts (仅核心基建,不含 shapes/ 和 filters/)3.2 核心业务实体与关联
- 实体定义:
| 实体 | 描述 |
|---|---|
| Node | 所有图形对象的抽象基类。维护位置、旋转、缩放、透明度等变换属性;提供事件监听系统;支持缓存和滤镜。 |
| Container | 继承自 Node,抽象出"可以有子节点"的语义。维护 children[] 数组,提供树的遍历、查找、增删改能力。 |
| Stage | 场景图的根节点。绑定 HTML DOM 容器,创建 SceneCanvas/HitCanvas,协调所有事件(鼠标/触摸/指针)的分发。 |
| Layer | 渲染表面。拥有独立的 SceneCanvas 和 HitCanvas,执行批量绘制(batchDraw)。每个 Stage 可以包含多个 Layer。 |
| FastLayer | Layer 的子类,不维护 HitCanvas,适合不需要交互的纯展示图层(性能优化)。 |
| Group | 组织容器。提供坐标系变换的嵌套,支持裁剪(clip)。本身不进行绘制,但影响子节点的渲染状态。 |
| Shape | 所有可渲染形状的基类。定义 sceneFunc() 和 hitFunc() 模板方法,子类实现具体绘制逻辑。管理填充/描边/阴影等样式。 |
| Transformer | 特殊的 Shape,提供可视化的锚点控件,用于选中和变换其他节点(缩放/旋转/倾斜)。 |
- 实体引用拓扑 (plainText):
[Konva Global Namespace]
|
+-- 1 -----> 1 [Stage] (根容器)
|
+-- 1 -----> N [Layer/FastLayer] (渲染图层)
|
+-- 1 -----> N [Group] (组织容器)
|
+-- 1 -----> N [Group] (可递归嵌套)
| |
| +-- 1 -----> N [Shape] (渲染单元)
|
+-- 1 -----> N [Shape] (渲染单元)
[Node] <|------- [Container] <|------- [Stage]
| |
| +------- [Layer] <|---- [FastLayer]
|
+------- [Shape] <|------- [Rect, Circle, Arc, Ellipse, ...]
| |------- [Image, Sprite, Text, TextPath]
| |------- [Line, Path, Arrow]
| |------- [Label, Transformer, ...]
|
+------- [Group]
[Shape].sceneFunc() --> [SceneContext] --> CanvasRenderingContext2D
[Shape].hitFunc() --> [HitContext] --> CanvasRenderingContext2D
[Shape].filters[] --> [Filter Pipeline] --> ImageData processing
[Stage] --> [PointerEvents] --> getIntersection() --> [HitCanvas]
[Node] --> [DD (DragAndDrop)] --> 拖拽位置计算
[Node] --> [Animation] --> requestAnimationFrame 循环
[Node] --> [Tween] --> TweenEngine (属性补间)4. 核心模块详解
模块一:Node(所有节点的抽象基类)
模块名称:Node — 图形对象基类
设计说明: Node 是整个框架的基石,继承自 EventEmitter 模式(自定义实现),提供:
- 属性系统:通过 Factory.addGetterSetter 动态生成属性的 getter/setter,setter 中自动触发变更通知和脏标记
- 变换系统:维护 Transform 矩阵(位置 x/y、缩放 scaleX/scaleY、旋转 rotation、偏移 offsetX/offsetY、倾斜 skewX/skewY)
- 缓存系统:支持将节点的渲染结果缓存到离屏 Canvas,提高复杂图形的重绘性能
- 滤镜管道:支持对缓存结果应用多个滤镜(CSS filter 或 JS 滤镜函数)
- 事件系统:完整的事件监听/触发/取消机制,支持命名空间(如
click.konva)
内部结构图 (plainText):
+=======================================================================+
| Node (基类) |
+=======================================================================+
| |
| +---------------------+ +---------------------+ |
| | 属性系统 | | 变换系统 | |
| | (Factory.getter/ | | (Transform Matrix) | |
| | setter 动态生成) | | - x, y | |
| | - attrs: {} | | - scaleX, scaleY | |
| | - onChange() | | - rotation | |
| | - _setAttr() | | - offsetX, offsetY | |
| +----------+-----------+ | - skewX, skewY | |
| | +----------+-----------+ |
| v v |
| +----------+-----------+ +----------+-----------+ |
| | 脏标记系统 | | 缓存系统 | |
| | - _dirty: boolean | | - cache() | |
| | - batchDraw 触发 | | - clearCache() | |
| +----------+-----------+ | - _cacheCanvas | |
| | +----------+-----------+ |
| v v |
| +----------+-----------+ +----------+-----------+ |
| | 事件系统 | | 滤镜管道 | |
| | - on/off/fire() | | - filters[] | |
| | - namespace support | | - _applyFilter() | |
| | - event bubbling | | - CSS filter parse | |
| +---------------------+ +---------------------+ |
| |
+=======================================================================+模块二:Shape(形状渲染基类)
模块名称:Shape — Canvas 2D 绘制核心
设计说明: Shape 是渲染的关键模块。采用模板方法模式:
- 子类实现
_sceneFunc(context)定义场景绘制逻辑 - 子类实现
_hitFunc(context)定义命中检测绘制逻辑 - Shape 基类的
sceneFunc(context)包装了填充/描边/阴影等样式处理 - 颜色编码的命中检测:每个 Shape 实例在 HitCanvas 上用一个唯一颜色绘制,通过
getIntersection()采样像素颜色来定位命中的节点
- 子类实现
内部结构图 (plainText):
+=======================================================================+
| Shape (渲染基类) |
+=======================================================================+
| |
| 绘制管线: |
| |
| draw() |
| | |
| v |
| drawHit(can, top) ---------> HitCanvas (颜色编码) |
| | | |
| | v |
| | hitFunc() |
| | - _hitFunc(context) [子类实现] |
| | - 填充唯一颜色 key |
| | |
| v |
| drawScene(can, top) ------> SceneCanvas (可见绘制) |
| | | |
| | v |
| | sceneFunc() |
| | - 填充样式处理 |
| | - 描边样式处理 |
| | - 阴影处理 |
| | - _sceneFunc(context) [子类实现] |
| | |
| v |
| drawHitFromCache(can, top) (缓存命中时) |
| |
+=======================================================================+
| |
| 命中检测: |
| |
| getIntersection(point) |
| | |
| v |
| HitCanvas.getContext().getImageData(x, y, 1, 1) |
| | |
| v |
| colors['#' + r + g + b] --> shape instances (颜色→形状映射) |
| |
+=======================================================================+模块三:Stage(舞台根节点与事件中枢)
模块名称:Stage — 整个场景图的入口
设计说明: Stage 是唯一直接与 DOM 交互的模块:
- 创建 HTML Canvas 元素并绑定到用户指定的 DOM 容器
- 管理
contentdiv 及其子 Canvas 元素的生命周期 - 监听所有原生 DOM 事件(mouse/touch/pointer/wheel),转换为 Konva 内部事件
- 通过 PointerEvents 模块进行坐标转换和命中检测
- 维护全局
stages[]数组(用于全局事件分发和资源管理)
内部结构图 (plainText):
+=======================================================================+
| Stage (根容器) |
+=======================================================================+
| |
| DOM 绑定层: |
| +--------------------------------------------------+ |
| | container (HTMLDivElement) | |
| | +-- content (HTMLDivElement) | |
| | +-- <canvas> SceneCanvas | |
| | +-- <canvas> HitCanvas (hidden) | |
| +--------------------------------------------------+ |
| |
| 事件管道: |
| +--------------------------------------------------+ |
| | DOM Events (mouse/touch/pointer/wheel) | |
| | | | |
| | v | |
| | _pointerdown / _pointermove / _pointerup... | |
| | | | |
| | v | |
| | PointerEvents.createEvent() | |
| | | | |
| | v | |
| | getIntersection() --> 从 HitCanvas 获取命中节点 | |
| | | | |
| | v | |
| | targetNode.fire('eventName', evt) | |
| | | | |
| | v | |
| | Event Bubbling (向上冒泡至 Layer/Stage) | |
| +--------------------------------------------------+ |
| |
+=======================================================================+模块四:Factory(属性系统工厂)
- 模块名称:Factory — getter/setter 生成引擎
- 设计说明: Factory 使用了元编程技术,通过
addGetterSetter()方法动态为类添加属性的 getter/setter:- 在类的原型链上定义属性访问器
- setter 中自动触发:值变更检测 -> 校验器执行 -> 属性存储 -> 变更回调
- 支持
components(复合属性自动拆分,如position自动分解为x,y) addGetterSetter的after回调支持在 setter 后执行额外逻辑(如触发重绘)
+=======================================================================+
| Factory (属性工厂) |
+=======================================================================+
| |
| addGetterSetter(NodeClass, 'x', 0, getNumberValidator(), afterFn) |
| | |
| v |
| Object.defineProperty(NodeClass.prototype, 'x', { |
| get: function() { return this._attrs.x; }, |
| set: function(val) { |
| val = validator(val, 'x'); // 校验 |
| if (val === this._attrs.x) return; // 去重 |
| this._attrs.x = val; // 存储 |
| this._dirty = true; // 标记脏 |
| if (afterFn) afterFn.call(this); // 后置处理 |
| } |
| }); |
| |
| overloadedGetterSetter(NodeClass, 'position', ['x', 'y']) |
| --> 自动生成 position({x, y}) 形式的复合 setter |
| |
+=======================================================================+模块五:Animation + Tween(动画引擎)
- 模块名称:Animation & Tween — 帧循环与补间动画
- 设计说明: Animation 管理 requestAnimationFrame 循环,维护全局动画列表。Tween 基于 Animation 实现属性的缓动过渡:
- Animation:构造函数接收 func 和 layers,start() 后将自身注册到全局动画列表,通过 RAF 循环每帧调用 func
- Tween:为每个被补间的属性创建独立的 TweenEngine 实例,支持 yoyo(往复)、play/pause/reverse/reset 控制
- TweenEngine 使用 easing 函数计算每帧的进度位置
- colorAttrs(fill, stroke, shadowColor)支持颜色空间的平滑过渡
+=======================================================================+
| 动画引擎 (Animation + Tween) |
+=======================================================================+
| |
| Animation (RAF 管理) |
| +--------------------------------------------------+ |
| | Animation.animations[] (全局动画列表) | |
| | Animation._animationLoop() | |
| | |--> requestAnimationFrame(_animationLoop) | |
| | |--> 遍历 animations[] | |
| | |--> 对每个 anim: | |
| | | anim.func(anim.frame) | |
| | | if layers: layers.forEach(l => l.draw()) | |
| +--------------------------------------------------+ |
| |
| Tween (补间管理器) |
| +--------------------------------------------------+ |
| | Node.to(config) | |
| | |--> 解析 config (duration, easing, onFinish) | |
| | |--> 为每个属性创建 TweenEngine | |
| | | TweenEngine(prop, propFunc, easing) | |
| | | |--> setTime() / setPosition() | |
| | | |--> play/pause/reverse/reset | |
| | | | |
| | |--> 创建 Animation(func, layers) | |
| | |--> Animation.start() | |
| +--------------------------------------------------+ |
| |
+=======================================================================+5. 关键数据流程
场景一:用户点击一个 Shape 的完整事件流程
场景说明:用户鼠标点击一个矩形 Shape,从 DOM 事件触发到 Konva 事件回调执行的完整链路。
流转时序图:
场景二:形状属性变更到屏幕渲染的完整流程
场景说明:用户调用
rect.x(200)修改属性,到最终屏幕更新(包括脏标记、自动绘制)的完整链路。流转时序图 (Mermaid):
场景三:拖拽操作的完整事件链
场景说明:用户拖拽一个形状从起点移动到终点。
流转时序图 (Mermaid):
6. 接口与契约规范
6.1 核心内部模块契约 (TypeScript)
// ============================================================
// Node.ts — 所有图形对象的基类契约
// ============================================================
export interface NodeConfig {
x?: number;
y?: number;
width?: number;
height?: number;
visible?: boolean;
listening?: boolean;
id?: string;
name?: string;
opacity?: number;
scale?: Vector2d;
scaleX?: number;
scaleY?: number;
rotation?: number;
rotationDeg?: number;
offset?: Vector2d;
offsetX?: number;
offsetY?: number;
draggable?: boolean;
dragDistance?: number;
dragBoundFunc?: (pos: Vector2d) => Vector2d;
preventDefault?: boolean;
}
export type KonvaEventListener<This, EventType> = (
this: This,
evt: KonvaEventObject<EventType>
) => void;
export interface KonvaEventObject<EventType> {
type: string;
target: Shape;
evt: EventType;
pointerId?: number;
currentTarget: Node;
cancelBubble: boolean;
}
// ============================================================
// Container.ts — 容器契约
// ============================================================
export interface ContainerConfig extends NodeConfig {
clearBeforeDraw?: boolean;
clipFunc?: (ctx: SceneContext) => ClipFuncOutput;
clipX?: number;
clipY?: number;
clipWidth?: number;
clipHeight?: number;
}
export abstract class Container<
ChildType extends Node = Node,
Config extends ContainerConfig = ContainerConfig
> extends Node<Config> {
children: Array<ChildType>;
getChildren(filterFunc?: (item: Node) => boolean): ChildType[];
hasChildren(): boolean;
removeChildren(): void;
destroyChildren(): void;
add(...children: ChildType[]): this;
find(selector: string | ((node: Node) => boolean)): Node[];
findOne(selector: string | ((node: Node) => boolean)): Node | undefined;
getClientRect(config?: { skipTransform?: boolean; skipShadow?: boolean; skipStroke?: boolean }): IRect;
draw(): this;
batchDraw(): this;
}
// ============================================================
// Stage.ts — 舞台契约
// ============================================================
export interface StageConfig extends ContainerConfig {
container?: HTMLDivElement | string;
}
export class Stage extends Container<Layer> {
container(): HTMLDivElement;
content: HTMLDivElement;
getPointerPosition(): Vector2d | null;
getIntersection(pos: Vector2d, children?: Node[]): Shape | undefined;
getAllIntersections(pos: Vector2d): Shape[];
setPointersPositions(evt: Event): void;
container(): HTMLDivElement;
draw(): this;
toDataURL(config?: object): string;
toBlob(config?: object): Promise<Blob>;
toCanvas(config?: object): HTMLCanvasElement;
toImage(config?: object): HTMLImageElement;
setSize(config: { width: number; height: number }): void;
getSize(): { width: number; height: number };
}
// ============================================================
// Layer.ts — 图层契约
// ============================================================
export interface LayerConfig extends ContainerConfig {
clearBeforeDraw?: boolean;
hitGraphEnabled?: boolean;
imageSmoothingEnabled?: boolean;
}
export class Layer extends Container<Group | Shape> {
canvas: SceneCanvas;
hitCanvas: HitCanvas;
batchDraw(): this;
getIntersection(pos: Vector2d): Shape | undefined;
enableHitGraph(): this;
disableHitGraph(): this;
drawScene(can?: SceneCanvas): this;
drawHit(can?: HitCanvas): this;
}
// ============================================================
// Shape.ts — 形状基类契约
// ============================================================
export type LineJoin = 'round' | 'bevel' | 'miter';
export type LineCap = 'butt' | 'round' | 'square';
export interface ShapeConfig extends NodeConfig {
fill?: string | CanvasGradient;
fillEnabled?: boolean;
fillPriority?: string;
fillRule?: CanvasFillRule;
stroke?: string | CanvasGradient;
strokeWidth?: number;
strokeScaleEnabled?: boolean;
strokeHitEnabled?: boolean;
strokeEnabled?: boolean;
lineJoin?: LineJoin;
lineCap?: LineCap;
miterLimit?: number;
dash?: number[];
dashEnabled?: boolean;
dashOffset?: number;
shadowColor?: string;
shadowBlur?: number;
shadowOffset?: Vector2d;
shadowOffsetX?: number;
shadowOffsetY?: number;
shadowEnabled?: boolean;
shadowOpacity?: number;
shadowForStrokeEnabled?: boolean;
perfectDrawEnabled?: boolean;
hitFunc?: ShapeConfigHandler<Shape>;
}
export class Shape<
Config extends ShapeConfig = ShapeConfig
> extends Node<Config> {
sceneFunc(context: SceneContext): void;
draw(): this;
drawHit(can?: HitCanvas, top?: Node): void;
drawScene(can?: SceneCanvas, top?: Node): void;
getSelfRect(): IRect;
getClientRect(config?: { skipTransform?: boolean }): IRect;
hasFill(): boolean;
hasStroke(): boolean;
hasShadow(): boolean;
fillStrokeScene(context: SceneContext): void;
getHitFunc(): ShapeConfigHandler<Shape>;
static getShapeKey(): string;
}
// ============================================================
// Animation.ts — 动画引擎契约
// ============================================================
export interface IFrame {
time: number;
timeDiff: number;
lastTime: number;
frameRate: number;
}
export type AnimationFn = (frame?: IFrame) => boolean | undefined | void;
export class Animation {
func: AnimationFn;
id: number;
layers: Layer[];
frame: IFrame;
constructor(func: AnimationFn, layers?: Layer | Layer[]);
start(): this;
stop(): this;
setLayers(layers?: Layer | Layer[]): void;
isRunning(): boolean;
}
// ============================================================
// Tween.ts — 补间动画契约
// ============================================================
export interface TweenConfig extends NodeConfig {
duration: number;
easing?: (t: number, b: number, c: number, d: number) => number;
onFinish?: () => void;
yoyo?: boolean;
node?: Node; // 额外属性注入
}
export class Tween {
constructor(config: TweenConfig);
play(): this;
pause(): this;
reverse(): this;
reset(): this;
finish(): this;
destroy(): void;
seek(t: number): this;
}6.2对外 API 契约(公共 API 导出)
// Konva 全局命名空间导出契约
declare namespace Konva {
// 核心类
export class Stage extends Container<Layer> { }
export class Layer extends Container<Group | Shape> { }
export class FastLayer extends Layer { }
export class Group extends Container<Shape> { }
export class Shape<Config> extends Node<Config> { }
// 形状
export class Rect extends Shape<RectConfig> { }
export class Circle extends Shape<CircleConfig> { }
export class Ellipse extends Shape<EllipseConfig> { }
export class Line extends Shape<LineConfig> { }
export class Path extends Shape<PathConfig> { }
export class Text extends Shape<TextConfig> { }
export class Image extends Shape<ImageConfig> { }
export class Arrow extends Shape<ArrowConfig> { }
export class Arc extends Shape<ArcConfig> { }
export class Ring extends Shape<RingConfig> { }
export class Star extends Shape<StarConfig> { }
export class Wedge extends Shape<WedgeConfig> { }
export class RegularPolygon extends Shape<RegularPolygonConfig> { }
export class Label extends Group { }
export class Tag extends Shape<TagConfig> { }
export class Sprite extends Shape<SpriteConfig> { }
export class TextPath extends Shape<TextPathConfig> { }
export class Transformer extends Group { }
// 滤镜
export namespace Filters {
export function Blur(imageData: ImageData): void;
export function Brighten(imageData: ImageData): void;
// ... 其余滤镜
}
// 工具
export namespace Util {
export function haveIntersection(r1: IRect, r2: IRect): boolean;
export function getRandomColor(): string;
// ...
}
// 动画
export class Animation { }
export class Tween { }
export const Easings: Record<string, EasingFunction>;
// 类型
export type Vector2d = { x: number; y: number };
export type IRect = { x: number; y: number; width: number; height: number };
}
export default Konva;7. 快速开始
7.1 安装与运行
# NPM 安装
npm install konva
# 开发模式
git clone https://github.com/konvajs/konva.git
cd konva
npm install
npm start # 启动开发服务器 + 测试 watcher7.2 最简示例
<script src="https://unpkg.com/konva@10/konva.min.js"></script>
<div id="container"></div>
<script>
const stage = new Konva.Stage({
container: 'container',
width: window.innerWidth,
height: window.innerHeight,
});
const layer = new Konva.Layer();
stage.add(layer);
const rect = new Konva.Rect({
x: 50, y: 50, width: 100, height: 50,
fill: '#00D2FF', stroke: 'black', strokeWidth: 4,
draggable: true,
});
layer.add(rect);
layer.draw();
</script>7.3 构建命令
| 命令 | 说明 |
|---|---|
npm run build | 完整构建(编译+打包+压缩) |
npm test | 运行所有测试 |
npm start | 开发模式(parcel serve + watch) |
npm run fmt | 代码格式化(prettier) |