【问题标题】:Randomize and animate multiple Start and End Angles of Arc随机化和动画多个弧的开始和结束角度
【发布时间】:2017-01-20 06:14:07
【问题描述】:

我正在尝试创建一个对Arc Tween 演示进行修改的视觉效果。在其中,我想要一个数据数组来定义每个弧的颜色和一般半径,并且在一个间隔内,开始和结束角度都应该缓慢地动画。但我认为我定义每条弧线的方式会导致事物过度动画化。

HTML

<!DOCTYPE html>

<html>
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width">
  <script src="https://d3js.org/d3.v4.min.js"></script>
  <title>Arcs</title>
</head>
<body>
  <svg width="960" height="500"></svg>
</body>
</html>

脚本

// Modified from Arc Tween
// https://bl.ocks.org/mbostock/5100636

var tau = 2 * Math.PI; // http://tauday.com/tau-manifesto
var overlap = 50

var jsonArcs = [
  { "base_radius": 370, "color" : "red"},
  { "base_radius": 330, "color" : "orange"},
  { "base_radius": 290, "color" : "yellow"},
  { "base_radius": 250, "color" : "green"},
  { "base_radius": 210, "color" : "blue" },
  { "base_radius": 170, "color" : "purple"},
  { "base_radius": 130, "color" : "black"},
  { "base_radius": 90, "color" : "red"}
];


var arc = d3.arc()
    .startAngle(function(d) { return Math.random() * tau; })
    .endAngle(function(d) { return Math.random() * tau; })
    .innerRadius(function(d) { return d.base_radius - overlap * Math.random(); })
    .outerRadius(function(d) { return d.base_radius + overlap * Math.random(); });

var center_def = d3.arc()
    .innerRadius(0)
    .outerRadius(60)
    .startAngle(0);


var svg = d3.select("svg"),
    width = +svg.attr("width"),
    height = +svg.attr("height"),
    g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

  var path = g.selectAll("path")
      .data(jsonArcs) 
    .enter().append("path")
      .attr("fill", function(d, i) { return d.color; })
      .attr("d", arc);


var center = g.append("path")
    .datum({endAngle: tau})
    .style("fill", "black")
    .attr("d", center_def);


d3.interval(function() {

  path.transition()
    .duration(750)
    .attrTween("d", arcTween(Math.random() * tau, arc));
}, 2500);

function arcTween(newAngle, obj) {

  return function(d) {

    var interpolate = d3.interpolate(d.endAngle, newAngle);

    return function(t) {

      d.endAngle = interpolate(t);

      return obj(d);
    };
  };
}

不是每条弧线都从起始角度平滑动画到新角度,而是整个视觉多次跳转到新状态。

如何配置此功能,以使每条圆弧平滑地将其起点和终点角度从旧角度过渡到新角度?

【问题讨论】:

标签: animation d3.js tween


【解决方案1】:

这里有几个问题

  1. 您的 arc 函数将在每次调用时返回随机半径,我认为这不是您想要的。您可以将弧从一个内/外半径过渡到下一个,但为简单起见,假设每条路径最初都只有一个随机半径

  2. 为了从一对旧的开始/结束角度转换到新的角度,您需要将当前角度存储在某处。我们会将其存储在local variable 中,该local variable 将绑定到每个路径

  3. 因为每条路径都有不同的内/外半径,所以我们也需要为每个路段设置不同的弧函数。

这里的工作代码:

    var tau = 2 * Math.PI; // http://tauday.com/tau-manifesto
    var overlap = 50;
    var currentSegment = d3.local();
    var segmentRadius = d3.local();

    var jsonArcs = [
      { "base_radius": 370, "color" : "red"},
      { "base_radius": 330, "color" : "orange"},
      { "base_radius": 290, "color" : "yellow"},
      { "base_radius": 250, "color" : "green"},
      { "base_radius": 210, "color" : "blue" },
      { "base_radius": 170, "color" : "purple"},
      { "base_radius": 130, "color" : "black"},
      { "base_radius": 90, "color" : "red"}
    ];

    var arc = d3.arc()
        .innerRadius(function() { return segmentRadius.get(this).innerRadius })
        .outerRadius(function() { return segmentRadius.get(this).outerRadius });

    var center_def = d3.arc()
        .innerRadius(0)
        .outerRadius(60)
        .startAngle(0);

    var svg = d3.select("svg"),
        width = +svg.attr("width"),
        height = +svg.attr("height"),
        g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

    var path = g.selectAll("path")
        .data(jsonArcs)
        .enter().append("path")
        .attr("fill", function(d, i) { return d.color; })
        .each(function(d) {
          var angles = randomAngles();
          d.startAngle = angles.startAngle;
          d.endAngle = angles.endAngle;
          segmentRadius.set(this, {
            innerRadius: d.base_radius - overlap * Math.random(),
            outerRadius: d.base_radius + overlap * Math.random()
          });
        })
        .attr("d", arc)
        .each(function(d) {currentSegment.set(this, d)});

    var center = g.append("path")
        .datum({endAngle: tau})
        .style("fill", "black")
        .attr("d", center_def);

    d3.interval(function() {
      path.transition()
          .duration(750)
          .attrTween("d", arcTween);
    }, 2500);

    function arcTween() {
      var thisPath = this;
      var interpolate = d3.interpolate(currentSegment.get(this), randomAngles());
      currentSegment.set(this, interpolate(0));

      return function(t) {
        return arc.call(thisPath, interpolate(t));
      };
    }
    function randomAngles() {
      var angles = [Math.random() * tau, Math.random() * tau].sort();
      return {startAngle: angles[0], endAngle: angles[1]};
    }

请注意更改代码的一些注意事项:

  1. 在设置“d”属性之前,我在路径上的每次调用中设置了随机初始角度
  2. 我将段半径存储在同一链末尾的 segmentRadius d3.local 变量中,并且在对转换的调用中设置了每个插值之后
  3. 在转换函数中,我需要调用 arc 来保留路径的“this”,以便在检索 segmentRadius 时,“this”在 arc.innerRadius 中是正确的。
  4. d3.interpolate 可以愉快地处理对象,而不仅仅是数字。

如果您想了解更多信息,我有一个类似的示例,我一直在研究 here

【讨论】:

  • 谢谢!这很有意义。我一直在为每次调用的半径随机化而苦苦挣扎,但我没想过存储以前的状态。我对“每个”也不够熟悉
猜你喜欢
  • 2011-10-08
  • 2023-03-22
  • 1970-01-01
  • 1970-01-01
  • 2016-05-18
  • 1970-01-01
  • 2012-12-02
  • 1970-01-01
  • 2015-08-23
相关资源
最近更新 更多