为什么选择 Rendx
一个类比
如果你用过 Ant Design 再用 shadcn/ui,你会理解 Rendx 在做什么。
Ant Design 给你一个完整的组件库:设计规范、主题系统、国际化、无障碍,开箱即用。代价是——你得接受它的一切决定。想改 DatePicker 的交互行为?在几千行源码里找到干预点。想换掉 Modal 的动画?先理解它的 CSSMotion 系统。组件越"完整",你偏离默认行为的成本越高。
shadcn/ui 换了个思路:把组件源码直接复制到你的项目里,你拥有它、修改它、删掉你不需要的部分。它不是一个"库",而是一组可以自由组合的模板。底层用的是 Radix Primitives 这些无样式原语——你在 shadcn/ui 里写的代码和不用 shadcn/ui 写的代码,调用的是同一层 API。
在 2D 可视化领域,AntV(G2/G6/X6)走的是 Ant Design 的路。Rendx 走的是 shadcn/ui 的路。
框架兜售选择,基建赋予自由
D3 在统计图表领域存活了 15 年。在这期间,ECharts、Highcharts、Chart.js、Recharts 轮番更迭,每一代都声称自己"更好用"。但它们底层都在复用 D3 的原语——d3-scale 做映射、d3-shape 造路径、d3-interpolate 做插值。
框架来了又走,基建永远在。
D3 难用,但它活得最久。不是因为 API 好,是因为它占据了正确的抽象层级:不替用户做决定,只提供做决定的工具。
G2 用声明式 spec 描述图表——几行配置就能出一张柱状图,demo 阶段很爽。但当你想做一个非标准 tooltip、在饼图上叠自定义标注、或者让两个图表共享坐标轴联动时,你得钻进 G2 内部几十个中间层去找干预点。"声明式"意味着框架替你做了 100 个决定,你想改第 73 个时,前 72 个都成了障碍。
X6 替你做好了端口模型、边路由、对齐线、快捷键、剪贴板。开箱即用的代价是——当你的业务需求偏离 X6 的预设 5% 时,你要花 50% 的精力去跟框架搏斗。
Rendx 不兜售仓库,不替你做决定。它给你选择权。
设计哲学
1. 同一套 API,从底层到业务
Rendx 的核心只有 5 个概念:App、Scene、Layer、Group、Node。
插件不会发明新概念。graph-plugin 内部用 Node.create 和 Group.add。selection-plugin 用 Layer 画选框。drag-plugin 用 translate 移动节点。你在插件内部写的代码和不用插件写的代码,调用的是同一层 API——就像 shadcn/ui 里的组件代码和你自己手写的 React 代码没有区别。
这意味着:
- 不存在"引擎能做但插件不让做"的能力壁垒
- 理解了引擎 API 就理解了所有插件的实现
- 你可以复制一个插件的源码,改成你要的样子,成本很低
2. 原语 > 框架
D3 在统计可视化领域证明了:基建比框架更持久。Rendx 在渲染领域做同样的事。
12 个包,每个职责单一,独立可用:
rendx-path 生成 SVG 路径字符串,零依赖
rendx-shape 几何形状生成器,不绑定渲染器
rendx-curve 12 种曲线插值,不绑定场景图
rendx-ease 20+ 种缓动函数,纯数学
rendx-interpolate 数值/颜色/向量/矩阵插值
rendx-bounding 包围盒计算
rendx-gradient 渐变解析你可以只用 rendx-path 生成路径字符串给 SVG 用,不引入任何渲染逻辑。也可以只用 rendx-shape + rendx-canvas 做轻量绑定,跳过场景图。每个包都能脱离 Rendx 体系独立存活——这才是基建。
3. 透明 > 方便
// Rendx — 你知道发生了什么
const rect = Node.create('rect', {fill: '#f00'});
rect.shape.from(0, 0, 100, 50);
layer.add(rect);
app.render();
// AntV/G — 你不知道发生了什么
const rect = new Rect({style: {x: 0, y: 0, width: 100, height: 50, fill: '#f00'}});
// 背后:属性解析 → CSS 继承计算 → 脏标记 → 动画检测 → 布局计算 → ...D3 难用但透明。jQuery 好用但不透明。jQuery 死了,D3 还在。
"方便"是一个危险的设计目标。你花 5 分钟用配置搞定了 demo,然后花 5 天去对抗配置当你的需求偏离框架预设。Rendx 选择让前 5 分钟稍微慢一点,换取后 5 天不存在。
4. 尊重 Canvas 的本质
Canvas 是即时模式渲染接口。调一次 fillRect() 就画一个矩形,没有 DOM 节点、没有样式继承。
一些引擎试图在 Canvas 上重建 DOM——CSS 属性计算、querySelector 查询、样式继承链。这是用 JavaScript 重写浏览器内核中 C++ 优化了 20 年的能力——不可能更快,只会更重。
Rendx 保留 Canvas 的即时模式本质。场景图只管渲染顺序和变换传播,事件系统只管交互,不模拟 DOM。
5. 插件是模板,不是黑箱
Rendx 的插件系统和 shadcn/ui 的组件一样:插件是被约束的代码组织方式,不是被封装的能力壁垒。
插件存在的目的是降低使用成本——给你一个"基础可用的模板",省去从头搭建的时间。但你随时可以:
- 看源码理解它做了什么(因为用的是同一套 API)
- 复制出来按需修改(因为没有私有魔法)
- 只用其中两三个,把其余的扔掉(因为零耦合)
- 自己写一个替代品(因为不存在"插件专用 API")
这和 X6 的插件截然不同。X6 的 Port 系统、边路由、Stencil 面板是框架的一部分,你不能绕过它们,也很难替换它们。
6. 一个引擎,覆盖多个领域
AntV 生态里,X6 做图编辑、G6 做图分析、G2 做图表、S2 做表格。四个框架、四套概念体系、四种插件协议、四种主题系统。你在项目里同时需要图编辑和图表?引两个框架,学两套 API。
Rendx 用一个引擎覆盖所有 Canvas 2D 可视化场景:
rendx-engine (统一渲染层)
├── graph-plugin → 图编辑
├── selection-plugin → 选中交互
├── drag-plugin → 拖拽
├── connect-plugin → 连线
├── grid-plugin → 网格
├── history-plugin → 撤销重做
├── minimap-plugin → 小地图
└── (未来) chart-plugin / table-plugin / ...所有插件共享同一个 App、同一个 Scene、同一套事件系统。图编辑器节点内嵌一个迷你图表,只需要在同一个场景图里多加几个 Node——不需要引入第二个框架、不需要跨框架通信、不需要协调两套渲染循环。
7. 性能来自正确的架构
- 多 Canvas 分层 — 每个 Layer 独立 Canvas,互不干扰。overlay 更新不触发数据层重绘
- 三级脏标记 —
dirty(结构变化)→needUpdate(局部矩阵)→worldMatrixNeedUpdate(传播标记),精确控制更新粒度 - 视口裁剪 — 画布外的节点不进入渲染管线
- 惰性 EventEmitter — 不监听事件的节点不创建 emitter
这些不是"黑魔法优化",是正确的架构选择——不做不需要做的事。
8. 知道自己不做什么
Canvas 2D 适合几千个节点。超过万级,正确做法是换 WebGL 引擎,而不是在 Canvas 2D 上硬优化。Rendx 不提供 WebGL 后端,不做滤镜,不做富文本——这些在图表/图编辑场景中使用率极低,但实现成本极高。
不跨界,才能在自己的区间做到极致的效率比。
9. 面向 AI 时代的可预测性
当 AI 辅助编程成为常态,代码库的可预测性比功能丰富度更重要:
- 5 个核心概念,无 DI 容器、无 CSS 继承树、无虚拟 DOM diff
Node.create('rect', { fill: '#f00' })— 没有隐式行为,输入/输出确定- TypeScript strict 模式,所有 API 类型完备
- 插件间
app.getPlugin()运行时软感知,无 import 硬依赖
AI 能通过类型签名推断出正确调用方式的 API,生产力远高于需要阅读大量文档才能理解隐式约定的 API。
横向对比
架构对比
| 维度 | Rendx | AntV (G/G2/G6/X6) |
|---|---|---|
| 渲染节点模型 | 轻量 Graphics(无 CSS) | 模拟 DOM(CSS 属性计算 + 样式继承) |
| 插件与引擎关系 | 同一套 API,源码即模板 | 框架内部 API,替换成本高 |
| 跨领域 | 一个引擎覆盖图编辑/图表/表格 | X6/G6/G2/S2 四个独立框架 |
| 定制成本 | 低(复制插件源码直接改) | 高(需理解框架内部抽象层) |
| 包组合方式 | 12 个独立原语包,按需引用 | 捆绑在 @antv/g 体系内 |
| 概念密度 | 5 个核心概念 | 20+ 概念(Shape/Display/Style/Plugin/...) |
代码量 vs 能力覆盖
| 引擎 | 源码行数 | 核心能力 |
|---|---|---|
| Rendx | ~7,800 行 | 场景图 + 双渲染后端 + 动画 + 事件 + 序列化 + 插件 |
| Konva | ~30,000 行 | 场景图 + Canvas + 动画 + 事件 |
| ZRender | ~40,000 行 | 场景图 + Canvas/SVG + 动画 + 事件 |
| AntV/G | ~50,000+ 行 | 场景图 + Canvas/SVG/WebGL + CSS 兼容 + 动画 |
| Fabric.js | ~60,000 行 | Canvas + 对象编辑 + 序列化 + SVG 导出 |
能力矩阵
| 能力 | Rendx | Konva | AntV/G | PixiJS |
|---|---|---|---|---|
| Canvas 2D | ✅ | ✅ | ✅ | ✅ |
| SVG | ✅ | ❌ | ✅ | ❌ |
| 多 Canvas 分层 | ✅ | ✅ | ❌ | ❌ |
| 三阶段事件流 | ✅ | ❌ | ✅ | ✅ |
| 视口裁剪 | ✅ | ❌ | ✅ | ✅ |
| 序列化 | ✅ | ✅ | ❌ | ❌ |
| 包级别 Tree-shake | ✅ | ❌ | ✅ | ✅ |
动画系统
| Transform | 用途 | 同类引擎 |
|---|---|---|
GraphicsTransform | translate / rotate / scale | 各引擎均有 |
AttributeTransform | opacity / fill / stroke 插值 | Konva Tween 等 |
ClipBoxTransform | 裁剪框揭露动效 (lr/rl/tb/bt) | Rendx 独有 |
ArcTransform | 弧线角度动画 | 需手动实现 |
SectorTransform | 扇形角度 + 半径动画 | 需手动实现 |
总结
Rendx 不是一个更好的 X6,也不是一个更好的 G2。
它是一组可自由组合的 2D 可视化渲染原语——底层提供干净的基建(path、shape、curve、ease、interpolate),中层提供完整的渲染引擎(场景图、事件、动画、分层渲染),上层提供可拆可改的插件模板(图编辑、选中、拖拽、连线、网格、撤销)。
每一层都用同一套 API。插件不是黑箱,是模板。你可以全用、部分用、改着用、或者只用底层原语自己搭。
框架让你快速开始,然后在你偏离预设时阻止你。基建让你自己开始,然后在你走多远时都支撑你。
Rendx 选择做基建。