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