d3圆弧转换逆时针不顺时针
问题描述:
我正在看有两个动画圆环图,需要以某种方式为它们制作动画。d3圆弧转换逆时针不顺时针
如果你看的jsfiddle:
https://jsfiddle.net/5uc1xfxm/
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="robots" content="noindex, nofollow">
<meta name="googlebot" content="noindex, nofollow">
<script type="text/javascript" src="/js/lib/dummy.js"></script>
<link rel="stylesheet" type="text/css" href="/css/result-light.css">
<script type="text/javascript" src="https://d3js.org/d3.v4.min.js"></script>
<style type="text/css">
.animated-ring {
margin-left: auto;
margin-right: auto;
//margin-top: 50px;
width: 200px;
background-color: #fff;
}
</style>
<script type='text/javascript'>//<![CDATA[
window.onload=function(){
var tau = 2 * Math.PI; // http://tauday.com/tau-manifesto
// An arc function with all values bound except the endAngle. So, to compute an
// SVG path string for a given angle, we pass an object with an endAngle
// property to the `arc` function, and it will return the corresponding string.
var arc1 = d3.arc()
.innerRadius(45)
.outerRadius(90)
.startAngle(0.75 * tau);
var arc2 = d3.arc()
.innerRadius(45)
.outerRadius(90)
.startAngle(0.25 * tau);
// Get the SVG container, and apply a transform such that the origin is the
// center of the canvas. This way, we don’t need to position arcs individually.
var svg1 = d3.select("#anim1"),
width = +svg1.attr("width"),
height = +svg1.attr("height"),
g = svg1.append("g").attr("transform", "translate(" + width/2 + "," + height/2 + ")");
var svg2 = d3.select("#anim2"),
width = +svg2.attr("width"),
height = +svg2.attr("height"),
h = svg2.append("g").attr("transform", "translate(" + width/2 + "," + height/2 + ")");
// Add the background arc, from 0 to 100% (tau).
var background1 = g.append("path")
.datum({
endAngle: tau
})
.style("fill", "transparent")
.attr("d", arc1);
var background2 = h.append("path")
.datum({
endAngle: tau
})
.style("fill", "transparent")
.attr("d", arc2);
// Add the foreground arc in orange, currently showing 12.7%.
var foreground1 = g.append("path")
.datum({
endAngle: 0.75 * tau
})
.style("fill", "#EF4939")
.attr("d", arc1);
var foreground2 = h.append("path")
.datum({
endAngle: 0.25 * tau
})
.style("fill", "blue")
.attr("d", arc2);
// Every so often, start a transition to a new random angle. The attrTween
// definition is encapsulated in a separate function (a closure) below.
d3.timer(function() {
foreground1.transition()
.duration(75)
.attrTween("d", arcTween(0 * tau));
foreground2.transition()
.duration(75)
.attrTween("d", arcTween2(0.5 * tau));
});
// Returns a tween for a transition’s "d" attribute, transitioning any selected
// arcs from their current angle to the specified new angle.
function arcTween(newAngle) {
// The function passed to attrTween is invoked for each selected element when
// the transition starts, and for each element returns the interpolator to use
// over the course of transition. This function is thus responsible for
// determining the starting angle of the transition (which is pulled from the
// element’s bound datum, d.endAngle), and the ending angle (simply the
// newAngle argument to the enclosing function).
return function(d) {
// To interpolate between the two angles, we use the default d3.interpolate.
// (Internally, this maps to d3.interpolateNumber, since both of the
// arguments to d3.interpolate are numbers.) The returned function takes a
// single argument t and returns a number between the starting angle and the
// ending angle. When t = 0, it returns d.endAngle; when t = 1, it returns
// newAngle; and for 0 < t < 1 it returns an angle in-between.
var interpolate = d3.interpolate(d.endAngle, newAngle);
// The return value of the attrTween is also a function: the function that
// we want to run for each tick of the transition. Because we used
// attrTween("d"), the return value of this last function will be set to the
// "d" attribute at every tick. (It’s also possible to use transition.tween
// to run arbitrary code for every tick, say if you want to set multiple
// attributes from a single function.) The argument t ranges from 0, at the
// start of the transition, to 1, at the end.
return function(t) {
// Calculate the current arc angle based on the transition time, t. Since
// the t for the transition and the t for the interpolate both range from
// 0 to 1, we can pass t directly to the interpolator.
//
// Note that the interpolated angle is written into the element’s bound
// data object! This is important: it means that if the transition were
// interrupted, the data bound to the element would still be consistent
// with its appearance. Whenever we start a new arc transition, the
// correct starting angle can be inferred from the data.
d.endAngle = interpolate(t);
// Lastly, compute the arc path given the updated data! In effect, this
// transition uses data-space interpolation: the data is interpolated
// (that is, the end angle) rather than the path string itself.
// Interpolating the angles in polar coordinates, rather than the raw path
// string, produces valid intermediate arcs during the transition.
return arc1(d);
};
};
}
// Returns a tween for a transition’s "d" attribute, transitioning any selected
// arcs from their current angle to the specified new angle.
function arcTween2(newAngle) {
return function(d) {
var interpolate = d3.interpolate(d.endAngle, newAngle);
return function(t) {
d.endAngle = interpolate(t);
return arc2(d);
};
};
}
}//]]>
</script>
</head>
<body>
<div class="animated-ring">
<svg width="200" height="200" id="anim1"></svg>
<svg width="200" height="200" id="anim2"></svg>
</div>
<script>
// tell the embed parent frame the height of the content
if (window.parent && window.parent.parent){
window.parent.parent.postMessage(["resultsFrame", {
height: document.body.getBoundingClientRect().height,
slug: "5uc1xfxm"
}], "*")
}
</script>
</body>
</html>
你会看到两个动画。橙色图表按需要工作,但蓝色图表不是。
开始和结束位置正确,但不是顺时针方向移动,只填充圆的四分之一。我需要它逆时针移动并填充圆的四分之三。
任何想法?
答
假设我理解这个问题,
而不必从0.25头至0.5头圆弧过渡,尽量-0.5头:
.attrTween("d", arcTween2(-0.5 * tau));
这将向后移动电弧并导致其填写整个甜甜圈的3/4。这符合第一个甜圈的模式,该甜圈的行为正确,最终的终止角度小于起始角度。
更新小提琴here
和使用好措施代码片段(我已经改变了甜甜圈的顺序,所以你不需要快速滚动):
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<meta name="robots" content="noindex, nofollow">
<meta name="googlebot" content="noindex, nofollow">
<script type="text/javascript" src="/js/lib/dummy.js"></script>
<link rel="stylesheet" type="text/css" href="/css/result-light.css">
<script type="text/javascript" src="https://d3js.org/d3.v4.min.js"></script>
<style type="text/css">
.animated-ring {
margin-left: auto;
margin-right: auto;
//margin-top: 50px;
width: 200px;
background-color: #fff;
}
</style>
<script type='text/javascript'>//<![CDATA[
window.onload=function(){
var tau = 2 * Math.PI; // http://tauday.com/tau-manifesto
// An arc function with all values bound except the endAngle. So, to compute an
// SVG path string for a given angle, we pass an object with an endAngle
// property to the `arc` function, and it will return the corresponding string.
var arc1 = d3.arc()
.innerRadius(45)
.outerRadius(90)
.startAngle(0.75 * tau);
var arc2 = d3.arc()
.innerRadius(45)
.outerRadius(90)
.startAngle(0.25 * tau);
// Get the SVG container, and apply a transform such that the origin is the
// center of the canvas. This way, we don’t need to position arcs individually.
var svg1 = d3.select("#anim1"),
width = +svg1.attr("width"),
height = +svg1.attr("height"),
g = svg1.append("g").attr("transform", "translate(" + width/2 + "," + height/2 + ")");
var svg2 = d3.select("#anim2"),
width = +svg2.attr("width"),
height = +svg2.attr("height"),
h = svg2.append("g").attr("transform", "translate(" + width/2 + "," + height/2 + ")");
// Add the background arc, from 0 to 100% (tau).
var background1 = g.append("path")
.datum({
endAngle: tau
})
.style("fill", "transparent")
.attr("d", arc1);
var background2 = h.append("path")
.datum({
endAngle: tau
})
.style("fill", "transparent")
.attr("d", arc2);
// Add the foreground arc in orange, currently showing 12.7%.
var foreground1 = g.append("path")
.datum({
endAngle: 0.75 * tau
})
.style("fill", "#EF4939")
.attr("d", arc1);
var foreground2 = h.append("path")
.datum({
endAngle: 0.25 * tau
})
.style("fill", "blue")
.attr("d", arc2);
// Every so often, start a transition to a new random angle. The attrTween
// definition is encapsulated in a separate function (a closure) below.
d3.timer(function() {
foreground1.transition()
.duration(75)
.attrTween("d", arcTween(0 * tau));
foreground2.transition()
.duration(75)
.attrTween("d", arcTween2(-0.5 * tau));
});
// Returns a tween for a transition’s "d" attribute, transitioning any selected
// arcs from their current angle to the specified new angle.
function arcTween(newAngle) {
// The function passed to attrTween is invoked for each selected element when
// the transition starts, and for each element returns the interpolator to use
// over the course of transition. This function is thus responsible for
// determining the starting angle of the transition (which is pulled from the
// element’s bound datum, d.endAngle), and the ending angle (simply the
// newAngle argument to the enclosing function).
return function(d) {
// To interpolate between the two angles, we use the default d3.interpolate.
// (Internally, this maps to d3.interpolateNumber, since both of the
// arguments to d3.interpolate are numbers.) The returned function takes a
// single argument t and returns a number between the starting angle and the
// ending angle. When t = 0, it returns d.endAngle; when t = 1, it returns
// newAngle; and for 0 < t < 1 it returns an angle in-between.
var interpolate = d3.interpolate(d.endAngle, newAngle);
// The return value of the attrTween is also a function: the function that
// we want to run for each tick of the transition. Because we used
// attrTween("d"), the return value of this last function will be set to the
// "d" attribute at every tick. (It’s also possible to use transition.tween
// to run arbitrary code for every tick, say if you want to set multiple
// attributes from a single function.) The argument t ranges from 0, at the
// start of the transition, to 1, at the end.
return function(t) {
// Calculate the current arc angle based on the transition time, t. Since
// the t for the transition and the t for the interpolate both range from
// 0 to 1, we can pass t directly to the interpolator.
//
// Note that the interpolated angle is written into the element’s bound
// data object! This is important: it means that if the transition were
// interrupted, the data bound to the element would still be consistent
// with its appearance. Whenever we start a new arc transition, the
// correct starting angle can be inferred from the data.
d.endAngle = interpolate(t);
// Lastly, compute the arc path given the updated data! In effect, this
// transition uses data-space interpolation: the data is interpolated
// (that is, the end angle) rather than the path string itself.
// Interpolating the angles in polar coordinates, rather than the raw path
// string, produces valid intermediate arcs during the transition.
return arc1(d);
};
};
}
// Returns a tween for a transition’s "d" attribute, transitioning any selected
// arcs from their current angle to the specified new angle.
function arcTween2(newAngle) {
return function(d) {
var interpolate = d3.interpolate(d.endAngle, newAngle);
return function(t) {
d.endAngle = interpolate(t);
return arc2(d);
};
};
}
}//]]>
</script>
</head>
<body>
<div class="animated-ring">
<svg width="200" height="200" id="anim2"></svg>
<svg width="200" height="200" id="anim1"></svg>
</div>
<script>
// tell the embed parent frame the height of the content
if (window.parent && window.parent.parent){
window.parent.parent.postMessage(["resultsFrame", {
height: document.body.getBoundingClientRect().height,
slug: "5uc1xfxm"
}], "*")
}
</script>
</body>
</html>
谢谢,多数民众赞成你试图编码与发烧和生病的孩子,你失去了所有明显的逻辑。谢谢你帮助我的大脑 – Dominic