补间许多弧线

时间:2017-09-07 19:22:04

标签: javascript d3.js

我有一个标尺,我用两个弧形建造。外部蓝色弧代表整个可能的值范围;内红色弧代表当前值。我希望内部的红色弧形有一个漂亮的,褪色的尾巴。请参阅JSFiddle

然而,我了解到渐变不是真的,所以我跟着bostock's Rainbow Circle block并设法得到了我想要的褪色。

下一步是在显示新值时补间内弧。我理解如何在值(like this example)之间获得一个弧形路径来补间,但是在计算多个弧的补间时遇到了问题,比如什么我已经在提供的JSFiddle链接中完成了。

非常感谢任何关于从哪里开始以及如何思考数学的建议。

的Javascript

var minAngle = -90;
var maxAngle = 90;
var minValue = 0;
var maxValue = 100;
var n = 500;

var div = d3.select("#canvas");
var divBox = div.node().getBoundingClientRect();
var svg = d3.select("svg");
var width = divBox.width;
var height = divBox.height;
var g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");

// Generate a new random value between the min and max allowed, inclusive
function randomValue() {
  return Math.floor(Math.random() * (maxValue - minValue + 1)) + minValue;
}

// convert degrees to radians
function deg2rad(deg) {
  return deg * Math.PI / 180;
}

// convert a value to a degree
function val2rad(val) {
  return deg2rad(val2deg(val));
}

// scale for mapping a value to a degree
var val2deg = d3.scaleLinear()
  .range([minAngle, maxAngle])
  .domain([minValue, maxValue])
  .clamp(true);

// scale for mapping a degree to a color
var color = d3.scaleLinear()
  .domain([minAngle, maxAngle])
  .range(['#bbbbbb', 'tomato'])
  .clamp(true);

// the full arc range
var fullArc = d3.arc()
  .innerRadius(210)
  .outerRadius(220)
  .startAngle(val2rad(minValue))
  .endAngle(val2rad(maxValue));

var full = g.append("path")
  .style("fill", "dodgerblue")
  .attr("d", fullArc);

// Update the inner arc fade trail
function update(value) {

  // the inner red arc
  var trailArc = d3.arc()
    .innerRadius(170)
    .outerRadius(209)
    .startAngle(function(d) {
      return deg2rad(d);
    })
    .endAngle(function(d) {
      return deg2rad(d + (val2deg(value + 1)) / n * 1.1);
    });

  var step = (value + 1) / n;
  // minimum will always be 0
  var minDeg = val2deg(minValue);

  // maximum is current value = 1 for inclusive range
  var maxDeg = val2deg(value + 1);
  var angles = d3.range(minDeg, maxDeg, step);

    // update the color scale domain with the new set of angles
  color.domain([angles[0], angles[angles.length - 1]]);

    // draw the trail
  var trail = g.selectAll("path.trail")
    .data(angles)
    .enter()
    .append('path')
    .attr("class", "trail")
    .attr("d", trailArc)
    .style("fill", color)
    .style("stroke", color);
}

// initial draw
update(55);

HTML

<div id="canvas">
  <svg width="100%" height="100%"></svg>
</div>

CSS

body {
  background-color: #bbbbbb;
}
#canvas {
  width: 960px;
  height: 500px;
  margin: auto;
}

1 个答案:

答案 0 :(得分:0)

我遇到的主要问题是我是如何产生弧线的。最初,每次有更新时我都会生成新弧,然后无法退出并正确输入。解决方案更简单。相反,我应该生成弧一次,然后像一个好的d3骑师更新他们的角度。我能够使Mike Bostock's Donut Transition示例适应圆弧补间。谢谢你,迈克博斯托克,比我聪明。

有关工作示例,请参阅下面的代码段。它当然可以清理一下,但它有效!

const minDeg = -90;
const maxDeg = 90;
const minValue = 0;
const maxValue = 100;
const currentValue = 40;
const n = 500;
const tailLength = 30;

const div = d3.select("#canvas");
const divBox = div.node().getBoundingClientRect();
const svg = d3.select("svg");
const width = divBox.width;
const height = divBox.height;
const g = svg.append("g").attr("transform", `translate(${width / 2}, ${height / 2})`);

const randomValue = () => Math.floor(Math.random() * (maxValue - minValue + 1)) + minValue;

const deg2rad = deg => deg * Math.PI / 180;

const val2rad = val => deg2rad(val2deg(val));

const val2deg = d3.scaleLinear()
  .range([minDeg, maxDeg])
  .domain([minValue, maxValue])
  .clamp(true);

const color = d3.scaleLinear()
  .domain([0, n])
  .range(['#bbbbbb', 'tomato'])
  .clamp(true);

const fullArc = d3.arc()
  .innerRadius(210)
  .outerRadius(220)
  .startAngle(val2rad(minValue))
  .endAngle(val2rad(maxValue));

g.append("path")
  .style("fill", "dodgerblue")
  .attr("d", fullArc);

const tailArcs = d3.range(n).map(i => ({
  i,
  color: color(i),
  startAngle: deg2rad(minDeg),
  endAngle: deg2rad(minDeg)
}));

const tailArc = d3.arc()
  .innerRadius(169)
  .outerRadius(210)
  .startAngle(d => d.startAngle)
  .endAngle(d => d.endAngle);

const tails = g.selectAll("path.tail")
  .data(tailArcs)
  .enter()
  .append('path')
  .attr("class", "tail")
  .attr("d", tailArc)
  .style("fill", d => d.color)
  .style("stroke", d => d.i === n - 1 ? 'none' : d.color);

const update = value => {
  const maxDeg = val2deg(value);
  const minDeg = val2deg(value - tailLength);
  const step = (maxDeg - minDeg) / n;

  tails.data(tailArcs)
    .transition().duration(500)
    .attrTween('d', tweenArc(d => {
      const startAngle = minDeg + (d.i * step);
      return {
        startAngle: deg2rad(startAngle),
        endAngle: deg2rad(startAngle + step)
      }
    }));
}

const tweenArc = getNextAngles => a => {
  const d = getNextAngles.call(this, a);
  const i = d3.interpolate(a, d);
  for (let k in d) {
    a[k] = d[k];
  }
  return t => tailArc(i(t));
};

update(currentValue);

d3.interval(() => update(randomValue()), 1000);
body {
  background-color: #bbbbbb;
}

#canvas {
  width: 600px;
  height: 500px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.2/d3.js"></script>
<div id="canvas">
  <svg width="100%" height="100%"></svg>
</div>