我有一个标尺,我用两个弧形建造。外部蓝色弧代表整个可能的值范围;内红色弧代表当前值。我希望内部的红色弧形有一个漂亮的,褪色的尾巴。请参阅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;
}
答案 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>