D3 v7 过渡(Transition)高级控制流详解:生命周期、interrupt、end 与事件监听 D3 v7 过渡Transition高级控制流详解生命周期、interrupt、end 与事件监听【免费下载链接】d3Bring data to life with SVG, Canvas and HTML. :bar_chart::chart_with_upwards_trend::tada:项目地址: https://gitcode.com/GitHub_Trending/d3/d3本文以 d3-transition 控制流文档 为主体系统讲解 D3 过渡从创建、调度、启动、插值到结束的完整生命周期以及selection.interrupt、interrupt、transition.end()、transition.on等高级控制流 API 的语义、边界与报错时机并辅以transition.each、transition.call等工具方法与仓库依赖结构的源码级佐证。读完后你将能够准确控制多个过渡之间的打断与同步、通过 Promise 等待过渡完成、注册start/end/interrupt/cancel四类过渡事件并理解 D3 为何要按帧批量执行 tween 以提升性能。一、先理解前提过渡是选择selection的“动画版”在 D3 中过渡是一种与 selection 类似、但用于把 DOM 从当前状态平滑插值到目标状态的接口典型入口是selection.transition()详见 元素选择文档 与 d3-transition 总览d3.select(body) .transition() .style(background-color, red);过渡创建后可以用 delay、duration、attr、style 等方法进行配置。本文聚焦的是“高级用法”——控制流control flow过渡何时开始、如何被打断、如何等待结束、事件何时触发。这也是官方文档 docs/d3-transition/control-flow.md 开篇的定位“For advanced usage, transitions provide methods for custom control flow.”二、过渡的一生The Life of a Transition这是控制流文档的核心章节理解它才能正确理解后面所有 API 的可用窗口。整个生命周期可以划分为五个阶段每个阶段对“还能改什么”有严格限制2.1 创建后配置窗口同步求值与延迟求值创建过渡之后例如通过selection.transition()或transition.transition()可以立即用delay、duration、attr、style等方法配置。这里有一个关键的求值时机区别指定目标值的方法如transition.attr同步求值——调用时立即评估需要起始值参与插值的方法如transition.attrTween、transition.styleTween必须推迟到过渡启动时才能求值因为起始值当前 DOM 状态要等到过渡开始才能确定。2.2 调度scheduled本帧末尾或下一帧过渡创建后不久——当前帧结束时或下一帧期间——会被调度scheduled。从这一刻起delay与start事件监听器不能再修改尝试修改会抛出错误消息为too late: already scheduled若过渡已经结束则抛出transition not found。2.3 启动start打断与取消发生在这里过渡随后启动时发生三件重要的事打断interrupt若同一元素上存在同名的活动过渡它会被打断并向已注册的监听器派发interrupt事件。取消cancel启动过渡会取消同一元素上在它之前创建的同名待处理pending过渡。派发start事件向监听器派发。两个容易踩坑的细节文档原文明确强调打断发生在“启动”时而不是“创建”时。因此即使是一个零延迟zero-delay的过渡也不会立刻打断活动中的旧过渡——旧过渡会被保留“最后一帧”。如果需要立即打断必须显式调用selection.interrupt()见下节。start事件派发后是最后一次可以修改过渡的时机运行中的过渡的 timing、tween 和监听器都不能再改强行修改会抛出too late: already running若已结束则为 transition not found。过渡在启动后立即初始化其 tweens。2.4 运行中按帧调用 tween且批量初始化过渡启动的那一帧但在所有启动于本帧的过渡都启动之后过渡第一次调用它的 tweens。这种批量batching初始化 tween的做法通常涉及读取 DOMD3 将其集中处理目的是避免 DOM 读与写交叉进行interleaved DOM reads and writes从而提升性能。在过渡活跃的每一帧它以经过 缓动eased 的t值0 到 1 之间调用 tweens在单帧内部tweens按注册顺序依次调用。2.5 结束end最终帧与配置销毁过渡结束时用**未经缓动non-eased**的t 1再调用一次 tweens保证终点值精确落地随后派发end事件。这是最后一次可以检查inspect过渡的时机结束之后过渡从元素上删除其配置被销毁在打断或取消时配置同样会被销毁在销毁之后再尝试检查该过渡会抛出transition not found。把三个报错信息串起来就是一张“操作窗口”检查表阶段还允许做什么越界操作的结果创建后、调度前配置 delay/duration/tween/listeners—已调度、未启动不能再改 delay 与start监听器抛 too late: already scheduled运行中不能再改 timing/tweens/listeners抛 too late: already running已销毁不能再检查过渡抛 transition not found三、selection.interrupt(name)显式打断活动过渡selection.interrupt(name)打断所选元素上指定name的活动过渡并取消指定name的待处理过渡如果有若不指定name则使用null。文档特别指出一个非递归语义打断某个元素上的过渡不会影响任何子孙元素上的过渡。这一点在组合组件中至关重要——例如 轴过渡axis transition 并不是单个过渡而是由轴g元素子孙元素上的多个相互独立但同步的过渡组成刻度线、刻度标签、域路径等。因此要打断一个轴过渡必须打断其子孙selection.selectAll(*).interrupt();其中通用选择器*会选中所有子孙元素。如果还想连g元素本身一起打断selection.interrupt().selectAll(*).interrupt();四、interrupt(node, name)面向单节点的底层版本interrupt(node, name)是面向单个 DOM 节点的等价操作打断指定节点上指定name的活动过渡并取消该name的待处理过渡如果有不指定name时使用null。它与selection.interrupt互为姊妹 API——前者作用于选择集合后者直接作用于单个节点适合在非 selection 上下文中例如原生 DOM 事件处理器里拿到this或event.currentTarget后使用。五、transition.end()用 Promise 等待过渡完成transition.end()返回一个Promise当所有被选中的元素都完成过渡时 resolve如果任一元素的过渡被取消cancel或打断interrupt该 Promise 会被 reject。这为“过渡完成后做后续动作”提供了基于 Promise 的现代写法例如在await之后再执行 DOM 移除、或触发下一个流程分支而不再必须依赖end事件回调。注意它与第二节的呼应由于 interrupt/cancel 会使 Promise reject用它串接业务流程时要显式catch或判断场景。六、transition.on(typenames, listener)四类过渡事件transition.on(typenames, listener)为每个被选中元素添加或移除指定事件typenames的listener。支持的事件类型共有四种start—— 过渡启动时end—— 过渡结束时interrupt—— 过渡被打断时cancel—— 过渡被取消时。需要特别强调文档原文明确指出这些不是原生 DOM 事件与selection.on/selection.dispatch实现的机制不同而是transition 事件由过渡机制本身派发。以下细节直接决定监听器的行为是否符合预期命名空间类型后可以可选地跟一个点号.和一个名字从而允许同一类型注册多个回调例如start.foo与start.bar多个 typenames用空格分隔如interrupt end或start.foo start.bar回调参数当过渡事件在某个选中节点上派发时listener 以该过渡元素为上下文调用接收当前数据d、当前索引i、当前分组nodesthis为当前 DOM 元素数据与索引的语义监听器总能拿到元素的最新 datum但索引是选择selection的属性在监听器被分配时就固定了——要更新索引需重新分配监听器替换与移除同一元素上若已为相同typename注册过监听器旧监听器会被移除后再加新的。移除单个监听器传null作为 listener移除某名字下的所有监听器传null且typename写.foofoo为该名字移除所有未命名监听器typename写.getter 形式不传 listener 时返回第一个非 null选中元素上该typename当前已分配的监听器如有指定多个 typenames 时返回第一个匹配的监听器。七、过渡上的控制流工具方法each / call / empty / nodes / node / size文档后半部分收录了过渡对象上的一组“直通选择”的控制流方法。它们与 d3-selection 同名方法 语义等价让你无需先transition.selection()拿到选择即可操作。transition.each(function)为每个选中元素调用指定函数传入当前数据d、索引i、分组nodesthis为当前 DOM 元素。可用于为每个元素执行任意代码特别适合构建同时访问父级与子级数据的上下文。等价于selection.each。transition.call(function, ...arguments)调用指定函数恰好一次传入本过渡以及任意可选参数并返回本过渡以便链式调用——即“手动调用函数的链式包装”。文档给出的可复用配色示例function color(transition, fill, stroke) { transition .style(fill, fill) .style(stroke, stroke); }于是可以这样使用d3.selectAll(div).transition().call(color, red, blue);它等价于手动展开color(d3.selectAll(div).transition(), red, blue);这种写法让“给一个过渡批量设置若干属性/样式”的公共逻辑可以封装成独立函数并复用等价于selection.call。empty / nodes / node / size 速查方法返回等价于transition.empty()过渡是否不含任何非 null元素布尔selection.emptytransition.nodes()过渡中所有非 null元素组成的数组selection.nodestransition.node()第一个非 null元素过渡为空时返回nullselection.nodetransition.size()过渡中元素总数selection.size这四个方法与生命周期检查直接相关例如在start监听器里用transition.node()拿到元素、用transition.nodes()做一次性批量处理都属于“start 之后仍是合法检查窗口”内的安全操作而一旦过渡结束过渡本身已被销毁只能操作节点而不能再操作过渡。八、结合本仓库依赖结构、版本与文档质量如何验证本仓库是 D3 的顶层发行包当前版本为7.9.0见 package.json。控制流文档所描述的d3-transitionAPI 在本仓库中的落地方式可以从三处确认依赖声明package.json 的dependencies中声明d3-transition: ^3.0.1说明本文所述行为对应 d3-transition 3.x 系列 APIyarn.lock 中将其解析为 d3-transition 3.0.1 的发布产物。统一再导出src/index.js 以export * from d3-transition;第 29 行将该模块的全部公共 API——包括interrupt、active、transition构造器及transition.on、transition.end等——并入 D3 顶层命名空间。因此d3.active(node, name)、d3.interrupt(node, name)等函数在import * as d3 from d3后均可直接调用。文档锚点校验test/docs-test.js 会递归爬取docs/下全部 Markdown收集标题与{#anchor}形式的显式锚点并校验所有内部链接指向的锚点真实存在documentation links point to existing internal anchors。这意味着 docs/d3-transition/control-flow.md 中如{#selection_interrupt}、{#transition_end}等锚点以及指向 timing、selecting、modifying 的链接在仓库测试层面是被持续保障的。从源码结构看本仓库并不内嵌 d3-transition 的实现源码——它作为独立 npm 包通过依赖引入文档中的 “Source” 指向该独立仓库的src/transition/*.js如end.js、on.js与src/selection/interrupt.js、src/interrupt.js。因此若需逐行研读控制流实现例如 tween 批量调度的具体帧循环、too late错误的抛出位置应以 d3-transition 包为准本仓库提供的是 API 契约、版本约束与文档锚点保障。九、适用前提与限制小结版本前提本文内容以本仓库 package.json 声明的d3-transition ^3.0.1与 D3 7.9.0 为准行为描述继承自 docs/d3-transition/control-flow.md适用于 d3-transition 3.x 文档所对应的过渡机制。事件语义限制start/end/interrupt/cancel是过渡事件而非原生 DOM 事件不能通过selection.on或selection.dispatch那套 DOM 事件机制触发或监听。修改窗口限制三个错误消息too late: already scheduled、too late: already running、transition not found划定了 delay、监听器、timing、tween 各自的可修改边界任何自动化脚本都应按此窗口安排调用时机。打断粒度限制interrupt不递归到子孙元素打断组合组件如轴的过渡时必须显式selectAll(*).interrupt()覆盖其子孙。end()的 reject 语义transition.end()在任一元素被取消或打断时 reject跨过渡编排时必须处理该分支。掌握上述生命周期时间线与各 API 的边界就能在 D3 中可靠地实现过渡的打断、同步、链式编排与 Promise 化收尾——这正是 d3-transition 控制流文档 所定义的“高级用法”的完整闭环。【免费下载链接】d3Bring data to life with SVG, Canvas and HTML. :bar_chart::chart_with_upwards_trend::tada:项目地址: https://gitcode.com/GitHub_Trending/d3/d3创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考