【问题标题】:transition a circle into a line by unrolling it with SVG and d3通过使用 SVG 和 d3 展开来将圆转换为线
【发布时间】:2013-04-16 22:13:56
【问题描述】:

对于一个项目,我们试图在它沿直线路径旋转时将一个圆变成一条线(然后再返回),就像轮胎在道路上滚动时旋转和平移,或者伸出卷曲的食指又卷入掌心。

在这个Fiddle 中,我有一个静态 SVG(顶部圆圈),它沿着 HTML 中定义的线性黑色路径(在圆圈上方,模仿手指伸展)旋转。

我还使用 d3 生成一个由连接点组成的“圆”(如果您单击/在圆中可以展开,感谢@ChrisJamesC here ),并被平移和旋转 在函数 moveAlongLine 当你点击紫色线时:

function moveAlongLine() {
  circle.data([lineData])
  .attr("transform", "translate(78.5,0) rotate(-90, 257.08 70) ")
  .duration(1000)
  circle.on("click", transitionToCircle)
}

第一个问题是.duration(1000)不被识别并在控制台中抛出一个Uncaught TypeError: Object [object Array] has no method 'duration',所以在SVG中静态定义dur和在JS/D3中动态设置是有区别的,但是这是次要的。

另一个是转换属性是否应该像在静态循环中那样相互抽象?在静态圆中,平移是一个动画,旋转是另一个,它们只是具有相同的星号和持续时间,因此它们一起动画。您将如何在 d3 中同时应用这两者?

我无法得到的挑战是如何让它向上展开(并重新回滚),静态点是圆的顶部中心,也与直线上最左边的点相同。

这些看起来更好:

  • 我应该尝试让展开动画同时旋转吗?这似乎需要逐步/基于顺序...
  • 或者考虑一个八边形(定义为一条路径),如果它要旋转 7 个边,然后旋转 6 个,然后是 5.... 对多面体上的大量点执行此操作? (圆圈只需要大约 50 个像素左右,所以 100 个点就足够了)这是小提琴中的中间示例。也许以编程方式执行此操作?
  • 或者这让我想到了一种不同的方式:(在八角形的情况下),我可以有 8 条线路径(没有 Z,只是一个额外的闭合点),并在它们之间进行过渡?喜欢这个fiddle
  • 或者与关键帧有关的任何事情?我在 Synfig 中制作了动画,但不确定是否可以将其用于 SVG。如果您可以转换为 SVG,则 synfig 文件位于 http://specialorange.org/filedrop/unroll.sifz,但 xsl 文件 here 无法使用 xsltproc 为我正确转换。

这看起来很复杂但很有潜力:

  • 定义点遵循的路径(可能是具有相同数量参考点的贝塞尔曲线),并让参考点动态平移...有关概念示例,请参阅 this

这看起来既复杂又笨重:

  • 做一个真正的圆圈滚动,前面有一个不断增长的蒙版,同时一条线的长度也在增长

几点说明:

  • d3 圆中的点数可以在 JS 中调整,目前设置得很低,以便您可以在渲染中看到一点点来验证是否发生了旋转(很像渐变在顶圈)。
  • 这是为了帮助学生学习在数轴和圆之间守恒的内容,特别是为了帮助学习分数。对于概念应用,请查看compthink.cs.vt.edu:3000 以查看我们的原型,这将有助于切换表示,帮助您获得更好的想法...

【问题讨论】:

    标签: svg d3.js


    【解决方案1】:

    我最终使用了与问题中生成圆圈相同的函数,并进行了一些思考,似乎我想要一个看起来像手指展开的动画fiddle。这让我想到了在fiddle 中实现它所需的数学和想法。

    答案是一个数组数组,每个嵌套数组是处于不同状态的一条线,然后通过在点之间进行插值来制作动画。

    var circleStates = [];
    for (i=0; i<totalPoints; i++){
        //circle portion
        var circleState = $.map(Array(numberOfPoints), function (d, j) {
          var x = marginleft + radius + lineDivision*i + radius * Math.sin(2 * j * Math.PI / (numberOfPoints - 1));
          var y =  margintop + radius - radius * Math.cos(2 * j * Math.PI / (numberOfPoints - 1));
          return { x: x, y: y};
        })
        circleState.splice(numberOfPoints-i);
        //line portion
        var lineState = $.map(Array(numberOfPoints), function (d, j) {
          var x = marginleft + radius + lineDivision*j;
          var y =  margintop;
          return { x: x, y: y};
        })
        lineState.splice(i);
        //together
        var individualState = lineState.concat(circleState);
        circleStates.push(individualState);
    }
    

    和动画

    function all() {
        for(i=0; i<numberOfPoints; i++){
          circle.data([circleStates[i]])
            .transition()
            .delay(dur*i)
            .duration(dur)
            .ease("linear")
            .attr('d', pathFunction)            
        }
    }
    function reverse() {
        for(i=0; i<numberOfPoints; i++){
          circle.data([circleStates[numberOfPoints-1-i]])
            .transition()
            .delay(dur*i)
            .duration(dur)
            .ease("linear")
            .attr('d', pathFunction)            
         }
    }
    

    【讨论】:

    • 太好了!我认为您也可以始终将相同的数组与某些 push-pop-map 函数一起使用,但这可能会使事情变得更难理解并且比您实际执行的操作更慢。 (ChrisJamecC)
    【解决方案2】:

    (注意:这应该是cmets但没有足够的间距)

    1. 圆形动画 尝试来自 SO 的radial wipe。需要调整它,使角度从 180 开始并在同一位置结束(线#4-6,19)并在每次交互时沿 X 轴(线#11)移动。更改&lt;path... 属性以适合您的口味。

    2. 线条动画 将线条从单点延伸到圆的长度(周长)。

    3. 同步两个动画,使其在所有浏览器上看起来都很好(非常头疼!)。

    【讨论】:

    • 更新:对于1.,你可以用&lt;g transform="translate(200,200) rotate(180)"&gt;包裹路径。因此,您只需在第 11 行中添加与第 2 行中的 x 值匹配的 x 轴偏移量。
    • 所以你是说有一个随着圆圈旋转的面具?我们的好处是只需要为 chrome 开发,因为它是唯一支持网络音频套件的浏览器
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-12-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-16
    • 1970-01-01
    相关资源
    最近更新 更多