如何在D3元素过渡过程中获取其中间属性值?SVG游戏开发求助
解决D3过渡中无法读取元素属性的问题
你遇到的这个"too late; already running"错误,是因为D3的transition在运行期间会接管目标元素的属性访问,此时直接用attr()同步读取属性会被阻止——这是D3过渡机制的设计,避免在动画运行时出现属性冲突。
解决思路:实时跟踪过渡中的属性值
与其在点击时尝试读取被过渡锁定的属性,不如在过渡过程中主动记录当前的cy值,这样点击时直接取用这个记录值即可。具体做法是用D3的tween函数,在每一帧动画更新时同步更新一个变量,保存当前的cy坐标。
修改后的完整代码
<div id="canvas"></div> <button id="catchMe"> Catch Me </button> <span id="msg"></span> <script src="https://d3js.org/d3.v5.min.js"></script> <script> const width = 200, height = 300, radius = 10; const svg = d3.select('#canvas') .append('svg') .attr('width', width) .attr('height', height); const catchCircle = svg.append('circle') .attr('r', radius) .attr('cx', width / 2) .attr('cy', 100) .attr('stroke', 'red') .attr('stroke-width', 3) .attr('fill', 'none'); // 初始化猎物圆的cy值 let currentPreyCy = height - radius; const preyCircle = svg.append('circle') .attr('r', radius) .attr('cx', width / 2) .attr('cy', currentPreyCy) .attr('fill', 'red') .transition() .duration(5000) .delay(1000) // 使用tween自定义动画,同时跟踪cy值 .tween('cy', function() { // 创建从初始cy到目标cy的插值器 const interpolateCy = d3.interpolate(height - radius, radius); return function(t) { // t是0到1的进度值,计算当前cy并更新变量 currentPreyCy = interpolateCy(t); // 更新元素的cy属性 d3.select(this).attr('cy', currentPreyCy); }; }) .remove(); d3.select('#catchMe') .on('click', () => { // 获取捕捉圆的cy值并转为数值类型 const catchCy = +catchCircle.attr('cy'); // 计算两圆cy的距离,判断是否重叠 const distance = Math.abs(currentPreyCy - catchCy); const catched = distance < radius * 2; const msgSpan = d3.select('#msg'); msgSpan.node().textContent = catched ? "Perfect! :)" : "Oh no! :("; }); </script>
关键细节说明
tween函数的作用:它允许我们自定义动画的每一帧行为,这里我们用d3.interpolate创建了初始值到目标值的线性插值器,在每一帧计算出当前的cy值,同时更新全局变量currentPreyCy。- 避免类型问题:注意把
catchCircle.attr('cy')转换为数值类型(用+前缀),因为D3的attr()返回的是字符串,直接计算会导致错误。 - 重叠判断逻辑:两圆重叠的条件是圆心距离小于等于半径之和,这里我们用
< radius * 2来判断(因为两个圆的半径都是10,半径之和是20)。
这样修改后,点击按钮时就能准确获取猎物圆的实时位置,不会再触发报错,游戏的核心逻辑也能正常运行了。
内容的提问来源于stack exchange,提问作者Abhishek Mukherjee
相关产品推荐
相关产品推荐

