【问题标题】:D3 - smooth radial chart transition animationD3 - 平滑的径向图表过渡动画
【发布时间】:2018-02-17 01:34:43
【问题描述】:

我准备了 D3 Radial Chart 组件来显示某个目标的百分比值。从 0 开始绘制前景圆到图表百分比(例如 70%)时添加平滑过渡效果会很棒。

问题是 - 如何使用下面附加的代码准备过渡/延迟/持续时间效果?

我还想实现的第二个想法是使用从 0 到 chartValue 的动画来计算圆内的值(径向内容)。如何准备这样的解决方案?

谢谢!

  const chartPercentage = 70;
  const chartValue = 1.1242  
  const radius = 75;
  const border = 7;
  const padding = 0;
  const width = 400;
  const height = 400;
  const twoPi = Math.PI * 2;
  const boxSize = (radius + padding) * 2;
  
  let svg;
  
  function setArc() {
    return d3.arc()
      .startAngle(0)
      .innerRadius(radius)
      .outerRadius(radius - border)
      .cornerRadius(50);
  }
  
	function draw() {
    svg = d3.select(".chart").append("svg")
      .attr('width', width)
      .attr('height', height);

    svg.append("foreignObject")
      .attr("width", boxSize)
      .attr("height", boxSize)
      .append("xhtml:div")
      .attr('class', 'radial-wrapper')
      .html(`<div class="radial-content">${chartValue}</div>`);

    const field = svg.append('g')
      .attr('transform', 'translate(' + boxSize / 2 + ',' + boxSize / 2 + ')');

    const meter = field.append('g')
      .attr('class', 'progress-meter');

    const background = meter.append("path")
      .datum({endAngle: twoPi})
      .attr('class', 'background')
      .attr('fill', '#2D2E2F')
      .attr('fill-opacity', 0.1)
      .attr("d", setArc());

    const foreground = meter.append("path")
      .datum({endAngle: (chartPercentage/100) * twoPi})
      .attr('class', 'foreground')
      .attr('fill', 'red')
      .attr('fill-opacity', 1)
      .attr('d', setArc());
  }
  
  draw();
  body { margin:30px;position:fixed;top:0;right:0;bottom:0;left:0; }
  .radial-wrapper{ display: flex; align-items: center; justify-content: center;width: 100%; height: 100%;}

  
<script src="https://d3js.org/d3.v4.min.js"></script>
<div class="chart"></div>

【问题讨论】:

    标签: javascript animation d3.js


    【解决方案1】:

    我重写了你的代码。当您需要为某些属性设置动画时,您应该使用attrTween 而不是attr 方法。

    const chartPercentage = 70;
    const chartValue = 1.1242
    const radius = 75;
    const border = 7;
    const padding = 0;
    const width = 400;
    const height = 400;
    const twoPi = Math.PI * 2;
    const boxSize = (radius + padding) * 2;
    
    let svg;
    
    const setArc = d3.arc()
      .startAngle(0)
      .innerRadius(radius)
      .outerRadius(radius - border)
      .cornerRadius(50);
    
    const arcParams = {};
    
    function draw() {
      svg = d3.select(".chart").append("svg")
        .attr('width', width)
        .attr('height', height);
    
      svg.append("foreignObject")
        .attr("width", boxSize)
        .attr("height", boxSize)
        .append("xhtml:div")
        .attr('class', 'radial-wrapper')
        .html(`<div class="radial-content"></div>`);
    
      const field = svg.append('g')
        .attr('transform', 'translate(' + boxSize / 2 + ',' + boxSize / 2 + ')');
    
      const meter = field.append('g')
        .attr('class', 'progress-meter');
    
      const background = meter
      	.append("path")
        .attr('class', 'background')
        .attr('fill', '#2D2E2F')
        .attr('fill-opacity', 0.1)
        .attr("d", setArc({ endAngle: twoPi }));
    
      const foreground = meter
      	.append("path")
        .transition()
        .ease(d3.easeBounce)
        .duration(1500)
        .attr('class', 'foreground')
        .attr('fill', 'red')
        .attr('fill-opacity', 1)
        .attrTween("d", function() {
          return arcTween({ endAngle: 0 }, chartPercentage/100 )
        })
    }
    
    function arcTween(d, new_score) {
        var new_startAngle = 0
        var new_endAngle = new_startAngle + new_score * 2 * Math.PI
        var interpolate_start = d3.interpolate(d.startAngle, new_startAngle)
        var interpolate_end = d3.interpolate(d.endAngle, new_endAngle)
        return function(t) {
          d.endAngle = interpolate_end(t)
          d3.select('.radial-content')
          	.text((d.endAngle / new_endAngle * chartValue).toFixed(4));
          return setArc(d)
        }
    }
    
    draw();
    body {
      margin: 30px;
      position: fixed;
      top: 0;
      right: 0;
      bottom: 0;
      left: 0;
    }
    
    .radial-wrapper {
      display: flex;
      align-items: center;
      justify-content: center;
      width: 100%;
      height: 100%;
    }
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <div class="chart"></div>

    【讨论】:

      【解决方案2】:

      为属性补间创建一个函数。

      function arcTween(a) {
        var j = {"endAngle":0};//start angle
        var i = d3.interpolateObject(j, a);
        return function(t) {
          d3.select(".radial-content").text(d3.format(".4n")(chartValue*t));
          return arc(i(t));
        };
      }
      

      在上述函数中

      d3.select(".radial-content").text(d3.format(".4n")(chartValue*t));
      

      这将在过渡运行时更改径向内容中的文本(并以格式输出)。

      现在将补间函数添加到前景路径。

        const foreground = meter.append("path")
          .datum({
            endAngle: (chartPercentage / 100) * twoPi
          })
          .attr('class', 'foreground')
          .attr('fill', 'red')
          .attr('fill-opacity', 1)
        .transition().duration(750).attrTween("d", arcTween);
      

      工作代码here

      【讨论】: