通过使用SVG和d3展开圆圈将其转换为直线

时间:2013-04-23 15:46:02

标签: svg d3.js

对于一个项目,我们试图在沿着直线路径旋转时将圆形成一条线(并且再次返回),就像轮胎在道路上滚动时旋转和平移,或者卷曲的前指延长并重新投入掌中。

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

我还使用d3生成一个由连接点组成的“圆圈”(如果你点击/圈中的@krisJamesC 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中同时申请?

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

这些似乎更好:

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

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

  • 定义点跟随的路径(可能是具有相同参考点数的bézier曲线),并使参考点动态转换...请参阅this以获取概念示例

这看起来很复杂而且很笨重:

  • 制作一个真正的圆圈,在它前面有一个不断增长的面具,一条线长度增长

几点说明:

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

2 个答案:

答案 0 :(得分: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)            
     }
}

答案 1 :(得分:0)

(注意:这应该在评论中,但间距不够)

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

  2. 线条动画从单点到圆的长度(周长)生成一条线。

  3. 同步这两个动画,使其在所有浏览器中都显示出来(主要是头痛!)。