- 图形学
- 前端
【免费下载链接】two.js
A renderer agnostic two-dimensional drawing api for the web
导读
Two.js 是一套面向现代浏览器的二维绘图 API,其核心设计目标是renderer agnostic(渲染器无关)——开发者只需编写一套场景图(scenegraph)代码,即可让同一套 API 分别在 WebGL、Canvas 2D 与 SVG 三种上下文中渲染。本文以仓库根目录 README.md 为主线,结合 src/two.js 等源码实现,系统讲解 Two.js 的安装方式、基础场景搭建、动画循环、定制构建、ES6 模块导入、无头(服务端)渲染以及文档站构建方法。读完本文,你将能够:用几行代码在页面中渲染一个旋转的矩形;按需裁剪源码构建体积更小的 Two.js 发行包;在 React 等现代框架中以 ES6 import 方式接入;并在 Node.js 服务端借助 node-canvas 将场景渲染为 PNG 图片。
Two.js 是什么
Two.js(当前仓库版本为v0.8.24,见 package.json)是一个面向二维绘图的 JavaScript 库。它通过同一套 API 抽象出三种渲染后端,由 src/constants.js 中的Two.Types常量统一登记:
Types: { webgl: 'WebGLRenderer', svg: 'SVGRenderer', canvas: 'CanvasRenderer', }构造实例时只需传入type参数选择渲染器,其余 API(如makeRectangle、makePath、bind('update'))完全一致,不依赖具体的渲染实现。三个渲染器分别位于 src/renderers/webgl.js、src/renderers/canvas.js 与 src/renderers/svg.js。
在 src/two.js 中,Two类是所有核心类(Path、Group、Vector、Text)、效果类(LinearGradient、RadialGradient、Sprite、ImageSequence)以及形状类(Rectangle、Circle、Polygon、Star等)的公共命名空间,同时Two.Utils聚合了read(SVG 解析)、xhr、getRatio、曲线与数学工具函数,详见 src/two.js#L59-L70。
快速开始:引入与第一个场景
通过<script>标签引入
下载构建好的压缩版two.min.js并放入 HTML 中:
<script src="js/two.min.js"></script>通过 npm 安装
npm install --save two.js安装后可以用require('two.js')(CommonJS)或 ES6 import 方式使用。此外,也可按下一节的方法自行构建所需版本。
绘制一个旋转的矩形
README 提供了一个完整的“旋转矩形”示例,也是理解 Two.js 工作方式的最小骨架:
<!doctype html> <html> <head> <meta charset="utf-8"> <script src="js/two.min.js"></script> </head> <body> <script> var two = new Two({ fullscreen: true, autostart: true }).appendTo(document.body); var rect = two.makeRectangle(two.width / 2, two.height / 2, 50 ,50); two.bind('update', function() { rect.rotation += 0.001; }); </script> </body> </html>该示例演示了三个核心概念:
- 构造实例并挂载:
new Two({ fullscreen, autostart })创建场景,appendTo(document.body)将渲染器的 DOM 元素(<svg>、<canvas>或 WebGL<canvas>)挂到页面上; - 创建形状:
makeRectangle(x, y, width, height)创建矩形并自动加入根场景(scene),从源码看该方法的实现是先new Rectangle(...)再this.scene.add(rect),见 src/two.js#L754-L759; - 动画循环:
autostart: true让实例自动进入requestAnimationFrame循环,随后每帧触发update事件,在事件回调中累加rect.rotation即可驱动旋转。
Two构造参数及其默认值定义在 src/two.js#L184-L191:
| 参数 | 默认值 | 说明 |
|---|---|---|
fullscreen | false | 为true时画布自动适配document.body尺寸,并覆盖width/height |
fitted | false | 为true时画布自动适配父元素尺寸,同样覆盖width/height |
width | 640 | 画布初始宽度 |
height | 480 | 画布初始高度 |
type | Two.Types.svg | 渲染器类型,取webgl/canvas/svg之一 |
autostart | false | 为true时自动调用play()进入动画循环 |
domElement | 无 | 传入已有的 canvas/svg 元素直接绘制,会覆盖type推断 |
从源码看,fullscreen模式会同时修改document.body与渲染器 DOM 的样式(去掉 margin、固定定位)并绑定resize事件,见 src/two.js#L226-L249;fitted模式则监听父元素的尺寸变化,见 src/two.js#L1238-L1250。
动画循环与事件系统
autostart: true等价于手动调用two.play()。Two.js 内部维护一个全局requestAnimationFrame循环(src/two.js#L1260-L1276),每帧遍历Two.Instances中所有处于playing状态的实例并调用其update();而update()会依次触发update事件、同步画布尺寸并调用render(),最终触发render事件,见 src/two.js#L570-L608。
事件系统由Two.Events提供(src/events.js),bind/on/addEventListener注册监听,unbind/off/removeEventListener移除监听,trigger/dispatchEvent触发事件。内置事件类型包括play、pause、update、render、resize、change、remove、insert、order、load,见 src/events.js#L174-L185。
Custom Build:按需定制自己的发行包
Two.js 使用 Node.js 完成源码构建。README 中的定制构建流程如下:
cd ~/path-to-repo/two.js npm installnpm install会安装构建所需的一系列开发依赖(esbuild、eslint、typescript、vuepress 等,详见 package.json 的devDependencies)。
实际构建入口是 utils/build.js。它使用esbuild将 src/two.js 打包成三种产物:
| 产物路径 | 格式 | 用途 |
|---|---|---|
build/two.js | IIFE(globalName: 'Two') | 浏览器全局变量、CommonJS 兼容 |
build/two.module.js | ESM(format: 'esm',target: 'es6') | ES6 import |
build/two.min.js | 压缩 IIFE | 生产环境最小化引入 |
构建时还会将版本号与发布日期注入Two.Version/Two.PublishDate常量,并把体积统计写入 utils/file-sizes.json。直接运行:
node ./utils/build得到的就是更新后的build/two.js与build/two.min.js。README 提示的“自定义裁剪”思路是:如果只使用SVGRenderer,可以修改构建脚本、只保留所需的渲染器文件,从而显著缩小包体。需要注意,构建产物目录build/由构建流程生成,仓库当前只托管源码与 src 目录下的 ESM 模块。
在 ES6 环境中使用(v0.7.5+)
从v0.7.5+开始,Two.js 原生支持 ES6 import,可配合 React、Angular 等框架以及 webpack、esbuild、gulp 等打包工具使用。README 给出了一个 React + TypeScript 风格的最小示例:
import React, { useEffect, useRef } from "react"; import Two from "two.js"; export default function App() { var domElement = useRef(); useEffect(setup, []); function setup() { var two = new Two({ fullscreen: true, autostart: true }).appendTo(domElement.current); var rect = two.makeRectangle(two.width / 2, two.height / 2, 50, 50); two.bind("update", update); return unmount; function unmount() { two.unbind("update"); two.pause(); domElement.current.removeChild(two.renderer.domElement); } function update() { rect.rotation += 0.001; } } return <div ref={domElement} />; }注意 React 组件卸载时依次做了三件清理工作:解绑update事件、two.pause()停止动画循环、移除渲染器 DOM 元素,避免内存泄漏。
按需导入特定模块
发布的 npm 包中包含了完整的模块源码,因此可以只导入自己需要的子模块并自行打包压缩:
import { Vector } from 'two.js/src/vector.js'; // 在 TypeScript 环境下省略 ".js" 后缀: import { Vector } from 'two.js/src/vector';例如本仓库的 src/vector.js、src/path.js、src/group.js 都支持这种直接导入方式。需要说明的是,README 明确提示:主入口import Two from 'two.js'会导入全部模块,因此目前 Two.js 尚未实现完善的 tree shaking(该功能在路线图上)。如果追求极致包体,优先考虑按子模块导入或定制构建。
在无头环境(服务端)中渲染
从v0.7.x开始,Two.js 可以在无浏览器环境中运行,即在 Node.js 服务端借助Node Canvas(canvas包)渲染场景并输出图片。Two.js 没有把canvas放进自身依赖,因为它对浏览器运行并非必需,但源码中预留了全部对接钩子。
安装依赖
按 node-canvas 官方文档完成系统级安装后,在项目中安装两个 npm 包:
npm install canvas npm install two.js服务端渲染并保存 PNG
README 给出了一个完整的服务端示例:创建 800×600 的 node-canvas 画布、用Two.Utils.polyfill补齐 Two.js 所需的 DOM 接口、构建场景、渲染后写出 PNG 文件。
var { createCanvas, Image } = require('canvas'); var Two = require('two.js') var fs = require('fs'); var path = require('path'); var width = 800; var height = 600; var canvas = createCanvas(width, height); Two.Utils.polyfill(canvas, Image); var time = Date.now(); var two = new Two({ width: width, height: height, domElement: canvas }); var rect = two.makeRectangle(width / 2, height / 2, 50, 50); rect.fill = 'rgb(255, 100, 100)'; rect.noStroke(); two.render(); var settings = { compressionLevel: 3, filters: canvas.PNG_FILTER_NONE }; fs.writeFileSync(path.resolve(__dirname, './images/rectangle.png'), canvas.toBuffer('image/png', settings)); console.log('Finished rendering. Time took: ', Date.now() - time); process.exit();polyfill 做了什么
关键的Two.Utils.polyfill实现在 src/utils/canvas-polyfill.js。它通过shim方法为 node-canvas 的Canvas对象补充tagName、nodeName、nodeType以及getAttribute/setAttribute方法,让 Two.js 能把它当作<canvas>DOM 元素使用;同时把 node-canvas 的Image构造函数注入CanvasPolyfill.Image,供加载位图素材时使用,并设置isHeadless = true标记无头模式,见 src/utils/canvas-polyfill.js#L19-L47。
这也解释了示例中构造参数domElement: canvas的作用:直接指定一个已有的绘制目标元素,它覆盖type参数的推断逻辑(见 src/two.js#L204-L215)。由于无头环境下不存在document,fullscreen/fitted模式不可用,应显式传入width与height;渲染使用一次性的two.render()而非动画循环,即可输出静态帧。若要输出图片序列做批处理,可循环创建画布、更新场景属性后逐帧render()并toBuffer()。
构建文档站(Two.js 网站)
Two.js 官方网站在仓库内自带构建工具链,基于Vuepress生成,文档内容来自wiki目录下众多的README.md。构建文档的命令如下:
npm run docs:generate // Generate README.md files for documentation from source code comments npm run docs:dev // Creates a local server to generate all documentation npm run docs:build // Builds out static site and associated files to wiki/.vuepress/dist对应脚本定义在 package.json:
docs:generate→node ./utils/document:用jsdoc-api扫描源码注释(见 utils/document.js),自动生成各模块的 README 文档;docs:dev→vuepress dev wiki:启动本地开发服务器实时预览;docs:build→vuepress build wiki:构建静态站点;docs:publish→./deploy.sh:发布部署。
文档工具链对 Node 版本有要求:需要 Node 24 LTS。仓库根目录的 .nvmrc 指定了v24.20.0,在使用 nvm 的环境下先执行nvm use即可自动切换到正确版本;未使用 nvm 的读者可手动安装.nvmrc中列出的 Node 版本。
深入:从 README 到源码的进阶路线
如果想进一步掌握 Two.js,建议沿着下列仓库路径深入:
- 核心入口与便利方法:src/two.js 定义了
makeRectangle、makeCircle、makePath、makeCurve、makeText、makeLinearGradient、makeSprite、makeImageSequence等全套“make”工厂方法(src/two.js#L653-L1152),它们统一完成“创建对象 → 加入根场景”的动作; - SVG 导入与解析:
two.interpret(svgNode)与two.load(svgPathOrText)可将 SVG 文档转换为 Two.js 场景对象,底层解析器为 src/utils/interpret-svg.js,支持path、polygon、polyline、circle、ellipse、rect、g、defs、use等 SVG 标签以及viewBox、transform、渐变填充的还原; - 渲染器实现:三种渲染器分别位于 src/renderers/svg.js、src/renderers/canvas.js、src/renderers/webgl.js。以 SVG 渲染器为例,路径顶点会被编译为
d属性字符串(src/renderers/svg.js#L68-L206),渐变效果写入<defs>,这与svg类型的默认构造行为一一对应; - 测试用例:仓库 tests/suite 下包含
canvas.js、svg.js、webgl.js、svg-interpreter.js等测试模块,可作为各渲染器与 SVG 解析行为的可运行参考。
版本与变更记录
Two.js 自 2012 年开始发展,完整变更记录存放在 wiki/changelog。从最早的基于 Three.js 的 alpha 版本到当前版本,README 建议查阅该目录跟踪每一次演进细节。当前仓库版本号可在 package.json 中确认(v0.8.24),构建后的运行时版本号通过Two.Version常量访问。
总结
本文围绕仓库 README.md 完整梳理了 Two.js 的五大使用场景:以<script>或 npm 快速引入并搭建旋转矩形动画;通过utils/build.js定制发行包;在 ES6 / 框架环境下按需导入子模块;在 Node.js 无头环境中借助 node-canvas 输出 PNG;以及基于 Vuepress 的文档站构建流程。与此同时,我们从 src/two.js、src/constants.js、src/renderers、src/utils/canvas-polyfill.js 等源码中印证了构造参数默认值、动画循环机制、事件系统、渲染器类型表与无头渲染钩子等实现细节。对于需要“一套代码、多端渲染”的二维图形项目,Two.js 的 renderer agnostic 设计提供了一个简洁而完整的切入点。
- 图形学
- 前端
【免费下载链接】two.js
A renderer agnostic two-dimensional drawing api for the web
相关推荐
如何微调ALBERT Large v1:从零开始构建自定义NLP模型
如何微调ALBERT Large v1:从零开始构建自定义NLP模型 ALBERT Large v1是一款轻量级yet高性能的NLP预训练模型,它通过参数共享技
two.js终极指南:WebGL、Canvas与SVG三维绘图API详解
two.js终极指南:WebGL、Canvas与SVG三维绘图API详解 two.js是一个功能强大的Web二维绘图API,它能够帮助开发者轻松创建跨平台的图形
图形学前端StringManipulation社区贡献指南:如何参与这个开源项目并提交你的改进
StringManipulation社区贡献指南:如何参与这个开源项目并提交你的改进 欢迎来到StringManipulation插件社区!🎉 如果你是Int
开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考