首页/新闻资讯/正文详情

Two.Collection 源码解析:two.js 中带事件传播的类数组容器

发布时间:2026/9/25 3:09:38 来源:云帆数科 栏目:资讯中心
Two.Collection 源码解析:two.js 中带事件传播的类数组容器
图形学前端【免费下载链接】two.jsA renderer agnostic two-dimensional drawing api for the web项目地址https://gitcode.com/gh_mirrors/tw/two.js点击查看免费下载Two.Collection 是 two.js 内置的类Array容器它在原生数组行为之上加入了基于 Two.Events 的事件传播机制。凡是使用push、pop、shift、unshift、splice、sort、reverse修改集合内容的操作都会自动触发insert、remove、order三类事件从而让外部监听者实时感知集合变化。在 two.js 中Two.Group的子元素列表、渐变色的stops、点的vertices集合均由它驱动是理解 two.js 对象模型与事件体系的关键一环。读完本文你将掌握 Two.Collection 的完整 API、事件触发规则以及它在源码内部的实际使用模式。什么是 Two.Collection根据 collection 文档 的定义AnArraylike object with additional event propagation on actions.pop,shift, andsplicetriggerremovedevents.push,unshift, andsplicewith more than 2 arguments trigger inserted. Finally,sortandreversetriggerorderevents.即它是一个Array子类任何修改集合结构的操作都会向外广播事件。它继承自 Two.Events因此天然具备 two.js 全套事件能力on/off/bind/unbind/trigger等。从类型声明src/collection.d.ts可以看出其泛型设计declare module two.js/src/collection { export class CollectionT any extends ArrayT { constructor(...args: any[]); on(...args: any[]): any; bind(...args: any[]): any; off(...args: any[]): any; trigger(...args: any[]): any; listen(...args: any[]): any; ignore(...args: any[]): any; } }默认元素类型为any也可显式指定例如CollectionTwo.Shape。方法、事件与触发时机对照Two.Collection 复写了 7 个数组方法见 src/collection.js下表总结了每个方法与对应事件的完整关系方法触发事件事件负载传给监听器的参数说明push(...items)insert被推入的所有元素对应Events.Types.insertunshift(...items)insert被插入的所有元素对应Events.Types.insertpop()remove被弹出移除的单个元素对应Events.Types.removeshift()remove被移除的单个元素对应Events.Types.removesplice(start, deleteCount, ...items)removeinsertorderremove 携带被删除元素insert 携带新增元素仅当参数多于 2 个即存在插入项时才会额外触发insert与ordersort(compareFn)order无对应Events.Types.orderreverse()order无对应Events.Types.order这些事件名定义在 src/events.js 的Events.Types常量中static Types { play: play, pause: pause, update: update, render: render, resize: resize, change: change, remove: remove, insert: insert, order: order, load: load }各方法的源码实现细节push/unshift—— 插入事件src/collection.jspush() { const pushed super.push.apply(this, arguments); this.trigger(Events.Types.insert, arguments); return pushed; } unshift() { const unshifted super.unshift.apply(this, arguments); this.trigger(Events.Types.insert, arguments); return unshifted; }注意这里的实现技巧先调用原生super.push / super.unshift完成数组变更再以arguments而非返回值作为事件负载触发insert保证监听器拿到的是完整的新增元素列表。pop/shift—— 移除事件src/collection.jspop() { const popped super.pop.apply(this, arguments); this.trigger(Events.Types.remove, [popped]); return popped; } shift() { const shifted super.shift.apply(this, arguments); this.trigger(Events.Types.remove, [shifted]); return shifted; }移除事件负载是单元素数组监听器可以从中取回被删掉的对象。splice—— 复合事件src/collection.jssplice() { const spliced super.splice.apply(this, arguments); this.trigger(Events.Types.remove, spliced); if (arguments.length 2) { const inserted this.slice( arguments[0], arguments[0] arguments.length - 2 ); this.trigger(Events.Types.insert, inserted); this.trigger(Events.Types.order); } return spliced; }arguments.length 2判断的依据是splice的签名是(start, deleteCount, ...items)当传入的实参超过 2 个就意味着存在新增元素...items非空。此时先触发remove携带被删元素再通过slice(start, start items.length)提取实际插入的元素段并触发insert最后触发order通知渲染层元素顺序可能发生变化。sort/reverse—— 顺序事件src/collection.jssort() { super.sort.apply(this, arguments); this.trigger(Events.Types.order); return this; } reverse() { super.reverse.apply(this, arguments); this.trigger(Events.Types.order); return this; }两者均返回this与原生Array.prototype.sort一致以便链式调用。保留原生行为的其他方法indexOf仍然委托给原生实现src/collection.js而map则被改写为返回普通数组resultssrc/collection.js并支持传入可选的scope作为回调的this上下文与原生map的第二参数语义保持一致map(func, scope) { const results []; for (let key 0; key this.length; key) { const value this[key]; let result; if (scope) { result func.call(scope, value, key); } else { result func(value, key); } results.push(result); } return results; }构造函数与初始化方式构造函数src/collection.js支持两种初始化输入传入一个数组new Two.Collection([a, b, c])此时会展开数组并依次push因此会触发一次insert事件传入多个散落参数new Two.Collection(a, b, c)等价于逐个添加。constructor() { super(); if (arguments[0] Array.isArray(arguments[0])) { if (arguments[0].length 0) { this.push.apply(this, arguments[0]); } } else if (arguments.length 0) { this.push.apply(this, arguments); } }事件系统如何注入多继承技巧Collection继承自Array无法再直接继承Events因此源码采用「组合 委托」实现事件能力src/collection.js通过私有字段#events new Events()持有真正的 Events 实例用 getter/setter 将_events、_bound代理到内部实例getter 天然不可枚举因此这些状态不会污染数组的枚举/序列化行为把addEventListener、on、bind、removeEventListener、off、unbind、dispatchEvent、trigger、listen、ignore全部转发给内部 Events 实例。export class Collection extends Array { #events new Events(); get _events() { return this.#events; } set _events(e) { this.#events e; } addEventListener() { return this.#events.addEventListener?.apply(this, arguments); } on() { return this.#events.on?.apply(this, arguments); } bind() { return this.#events.bind?.apply(this, arguments); } trigger() { return this.#events.trigger?.apply(this, arguments); } // ... 其余方法同理 }这意味着每个Collection实例的this就是事件分发的作用域监听器回调中的this指向集合本身。实战监听集合变化综合以上 API一个典型的监听示例假设页面已通过script src.../two.js引入或在模块环境中import { Two } from two.jsconst collection new Two.Collection(); // 监听插入 collection.on(Two.Events.Types.insert, function(items) { console.log(inserted:, items); }); // 监听移除 collection.on(Two.Events.Types.remove, function(items) { console.log(removed:, items); }); // 监听顺序变化 collection.on(Two.Events.Types.order, function() { console.log(order changed); }); collection.push(a, b); // 触发 insert参数 [a, b] collection.push(c); // 触发 insert参数 [c] collection.pop(); // 触发 remove参数 [c] collection.unshift(x); // 触发 insert参数 [x] collection.sort(); // 触发 order collection.reverse(); // 触发 order collection.splice(0, 1, y, z);// 先 remove携带被删元素再 insert携带 [y,z]最后 order事件名insert、remove、order也可以直接以字符串形式传入如collection.on(insert, cb)但建议使用Two.Events.Types常量以保证拼写正确。在 two.js 内部的实际应用Two.Collection 并非孤立的工具类而是构成了 two.js 场景图scene graph与资源管理的基础。以下均为可从当前仓库源码直接验证的使用点。1.Two.Group.Children子元素集合src/children.jsTwo.Group的子元素列表Children extends Collection在insert/remove事件中同步维护按id索引的ids映射实现「既可按索引访问又可按对象 id 查找」的混合访问export class Children extends Collection { #ids {}; constructor(children) { super(children); this.attach(children); this.on(Events.Types.insert, this.attach); this.on(Events.Types.remove, this.detach); } attach(children) { for (let i 0; i children.length; i) { const child children[i]; if (child child.id) { this.ids[child.id] child; } } return this; } detach(children) { for (let i 0; i children.length; i) { delete this.ids[children[i].id]; } return this; } }这正是「事件驱动索引同步」的教科书式应用向 Group 添加/移除子元素时ids映射通过insert/remove事件自动保持一致无需在Group各处手动维护。2. 渐变stops与Points的vertices集合Gradient 用new Collection((stops || []).slice(0))管理颜色控制点stops并监听集合变化以重算渐变纹理Points 用new Collection(vertices || [])管理顶点集合Points内部还专门写了「针对不完整 Collection 对象的 unbind 错误忽略」逻辑src/shapes/points.js#L326从侧面印证了「集合自身可被销毁/替换」这一使用模式。3. 作为静态导出挂载src/two.jsTwo.Collection通过静态属性对外暴露src/two.js#L329static Collection Collection;因此用户可以直接使用new Two.Collection()或在Two.Group、Two.Gradient、Two.Points等对象的源码中继续沿用这一类型。小结Two.Collection 是 two.js 中连接「数据结构」与「事件体系」的枢纽它继承了原生Array的完整数组能力又借助Events混入为每一次结构变更提供insert/remove/order三种事件通知。理解这张 [方法 → 事件] 对照表以及Group.Children借助事件维护ids索引的实现思路能帮助你更深入地把控 two.js 场景图的生命周期也便于在自定义组件中复用它进行「可被监听」的集合管理。相关代码可继续阅读 src/collection.js、src/events.js 与 src/children.js。赞分享图形学前端【免费下载链接】two.jsA renderer agnostic two-dimensional drawing api for the web项目地址https://gitcode.com/gh_mirrors/tw/two.js点击查看免费下载上一篇vim-airline语法高亮性能优化设置设置下一篇android-sunflower中的基准测试结果解释指标含义创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关推荐

Codex流式中断根因与TaoToken协议适配指南
Codex流式中断根因与TaoToken协议适配指南

1. 项目概述:这不是简单的URL替换,而是Codex服务链路的底层重定向改造Codex不是普通插件,它是把本地编辑器(VS Code、Cursor)变成AI原生开发环境的核心协议桥。当用户看到“stream disconnected”报错时,90… · 2026/9/25 3:09:38

FLUX.1 Kontext 本地图像编辑:从零到 TensorRT 加速实战
FLUX.1 Kontext 本地图像编辑:从零到 TensorRT 加速实战

FLUX.1 Kontext 本地图像编辑:从零到 TensorRT 加速实战 【免费下载链接】flux Official inference repo for FLUX.1 models 项目地址: https://gitcode.com/GitHub_Trending/flux49/flux 场景速览:把图里的 Logo 换成品牌文字 任务很具体&#… · 2026/9/25 3:09:38

洗碗机水泵EMC整改:从电流路径失控到高集成方案落地
洗碗机水泵EMC整改:从电流路径失控到高集成方案落地

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 3:09:32

Linux 内核防御机制:`__ro_after_init` 只读数据保护与 `mmap_min_addr` 空指针防护解析(CTF-Wiki 内核 Pwn 篇)
Linux 内核防御机制:`__ro_after_init` 只读数据保护与 `mmap_min_addr` 空指针防护解析(CTF-Wiki 内核 Pwn 篇)

文档网络安全教程 【免费下载链接】ctf-wiki Come and join us, we need you! 项目地址: https://gitcode.com/gh_mirrors/ct/ctf-wiki 点击查看 免费下载 导读 在内核 Pwn 的攻防博弈中,"只读数据保护"与"低地址空间封锁"是两种极… · 2026/9/25 3:38:31

Rematch 插件体系详解:五大官方插件与插件扩展机制
Rematch 插件体系详解:五大官方插件与插件扩展机制

前端 【免费下载链接】rematch The Redux Framework 项目地址: https://gitcode.com/gh_mirrors/re/rematch 点击查看 免费下载 Rematch 是建立在 Redux 之上的状态管理框架,其最大的工程特征之一就是通过插件(Plugin)机制把 Imm… · 2026/9/25 3:38:19

MCP协议六大安全风险揭秘:从提示注入到数据泄露的AI安全指南
MCP协议六大安全风险揭秘:从提示注入到数据泄露的AI安全指南

MCP协议最近在AI圈里热度很高,有人把它比作“AI生态的USB-C接口”,说它能让所有AI应用和工具像插拔U盘一样互相连接。这个比喻确实形象,MCP(Model Context Protocol)的定位就是打破信息孤岛,让大模型、数据… · 2026/9/25 3:38:13

网络安全等级保护与商用密码评估落地指南:97页实操解析
网络安全等级保护与商用密码评估落地指南:97页实操解析

手里拿到这份97页的《网络安全等级保护与商用密码应用安全性评估工作指南》时,我的第一反应是:终于有人愿意把两件事写进同一本手册里了。做安全的人都清楚,等级保护和商用密码应用安全性评估过去经常被当成两条平行线,业务部门要… · 2026/9/25 3:38:13

Umi-OCR:离线 OCR 实操指南,截图、批量图片与 PDF 识别
Umi-OCR:离线 OCR 实操指南,截图、批量图片与 PDF 识别

Umi-OCR:离线 OCR 实操指南,截图、批量图片与 PDF 识别 【免费下载链接】Umi-OCR OCR software, free and offline. 开源、免费的离线OCR软件。支持截屏/批量导入图片,PDF文档识别,排除水印/页眉页脚,扫描/生成二维码。… · 2026/9/25 3:38:01

SharePoint默认打开方式设置与排错:View Mode与文件关联
SharePoint默认打开方式设置与排错:View Mode与文件关联

1. 两种默认打开方式的实际体验差异先从一个很常见的场景说起。我接到过不少同事的求助,说从 SharePoint 文档库里点开一个 Word 文件,浏览器直接弹出一个预览页面,想编辑还得再点一次"在 Word 中编辑",多了一步操作不说… · 2026/9/25 3:38:00

数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)
数值优化(Numerical Optimization)学习系列-03-共轭梯度方法(Conjugate Gradient)

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31

创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战
创维E900V22D刷机全攻略:S905L3SB芯片兼容性解析与救砖实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:31

MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX
MQTT协议原理与Broker服务器搭建实战:从Mosquitto到EMQX

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views … · 2026/9/25 1:00:37

了解更多?预约专属演示

我们的顾问将为您一对一讲解产品与方案

企业微信二维码