ARTICLE DETAIL

资讯详情

深耕网站视觉设计与运营推广的一线实战洞察。

Two.js 场景图变换基石:Two.Shape 全面指南

Two.js 场景图变换基石:Two.Shape 全面指南 图形学前端【免费下载链接】two.jsA renderer agnostic two-dimensional drawing api for the web项目地址https://gitcode.com/gh_mirrors/tw/two.js点击查看免费下载导读Two.Shape是 Two.js 场景图中所有可绘制对象的变换基类它为圆、矩形、路径、文本乃至分组等一切形状统一提供「位置、旋转、缩放、斜切」四类变换属性以及矩阵计算、命中测试、序列化、克隆与销毁等基础能力。阅读本文后你将掌握Two.Shape的完整 API 与变换矩阵的底层计算原理能够用它统一操控场景中任意图形对象的变换与生命周期并结合toObject/fromObject实现场景的持久化与还原。本文内容以 wiki/docs/shape/README.md 为骨架源码佐证来自 src/shape.js、src/element.js、src/matrix.js、src/utils/math.js 及测试套件 tests/suite/shapes.js、tests/suite/hit-test.js。一、Two.Shape 是什么场景图的基础变换对象从源码定义看src/shape.js 中类注释将其定位为「Two.js 场景图的基础变换对象foundational transformation object」export class Shape extends Element { // ... this.isShape true; }它的继承链是Shape继承自Two.Element继承自Two.Events。也就是说Two.Shape首先是一个具备事件绑定能力的场景元素id、className、renderer都来自Two.Element参见 src/element.js在此之上额外增加了变换相关的全部能力。在 TypeScript 类型声明 src/shape.d.ts 中同样可以看到export class Shape extends TwoElement并定义了static Properties [position,rotation,scale,skewX,skewY,matrix,worldMatrix]。一个实用的判断方式在 Two.js 中几乎任何可绘制对象Two.Circle、Two.Rectangle、Two.Path、Two.Text、Two.Group、Two.Image、Two.Sprite等都可以视为「一个Two.Shape加上各自的几何定义」。因此本页的每一个 API 都适用于这些子类实例——这是理解 Two.js 场景图的关键入口。二、变换五件套position、rotation、scale、skewX、skewYTwo.Shape通过五个「相对父级」的变换属性来描述自身姿态它们全部定义在构造函数中src/shape.js默认值如下属性类型默认值说明positionTwo.Vector(0, 0)形状相对父级放置的 x/y 坐标rotationNumber0相对父级的旋转角度单位是弧度radiansscaleNumber或Two.Vector1相对父级的缩放比例skewXNumber0沿 x 轴方向的斜切角度skewYNumber0沿 y 轴方向的斜切角度2.1 position平移position是一个 Two.Vector直接赋值或修改其x、y即可移动形状例如circle.position.x 100; circle.position.y 50; // 等价于 circle.position.set(100, 50);2.2 rotation旋转注意单位是弧度rotation的单位是弧度而非角度Math.PI为半圈、2 * Math.PI为一整圈circle.rotation Math.PI / 4; // 顺时针旋转 45°2.3 scale均匀缩放与非均匀缩放scale默认是Number1此时为均匀缩放。官方文档特别强调可以将scale替换为一个Two.Vector来实现非均匀缩放例如只拉宽不拉高shape.scale new Two.Vector(2, 1); // x 方向放大 2 倍y 方向保持原样从源码看src/shape.jsscale的 setter 会判断赋值是否为Vector实例并动态绑定/解绑其change事件到flagMatrix处理器因此在Two.Vector模式下修改向量分量也会触发矩阵更新_update中同样区分了两种缩放方式src/shape.js。2.4 skewX / skewY斜切skewX沿 x 轴方向、skewY沿 y 轴方向对形状施加斜切shear单位为弧度角度值默认均为0shape.skewX 0.2; shape.skewY -0.1;2.5 translationposition 的别名translation是position的别名src/shape.js二者完全等价读写互相同步shape.translation.set(20, 30); // 与 shape.position.set(20, 30) 等价三、矩阵层matrix 与 worldMatrix3.1 matrix本地的变换矩阵matrix是形状自身相对父级的 3×3 变换矩阵src/shape.js。核心要点position、rotation、scale、skewX、skewY五个属性在被修改时会把值写入matrix而这个matrix正是最终发送给渲染器绘制的内容。底层组装顺序在_update中src/shape.jsthis._matrix.identity() .translate(this.position.x, this.position.y) // 先平移 .rotate(this.rotation) // 再旋转 .scale(this._scale) // 然后缩放 .skewX(this.skewX) // 接着 x 斜切 .skewY(this.skewY); // 最后 y 斜切即在标准变换流水线中先平移、再旋转、再缩放、最后依次施加两个方向的斜切。3.2 worldMatrix场景世界空间的变换矩阵worldMatrix表示形状在整个场景中的最终变换矩阵src/shape.js。与matrix不同它是一个只读计算值每次读取worldMatrix都会通过Two.Utils.getComputedMatrix现场计算src/shape.js。getComputedMatrix的实现位于 src/utils/math.js它从当前对象沿parent链向上收集每一级的_matrix再按「根 → 叶子」顺序依次相乘最终得到形状在世界空间的累积变换。这也解释了为什么修改父级Group的变换会影响子形状的worldMatrix而matrix保持不变。3.3 手动接管矩阵matrix.manualTwo.Matrix提供manual标志src/matrix.js当设为true时Two.js 不再自动计算矩阵改由开发者直接管理同时translation、rotation、scale的设置将不再生效官方注释明确说明「Setting totruenullifies Two.Shape#translation, rotation and scale」。_update中的判断即为此逻辑src/shape.jsif (!this._matrix.manual this._flagMatrix) { // 自动组装矩阵... }copy、clone也都会在源对象matrix.manual true时同步复制矩阵src/shape.js、src/shape.js。四、renderer渲染器专用存储区renderer是一个供各渲染器存放私有状态的对象src/shape.js。官方文档明确给出警告手动操作该对象可能引发不可预期的后果一般不建议直接修改。一个被官方文档明示的合法用途是配合Two.SVGRenderer通过shape.renderer.elem可以拿到形状对应的底层 SVG 元素进而做 DOM 级操作如读取或覆盖 SVG 属性。在 src/element.js 中_renderer被初始化为{}Two.Shape构造时在此基础上挂载flagMatrix回调src/shape.js。五、场景图生命周期方法5.1 addTo(group)便捷入树addTo是把自己加入某个场景图父级通常是Two.Group或Two实例的便捷方法等价于group.add(this)并返回this以支持链式调用src/shape.jsconst group new Two.Group(); circle.addTo(group); // 等价于 group.add(circle);5.2 remove()从场景中移除remove将形状从当前父级中移除src/shape.js若对象尚无父级则直接返回自身shape.remove(); // 从 scene 中摘除但实例仍可复用5.3 dispose()释放与解绑dispose用于释放形状绑定的对象、解绑相关事件src/shape.js。它首先调用父类Element.disposesrc/element.js——该方法会unbind()全部事件并清理各渲染器的资源SVG 的 DOM 节点、WebGL 的纹理与缓冲区、Canvas 的上下文缓存随后再对translation即position和可能为Vector的scale分别执行unbind()。注意dispose是彻底释放语义调用后该实例通常不应再继续用于渲染。5.4 contains(x, y, options)命中测试contains判断给定坐标是否落在形状的包围矩形内src/shape.js返回Booleanshape.contains(x, y, { ignoreVisibility: true, // 是否忽略 visiblefalse 的形状默认 false tolerance: 4, // 以像素为单位的命中容差padding默认 0 });从源码可以看到三条关键行为默认情况下visible false或opacity 0的形状会直接返回false设置ignoreVisibility: true可忽略可见性检查src/shape.js。命中判定基于getBoundingClientRect()计算的包围矩形并按容差向外扩展src/shape.js。该方法期望世界空间world-space坐标——即渲染器输出的像素空间坐标例如鼠标的clientX/clientY减去 canvas 偏移并考虑像素比之后的值。这一点在官方 nota-bene 中明确标注。测试套件 tests/suite/hit-test.js 对该 API 的覆盖包括圆心命中、远点排除、线段描边在容差内命中、旋转矩形命中、不可见形状配合ignoreVisibility命中等场景。六、复制与序列化copy、clone、toObject、fromObject6.1 copy(shape)属性拷贝copy将一个Two.Shape的变换属性拷贝到另一个实例上src/shape.js。它先调用Element.copy同步renderer.type与className注意不会拷贝id以保持实例唯一性见 src/element.js再依次拷贝position、rotation、scale、skewX、skewY最后在源矩阵为manual时同步矩阵。6.2 clone(parent?)克隆clone创建与当前形状同值的新实例并返回该实例src/shape.js。若传入可选的parent如Two.Group克隆体会被自动加入该父级const twin circle.clone(group); // 克隆并加入 group从源码看clone复制position、rotation、scale、skewX、skewY并在源matrix.manual时同步矩阵最后调用_update()使克隆体矩阵处于最新状态。6.3 toObject()导出为 JSON 兼容对象toObject生成一个 JSON 兼容的对象描述形状的全部信息src/shape.js{ renderer: { type: shape }, isShape: true, translation: { x, y }, rotation: ..., scale: ...|{ x, y }, skewX: ..., skewY: ..., matrix: { a, b, c, d, e, f }, id: ..., className: ... }注意导出的scale在Two.Vector模式下会序列化为{x, y}对象rotation字段从源码看取的是translation.rotation一处值得留意的既有行为。由于对象中同时携带matrix的完整分量toObject的结果可直接用于JSON.stringify持久化。6.4 fromObject(obj)从对象恢复实例Two.Shape.fromObject是静态工厂方法接收toObject产出的对象字面量创建并返回新的Two.Shape实例src/shape.jsconst restored Two.Shape.fromObject(savedObject);其实现为new Shape().copy(obj)并在对象携带id字段时恢复该idsrc/shape.js。官方 nota-bene 强调fromObject与toObject是一对配套方法——这一点在测试中被大量验证在 tests/suite/shapes.js 中Two.Points、Two.ArcSegment、Two.Circle、Two.Ellipse、Two.Polygon、Two.Rectangle、Two.RoundedRectangle、Two.Star等子类均验证了「toObject→fromObject往返后几何参数、填充色、id均保持一致」的闭环并进一步用{ ...copied.toObject(), id: origin.id }与原始toObject()做全等断言。七、性能机制脏标记与延迟更新Two.Shape内部通过一组私有布尔标志flag实现「按需重算」私有标志作用_flagMatrix矩阵是否需要重新组装初始为true_flagScale缩放是否发生变化任何变换属性被赋值时都会将_flagMatrix置为trueposition与scaleVector模式还会绑定change事件到flagMatrix回调src/shape.js。渲染器在绘制前调用_update()官方提示每帧最多调用一次仅在_flagMatrix为真时重新组装矩阵随后flagReset()统一清零标志src/shape.js。这保证了「属性变了才重算不变则零开销」是 Two.js 渲染性能的关键设计。八、组合实战一个统一变换的完整示例把上述 API 串起来得到一套标准的 Two.js 形状操控流程// 1. 创建形状以圆为例任何 Two.Shape 子类同理 const circle new Two.Circle(50, 50, 30); const scene new Two({ type: Two.Types.svg }).appendTo(document.body); // 2. 变换位置 / 旋转 / 缩放 / 斜切 circle.position.set(200, 150); // 平移 circle.rotation Math.PI / 4; // 旋转 45° circle.scale new Two.Vector(2, 1); // 非均匀缩放 circle.skewX 0.2; // x 方向斜切 // 3. 读取世界矩阵结合父级变换后的最终矩阵 const world circle.worldMatrix; // 4. 命中测试世界坐标 const hit circle.contains(mouseX, mouseY, { tolerance: 5 }); // 5. 克隆并加入分组 const clone circle.clone(parentGroup); // 6. 序列化与恢复 const snapshot JSON.stringify(circle.toObject()); const restored Two.Circle.fromObject(JSON.parse(snapshot)); // 7. 移出场景 / 彻底释放 circle.remove(); circle.dispose();九、结语Two.Shape是整个 Two.js 场景图中名副其实的「变换基石」从平移旋转缩放到世界矩阵计算从命中测试到序列化往返再到脏标记驱动的增量更新它为上层所有形状提供了统一、可预测、可复用的行为契约。深入理解它就等于掌握了 Two.js 中「任意对象如何被摆放、如何被绘制、如何被序列化」的全链路答案。进一步阅读Two.Element 基类文档id、className的来源Two.Matrix 文档 与 Two.Vector 文档Two.Group 文档addTo的目标父级SVG 渲染器文档shape.renderer.elem的用法各种内置形状文档Two.Shape的具体子类Two.Path 文档变换 顶点编辑的组合应用赞分享图形学前端【免费下载链接】two.jsA renderer agnostic two-dimensional drawing api for the web项目地址https://gitcode.com/gh_mirrors/tw/two.js点击查看免费下载相关推荐PixiJS v8 场景对象Scene Objects完全指南场景图、变换、遮罩与滤镜实战PixiJS v8 场景对象Scene Objects完全指南场景图、变换、遮罩与滤镜实战 导读 在 PixiJS v8 中屏幕上的一切可见内容都是 场前端图形学原神抽卡数据分析终极指南免费开源工具genshin-wish-export完整教程原神抽卡数据分析终极指南免费开源工具genshin wish export完整教程 还在为原神抽卡记录无法保存而烦恼吗想知道自己的抽卡概率到底如何吗gen桌面应用Two.js CanvasRenderer 详解场景图到 canvas / 的 2D 渲染管线Two.js CanvasRenderer 详解场景图到 canvas / 的 2D 渲染管线 导读 Two.CanvasRenderer 是 Two.js图形学前端上一篇Fontello性能优化 checklist上线前必做的15项检查下一篇OI-wiki 字符串专题Main–Lorentz 算法——用分治与 Z 函数在 O(n log n) 时间内找出字符串全部重串创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表