图形学前端【免费下载链接】two.jsA renderer agnostic two-dimensional drawing api for the web项目地址https://gitcode.com/gh_mirrors/tw/two.js点击查看免费下载导读Two类是 Two.js 的入口点entrypoint通过new Two实例化即可搭建一个可渲染的场景同时Two也是整个库对外暴露的全局命名空间所有子类、函数与工具函数都挂载其上。本文以仓库中的官方 API 文档 wiki/docs/two/README.md 为骨架结合 src/two.js 与 src/constants.js 的实现源码、以及 tests/suite/ 下的测试用例系统讲解Two的构造函数选项、静态常量、实例属性与全部make*图形工厂方法、SVG 解释/加载流程与动画循环机制。读完本文你将能独立完成一个 Two.js 场景的搭建、图形绘制、动画控制、命中检测与 SVG 导入。一、认识 Two入口点与全局命名空间Two继承了 Two.Events事件系统因此实例天然具备bind/unbind、trigger等事件能力。正如文档所述The entrypoint for Two.js. Instantiate anew Twoin order to setup a scene to render to.Twois also the publicly accessible namespace that all other sub-classes, functions, and utilities attach to.从源码 src/two.js 可见类内部通过_events new Events()持有事件对象并对外暴露了on/bind/addEventListener、off/unbind/removeEventListener、trigger/dispatchEvent等别名方法src/two.js#L102-L131保证与浏览器事件接口习惯的兼容性。同时Two作为命名空间在 src/two.js#L328-L375 上挂载了全部核心类与工具Two.Anchor、Two.Collection、Two.Group、Two.Matrix、Two.Path、Two.Text、Two.Vector效果类Two.Gradient、Two.Image、Two.ImageSequence、Two.LinearGradient、Two.RadialGradient、Two.Sprite、Two.Stop、Two.Texture基础形状Two.ArcSegment、Two.Circle、Two.Ellipse、Two.Line、Two.Points、Two.Polygon、Two.Rectangle、Two.RoundedRectangle、Two.Star以及三种渲染器Two.CanvasRenderer、Two.SVGRenderer、Two.WebGLRenderer。这意味着你既可以new Two()创建场景也可以直接用new Two.Circle(...)、new Two.Path(...)等构造对象。二、构造函数与实例化选项构造函数签名new Two(options)文档给出的options参数表如下参数类型默认值说明options.fullscreenBooleanfalse设为true时舞台自动适配父级文档document的宽高此参数会覆盖width/height同时覆盖options.fittedoptions.fittedBooleanfalse设为true时舞台自动适配父元素的宽高此参数会覆盖width/heightoptions.widthNumber640构造时舞台的宽度可在之后通过two.width修改options.heightNumber480构造时舞台的高度可在之后通过two.height修改options.typeStringTwo.Types.svg渲染器类型可选值见下文 Two.Typesoptions.autostartBooleanfalse设为true时实例自动通过requestAnimationFrame进入绘制循环等价于调用Two.play()options.domElementElement—要绘制进其中的 canvas 或 SVG 元素当元素类型与type不匹配时它会覆盖options.type这些默认值在源码构造函数中有明确对应src/two.js#L184-L191const params _.defaults(options || {}, { fullscreen: false, fitted: false, width: 640, height: 480, type: Two.Types.svg, autostart: false, });渲染器类型解析逻辑构造函数会先按默认值补齐参数随后处理domElement的覆盖逻辑src/two.js#L204-L215当传入的domElement是 DOM 元素时会比对this.type与元素tagName的组合是否属于CanvasRenderer-canvas、WebGLRenderer-canvas、SVGRenderer-svg三者之一若不一致则根据元素标签名将type重置为Two.Types[tagName]。例如你传入一个svg元素但声明type: canvasTwo.js 会将其纠正为 SVG 渲染器。紧接着this.renderer new Twothis.type; this.setPlaying(params.autostart);即用字符串形式的type动态实例化对应的渲染器类。最后实例被Two.Instances.push(this)注册进全局实例列表若autostart为真则启动raf.init()动画循环src/two.js#L264-L271。三种典型实例化方式全屏自适应最简起步来自仓库 README.md 的官方 boilerplate!doctype html html head meta charsetutf-8 script srcjs/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指定尺寸与渲染器const two new Two({ width: 800, height: 600, type: Two.Types.webgl, // WebGLRenderer }).appendTo(document.body);挂载到已有 DOM 元素domElement优先于typeconst canvas document.getElementById(my-canvas); const two new Two({ width: 800, height: 600, domElement: canvas, // 传入 canvas 时自动采用 CanvasRenderer 或 WebGLRenderer });三、静态成员与常量Two上定义的静态常量多数直接引用 src/constants.js 中的Constants对象src/two.js#L273-L319。Two.Types —— 渲染器类型字典The different rendering types available in the library.Types: { webgl: WebGLRenderer, svg: SVGRenderer, canvas: CanvasRenderer, }对应源码在 src/constants.js#L17-L21。这是「渲染器无关renderer agnostic」设计的核心同一套 API 可分别绘制到 WebGL、Canvas2D 与 SVG 三种上下文。构造函数中正是用new Twothis.type动态选择渲染器实现。Two.Version 与 Two.PublishDateTwo.Version库当前版本字符串构建时由Constants.Version注入% version %模板。Two.PublishDate构建流程自动生成的发布日期用于验证版本候选。Two.Identifier —— 对象 id 前缀String prefix for all Two.js objects ids. This trickles down to SVG ids.值为two-src/constants.js#L39所有 Two.js 对象的 id 均以该前缀开始并会传导到 SVG 元素的 id 上方便 DOM 侧定位。Two.Resolution —— 弧线采样分辨率Default amount of vertices to be used for interpreting Arcs and ArcSegments.值为12src/constants.js#L45。这是解释 Arc 与 ArcSegment 时的默认顶点数量数值越大曲线越平滑、顶点越多。Two.AutoCalculateImportedMatrices —— SVG 导入矩阵计算开关When importing SVGs through theTwo.interpretandTwo.load, this boolean determines whether Two.js infers and then overrides the exact transformation matrix of the reference SVG.默认值为truesrc/constants.js#L52。文档特别提示falsecopies the exact transformation matrix values, but also sets the pathsmatrix.manual true.即设为true时 Two.js 自行推断并覆盖参考 SVG 的精确变换矩阵设为false时则原样拷贝矩阵值但同时会把路径的matrix.manual置为true意味着后续不再自动计算矩阵需手动维护。Two.Instances —— 实例注册表Registered list of all Two.js instances in the current session.默认空数组src/constants.js#L58。每次new Two()都会push自己。全局动画循环正是遍历该列表实现的function loop() { for (let i 0; i Two.Instances.length; i) { const t Two.Instances[i]; if (t.playing) { t.update(); } } Two.NextFrameId raf(loop); }见 src/two.js#L1262-L1271这解释了为何多个 Two.js 实例共享同一个requestAnimationFrame驱动。Two.uniqueId —— 自增 id 分配器静态函数返回一个不断递增的 NumberuniqueId: function () { return count; }src/constants.js#L65-L67用于所有 Two.js 对象的id分配。Two.Commands —— 路径命令映射Map of possible path commands. Taken from the SVG specification. Commands include:move,line,curve,arc, andclose.来自 src/utils/path-commands.js源自 SVG 规范。构造路径顶点时即为每个Anchor指定命令类型见下文makeArrow实现中Commands.move、Commands.line的使用。Two.Utils —— 工具函数集合A massive object filled with utility functions and properties.在 src/two.js#L59-L70 中通过_.extend聚合而成包含Two.Utils.Error错误类型TwoErrorTwo.Utils.getRatio来自 src/utils/device-pixel-ratio.js 的像素比计算Two.Utils.read按元素名索引的 SVG 解析函数集合其中Two.Utils.read.path用于解析 SVGpath元素或d属性字符串见 src/utils/interpret-svg.jsTwo.Utils.xhr异步加载工具以及_underscore 工具、CanvasPolyfill、Curves、math等展开内容。四、实例成员属性属性类型说明two.typeString实例所实例化的渲染器类型字符串如SVGRenderer默认构造时赋值two.rendererObject实例的渲染器实例可选类型见Two.Typestwo.sceneTwo.Group场景根 Two.Group容纳实例的所有对象因为它是一个 Group可以对其施加变换从而影响全部对象适合当作「反转的相机」使用two.widthNumber实例 DOM 元素的宽度two.heightNumber实例 DOM 元素的高度two.frameCountNumber已流逝的帧数整数two.timeDeltaNumber距上一帧流逝的时间毫秒two.playingBoolean是否正在通过自动的requestAnimationFrame更新这些属性在 src/two.js#L133-L179 中均有对应字段声明。其中scene在构造函数末尾被赋值为this.renderer.scenesrc/two.js#L265即渲染器持有的根 Group。五、实例方法生命周期与场景管理fit()仅当构造时传入fullscreen或fitted才会创建该方法。它根据传入的选项把实例的width/height设置为对应父级window或父元素的尺寸。源码中的fitToWindow与fitToParentsrc/two.js#L1229-L1250分别读取document.body.getBoundingClientRect()与renderer.domElement.parentElement.getBoundingClientRect()并调用renderer.setSize(width, height, this.ratio)。fullscreen模式下还会同步修改body与舞台元素的样式去边距、position: fixed并绑定窗口resize事件src/two.js#L226-L249。appendTo(elem)将 Two.js 舞台快捷挂载到指定 DOM 元素two.appendTo(document.body); // 返回 this支持链式调用实现为elem.appendChild(this.renderer.domElement)若实例处于fit模式且目标不是window还会把 resize 监听绑定到新父元素并立即update()src/two.js#L383-L395。play() 与 pause()play()启动内部动画循环将playing置为true并触发event:play。注意它发起的是一个requestAnimationFrame循环全局raf.init()见 src/two.js#L1260-L1276。pause()停止该实例的动画循环触发event:pause。two.play(); // 开始 two.pause(); // 暂停release(obj)Release a Two.Element’s events from memory and recurse through its children, effects, and/or vertices.释放某个 Two.Element 的事件监听并从内存中解除同时递归遍历其子元素、效果与顶点不传参时默认释放根Two.Group即scene。返回传入的对象。源码实现src/two.js#L432-L535完整展示了清理层级对对象调用unbind()解除事件解除fill、stroke上挂载的效果如 Gradient的监听递归vertices逐个解除顶点与其左右控制点controls.left/controls.right的监听递归children并解除集合监听清理渲染器资源SVG 中移除对应 DOM 节点、WebGL 中删除texture与positionBuffer、Canvas 中清理缓存上下文。getShapesAtPoint(x, y, options)返回指定世界坐标系与渲染输出一致的坐标空间下的图形列表从前到后排列即Array.Two.Shape。命中测试配置项参数说明options.visibleOnly仅返回可见图形options.includeGroups命中结果中包含组options.mode返回所有相交图形还是仅最顶层图形all/deepestoptions.deepestmode: deepest的别名options.precision曲线几何的细分精度options.tolerance命中测试的像素容差options.fill覆盖填充命中测试行为options.stroke覆盖描边命中测试行为options.filter用于从结果集中过滤图形的谓词函数文档提示该方法委托给根场景的Two.Group.getShapesAtPoint。源码印证src/two.js#L556-L561getShapesAtPoint(x, y, options) { if (this.scene typeof this.scene.getShapesAtPoint function) { return this.scene.getShapesAtPoint(x, y, options); } return []; }仓库测试 tests/suite/hit-test.js 对该 API 的行为做了验证two.getShapesAtPoint(100, 100)与two.scene.getShapesAtPoint(100, 100)结果一致{ mode: deepest }只返回最顶层图形{ visibleOnly: false }会把隐藏图形也纳入结果集。update() 与 render()update()渲染前一次性更新位置与计算再绘制到画布触发event:update并传入frameCount与timeDelta。使用Two.play()或构造时autostart: true会自动调用它。render()渲染场景中所有可绘制且可见的对象触发event:render并递增frameCount。two.update(); // 手动更新渲染一帧 two.render(); // 仅渲染从源码可见update()内部会计算timeDelta基于performance.now()、在fit模式下同步尺寸、在尺寸变化时调用renderer.setSize(width, height, this.ratio)最后trigger(update)再return this.render()src/two.js#L570-L597。add()、remove() 与 clear()add(objects)把 Two.js 对象加入场景的快捷方法支持传入数组也支持逐个传入多个参数。remove(objects)把对象从场景移除的快捷方法。clear()移除场景中的全部对象。文档特别提醒若希望浏览器进行垃圾回收应用层还需要删除自身的引用。two.add(rect, circle); // 逐个参数 two.remove([rect]); // 数组形式 two.clear(); // 清空场景实现分别委托给this.scene.add(...)、this.scene.remove(...)与this.scene.remove(this.scene.children)src/two.js#L618-L651。六、图形工厂方法 make*一行代码绘制各种形状所有make*方法的共同行为是创建对应对象并立即加入当前实例的scene然后返回该对象源码中统一为this.scene.add(obj); return obj;模式。这也意味着构造后无需再手动two.add()。线与箭头makeLine(x1, y1, x2, y2)→Two.Lineconst line two.makeLine(0, 0, 100, 100);makeArrow(x1, y1, x2, y2, size?)→Two.Path创建带箭头的路径并加入场景。源码实现src/two.js#L680-L742很直观先以Math.atan2计算连线角度构造主线段顶点Commands.move、Commands.line与两条呈 ±45° 的箭头线顶点Commands.line默认箭头长度headlen 10传入size可自定义最后path.noFill()并设置圆角cap/join。矩形与圆角矩形makeRectangle(x, y, width, height)→Two.Rectangleconst rect two.makeRectangle(100, 100, 50, 50); rect.fill rgb(255, 100, 100); rect.noStroke();makeRoundedRectangle(x, y, width, height, radius)→Two.RoundedRectangleradius接受 Number 或Two.Vector可分别控制四个角的圆角半径。圆与椭圆makeCircle(x, y, radius, resolution?)→Two.Circleconst circle two.makeCircle(100, 100, 50);makeEllipse(x, y, rx, ry, resolution?)→Two.Ellipseresolution默认为4源码 src/two.js#L789-L794 注释标明[resolution4]控制圆弧细分程度。星形与多边形makeStar(x, y, innerRadius, outerRadius, sides)→Two.Starconst star two.makeStar(100, 100, 30, 60, 5); // 五角星makePolygon(x, y, radius, sides)→Two.Polygonconst hex two.makePolygon(100, 100, 50, 6); // 正六边形曲线、路径与点集makeCurve(points)→Two.Pathpath.curved true两点用法传入Two.Anchor数组或直接传交替的x/y坐标值两种形式下最后一个参数都可以是可选 Boolean用于指定路径开/闭。创建后会自动居中并平移回包围盒中心源码 src/two.js#L841-L870const curve two.makeCurve(0, 0, 50, 80, 100, 40, true); // true 表示闭合makePath(points)→Two.Path同样支持Two.Anchor数组或交替x/y坐标值最后一个参数同样可选开/闭 Boolean同样自动居中const path two.makePath( new Two.Anchor(0, 0), new Two.Anchor(50, 80), new Two.Anchor(100, 40) ); // 或 const path two.makePath(0, 0, 50, 80, 100, 40, true);makePoints(points)→Two.Points支持Two.Vector数组或交替x/y坐标值源码会将数值参数组合成Two.Vector见 src/two.js#L931-L952const pts two.makePoints(0, 0, 20, 30, 40, 15, 60, 45);弧段makeArcSegment(x, y, innerRadius, outerRadius, startAngle, endAngle, resolution?)→Two.ArcSegmentresolution为构成弧段的顶点数量默认取Two.Resolution即12。const arc two.makeArcSegment(100, 100, 30, 60, 0, Math.PI * 1.5);文本makeText(message, x, y, styles?)→Two.Textstyles可描述 Two.Text.Properties 中的任意属性包括fill、stroke、linewidth、family、alignment、leading、opacity等const text two.makeText(Hello Two.js, 200, 100, { fill: #333, family: monospace, size: 24, alignment: center, });渐变makeLinearGradient(x1, y1, x2, y2, ...args)→Two.LinearGradientargs为任意数量的色标ramp stops即Two.Stop不提供时应用默认的黑→白两色渐变。效果类对象会被加入一个不可见的「definitions」组对应 SVG 的defs概念const lg two.makeLinearGradient(0, 0, 100, 100, new Two.Stop(0, #ff0000), new Two.Stop(1, #0000ff) ); rect.fill lg;makeRadialGradient(x1, y1, radius, ...args)→Two.RadialGradient用法与线性渐变一致只是以圆心 半径定义渐变区域const rg two.makeRadialGradient(100, 100, 80, new Two.Stop(0, #ff0000), new Two.Stop(1, #0000ff) );源码中两者均通过Array.prototype.slice.call(arguments, 4 / 3)收集剩余色标参数src/two.js#L1031-L1038、src/two.js#L1050-L1057不传色标时由渐变类内部补默认黑白色标。精灵、图片与图片序列makeSprite(src, x, y, columns?, rows?, frameRate?, autostart?)→Two.Spritesrc为图片 URL 或已创建的 Two.Texturecolumns/rows用于把一张雪碧图切分为动画帧frameRate为播放帧率autostart为真则立即play()。Sprite 既可当静态图片也可当动画使用const sprite two.makeSprite(spritesheet.jpg, 0, 0, 8, 8, 30, true);仓库测试资源 tests/images/spritesheet.jpg 即为这种整图切帧的典型素材。makeImage(src, x, y, width, height, mode?)→Two.Image图片会缩放以适配给定的宽高。mode控制适配方式如fill填充const img two.makeImage(logo.png, 0, 0, 200, 100);makeImageSequence(src, x, y, frameRate?, autostart?)→Two.ImageSequencesrc为图片路径数组或Two.Texture数组按帧率顺序播放形成动画。仓库测试资源 tests/images/sequence/ 中00000.png~00029.png即是供 ImageSequence 使用的序列帧素材const seq two.makeImageSequence( Array.from({ length: 30 }, (_, i) sequence/${String(i).padStart(5, 0)}.png), 0, 0, 24, true );纹理与组makeTexture(src, callback?)→Two.Texturesrc为图片 URL 或 DOM 图像类元素HTMLImageElement/HTMLCanvasElement/HTMLVideoElementcallback在图片加载完成后调用。注意Texture不会自动加入场景它是可复用的图像资源对象。makeGroup(objects)→Two.Group支持传入 Two.js 对象数组或逐个参数创建 Group 加入场景并把对象归入组内。返回该组可用于统一变换与组织层级参见 Two.Group 文档const group two.makeGroup(rect, circle, star); group.rotation 0.01; // 组内所有对象一起旋转七、SVG 导入interpret 与 loadinterpret(svg, shallow?, add?)Interpret an SVG Node and add it to this instances scene. The distinction should be made that this doesntimportsvgs, it solely interprets them into something compatible for Two.js - this is slightly different than a direct transcription.svg要解析的 SVG 节点shallow不创建最顶层的组而是把所有内容直接追加add是否自动把重建后的 SVG 节点加入场景默认true返回Two.Group。实现要点src/two.js#L1163-L1183根据svg.tagName从Two.Utils.read中查找对应解析器如path、circle、rect等调用后获得 Two.js 对象shallow时若返回的是 Group 则直接取其children加入场景add为假时会把重建过程中临时加入场景的g标签移除以兼容getById类方法。const node document.querySelector(svg); const group two.interpret(node); // 自动加入场景load(pathOrSVGContent, callback?)pathOrSVGContentSVG 文件的 URL 路径或作为文本的 SVG 文档字符串callback加载完成后调用的函数返回Two.Group。实现src/two.js#L1193-L1226若传入以.svg结尾的路径走xhr(pathOrSVGContent, attach)异步加载否则直接把文本交给attach。attach内部把 SVG 文本放入临时 DOM 容器逐个interpret子元素并加入返回的 Group最后回调callback(group, svg)。这是将外部 SVG 资产转成 Two.js 可绘制对象的推荐入口const group two.load(/assets/scene.svg, (group, svg) { two.update(); // 加载完成后渲染一帧 });SVG 导入相关测试可参考 tests/suite/svg-interpreter.js 与 tests/suite/svg.js测试素材位于 tests/images/interpretation/含circle.svg、polygon.svg、compound-path.svg、linear-gradient.svg等典型元素。同时不要忘记上文提到的Two.AutoCalculateImportedMatrices开关true时 Two.js 会推断并覆盖参考 SVG 的精确变换矩阵false时原样拷贝矩阵但置matrix.manual true。八、动画循环与事件驱动的完整示例综合以上所有知识一个完整的「场景搭建 → 绘制 → 动画 → 命中检测」示例const two new Two({ width: 600, height: 400, type: Two.Types.canvas, }).appendTo(document.body); // 绘制对象 const rect two.makeRectangle(150, 150, 60, 60); rect.fill #2c6fbb; const circle two.makeCircle(300, 150, 40); circle.fill two.makeRadialGradient(300, 150, 60, new Two.Stop(0, #fff), new Two.Stop(1, #2c6fbb) ); const text two.makeText(Two.js, 450, 160, { size: 28, alignment: center }); // 监听 update 事件驱动动画autostart: true 时自动触发 two.bind(update, (frameCount, timeDelta) { rect.rotation 0.01; circle.scale 1 0.1 * Math.sin(frameCount * 0.05); }); // 命中检测点击坐标对应的图形 two.bind(click, (e) { const hits two.getShapesAtPoint(e.clientX, e.clientY, { mode: deepest, // 只取最顶层 tolerance: 4, // 允许 4px 容差 }); hits.forEach((shape) shape.opacity 0.5); }); two.play();核心事件驱动链总结play()→ 全局raf循环遍历Two.Instances中playing true的实例 → 调用update()触发event:update→ 内部render()触发event:renderframeCount递增→ 渲染器绘制。仓库测试 tests/suite/core.js 中即包含new Two().appendTo(elem)后调用two.play()并makeRectangle绘制旋转矩形的完整验证流程。九、进阶指引想深入场景树与变换阅读 Two.Group 与 Two.Matrix想定制路径与锚点阅读 Two.Path 与 Two.Anchor想理解三种渲染后端差异对比 renderers/svg、renderers/canvas、renderers/webgl 三份文档想查阅效果与图像能力参见 effects 系列文档核心实现与类型声明始终以 src/two.js 与 src/two.d.ts 为准测试验证见 tests/suite/。总而言之Two是理解整个 Two.js 架构的钥匙一个构造函数打通「渲染器选择、舞台尺寸、动画循环、场景根 Group」四大基础设施一套make*工厂方法覆盖全部基础图形、文本、渐变与图像能力再加上interpret/load/getShapesAtPoint/release等高层 API让开发者可以用几乎相同的心智模型同时驾驭 SVG、Canvas 与 WebGL 三种渲染环境。赞分享图形学前端【免费下载链接】two.jsA renderer agnostic two-dimensional drawing api for the web项目地址https://gitcode.com/gh_mirrors/tw/two.js点击查看免费下载相关推荐MonoGame核心框架模块详解从Game类到图形渲染MonoGame核心框架模块详解从Game类到图形渲染 本文深入解析MonoGame框架的核心架构涵盖Game类的游戏循环机制、图形设备管理、数学库与几何运游戏开发图形学Two.js Element 深度指南场景图基类 Two.Element 的渲染、序列化与资源释放Two.js Element 深度指南场景图基类 Two.Element 的渲染、序列化与资源释放 Two.js 是一个与渲染器无关renderer agn图形学前端Two.js Two.Path 完全指南掌控顶点、曲线与渲染的可绘制形状核心类Two.js Two.Path 完全指南掌控顶点、曲线与渲染的可绘制形状核心类 Two.js 是一个渲染器无关的二维绘图 API而 Two.Path 正是其图形学前端上一篇如何用Data Pipelines with Apache Airflow处理实时数据流实战案例分享下一篇深入解析 Redox OS 构建系统从源码到可启动镜像的完整流水线创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
阅读完成 · 觉得有帮助?