我有一个用两个弧建立的量规。外面的蓝色弧形表示整个可能的值范围;内部红色弧形表示当前值。我希望内部的红色弧线有一个漂亮的,褪色的尾巴。请参见JSFiddle

但是,我了解到渐变并不是真正的弧形,因此我遵循了bostock's Rainbow Circle block并设法获得了想要的渐变。

下一步是在显示新值时补间内弧。我知道如何在值(like this example)之间获得一条单一的过渡路径,但是在计算许多弧的过渡时,就像在提供的JSFiddle链接中所做的那样,我遇到了麻烦。

非常感谢您提供从何处开始以及如何考虑数学的任何建议。

Java脚本


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;
}

最佳答案

我遇到的主要问题是如何生成弧。最初,每次更新时我都会生成新的弧,然后无法正确退出并进入。解决方案更简单。相反,我应该只生成一次弧,然后像一个好的d3骑师一样更新其角度。我能够将Mike Bostock's Donut Transition示例改编为圆弧补间。谢谢Mike Bostock,他比我聪明。

有关工作示例,请参见下面的代码段。当然可以将其清除一点,但是可以!



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>

关于javascript - 补间许多弧,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/46103689/

10-10 01:22