D3 - 平滑的径向图表过渡动画

时间:2017-09-08 07:38:47

标签: javascript animation d3.js

我已准备好D3径向图组件以显示某些目标的百分比值。当开始将前景圆从0绘制到chartPercentage(例如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>

2 个答案:

答案 0 :(得分:1)

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

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

答案 1 :(得分: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>