向 d3.js 个动画气泡图添加轨迹
Adding traces to d3.js animated bubble chart
我正在尝试构建一个动画时间序列图表,该图表在移动点之后显示 'trace' 或蜗牛轨迹。我一直在尝试集成 KoGor 的 http://bl.ocks.org/KoGor/8163022 但运气不佳 - 我认为问题出在 tweenDash() - 原始功能是为单个跟踪设计的 - 每个公司都有一个。
下面附上了一个工作示例——时间序列清理和可移动数据标签有效,只是跟踪方面无效。
谢谢,
RL
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.10/d3.min.js"></script>
<!DOCTYPE html>
<meta charset="utf-8">
<body bgcolor="#000000">
<title>BPS</title>
<style>
@import url(style.css);
#chart {
margin-left: -40px;
height: 506px;
display:inline;
}
#buffer {
width: 100px;
height:506px;
float:left;
}
text {
font: 10px sans-serif;
color: #ffffff;
}
.dot {
stroke: #000;
}
.axis path, .axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.label {
fill: #777;
}
.year.label {
font: 900 125px "Helvetica Neue";
fill: #ddd;
}
.year.label.active {
fill: #aaa;
}
.overlay {
fill: none;
pointer-events: all;
cursor: ew-resize;
}
</style>
<div>
<div id="buffer"></div><div id="chart"></div>
</div>
<script src="d3.v3.min.js"></script>
<script>
var source = '[{"name":"ABCD","AUM":[[2010,1000.6],[2011,1200.6],[2012,1300.1],[2013,1400.5],[2014,1600.0]],"AUA":[[2010,3000.6],[2011,3300.2],[2012,4000.0],[2013,4500.8],[2014,6000.3]],"marketPercentage":[[2010,40.4],[2011,39.7],[2012,38.5],[2013,37.1],[2014,36.5]],"fill":[[2010,0],[2011,-1],[2012,-1],[2013,-1],[2014,-1]],"xOffset":[[2010,5],[2011,5],[2012,5],[2013,5],[2014,5]],"yOffset":[[2010,-30],[2011,-20],[2012,-20],[2013,-20],[2014,-10]]},{"name":"EFGH","AUM":[[2010,32.8],[2011,43.2],[2012,58.3],[2013,78.8],[2014,92]],"AUA":[[2010,327.3],[2011,439.3],[2012,547.0],[2013,710.0],[2014,824.0]],"marketPercentage":[[2010,1.0],[2011,1.2],[2012,1.5],[2013,1.8],[2014,1.9]],"fill":[[2010,0],[2011,1],[2012,1],[2013,1],[2014,1]],"xOffset":[[2010,5],[2011,5],[2012,5],[2013,5],[2014,5]],"yOffset":[[2010,-10],[2011,-10],[2012,-10],[2013,-10],[2014,-10]]},{"name":"HIJK","AUM":[[2010,0.1],[2011,0.5],[2012,1.2],[2013,2.4],[2014,2.6]],"AUA":[[2010,159.6],[2011,176.7],[2012,199.9],[2013,235.1],[2014,269.0]],"marketPercentage":[[2010,0.1],[2011,0.1],[2012,0.1],[2013,0.1],[2014,0.1]],"fill":[[2010,0],[2011,0],[2012,0],[2013,1],[2014,1]],"xOffset":[[2010,5],[2011,5],[2012,5],[2013,5],[2014,5]],"yOffset":[[2010,-10],[2011,-10],[2012,-10],[2013,-10],[2014,-10]]}]';
// Various accessors that specify the four dimensions of data to visualize.
function x(d) { return d.AUM; }
function y(d) { return d.AUA; }
function xo(d) {return d.xOffset; }
function yo(d) {return d.yOffset; }
function radius(d) { return d.marketPercentage; }
function key(d) { return d.name; }
// Chart dimensions.
var margin = {top: 19.5, right: 19.5, bottom: 19.5, left: 39.5},
width = 960 - margin.right,
height = 500 - margin.top - margin.bottom;
// Various scales. These domains make assumptions of data, naturally.
var xScale = d3.scale.linear().domain([0, 2000]).range([0, width]),
yScale = d3.scale.linear().domain([0, 5000]).range([height, 0]),
radiusScale = d3.scale.sqrt().domain([0, 500]).range([0, 40]),
colorScale = d3.scale.category10();
// The x & y axes.
var xAxis = d3.svg.axis().orient("bottom").scale(xScale).ticks(12, d3.format(",d")),
yAxis = d3.svg.axis().scale(yScale).orient("left");
// Create the SVG container and set the origin.
var svg = d3.select("#chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Add the x-axis.
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.style("fill", "#FFFFFF")
.call(xAxis);
// Add the y-axis.
svg.append("g")
.attr("class", "y axis")
.style("fill", "#FFFFFF")
.call(yAxis);
// Add an x-axis label.
svg.append("text")
.attr("class", "x label")
.attr("text-anchor", "end")
.style("fill", "#FFFFFF")
.attr("x", width)
.attr("y", height - 6);
//.text("income per capita, inflation-adjusted (dollars)");
// Add a y-axis label.
svg.append("text")
.attr("class", "y label")
.attr("text-anchor", "end")
.attr("y", 6)
.attr("dy", ".75em")
.style("fill", "#FFFFFF")
.attr("transform", "rotate(-90)")
// .text("life expectancy (years)")
;
// Add the year label; the value is set on transition.
var label = svg.append("text")
.attr("class", "year label")
.attr("text-anchor", "end")
.attr("y", height - 24)
.attr("x", width)
.text(2010);
//d3.json("investments_v04ANON.json", function(companies) {
companies = JSON.parse(source)
// A bisector since many company's data is sparsely-defined.
var bisect = d3.bisector(function(d) { return d[0]; });
// Add a dot per company. Initialize the data at 2010, and set the colors.
var dot = svg.append("g")
.attr("class", "dots")
.selectAll(".dot")
.data(interpolateData(2010))
.enter().append("circle")
.attr("class", "dot")
// .style("fill", function(d) { return colorScale(color(d)); })
.style("fill", function(d) {return colorScale(interpolateData(2010)) })
.call(position)
.sort(order);
var lineTraces = svg.append("path")
.attr("class", "lineTrace")
.selectAll(".traces")
.attr("stroke-width", 2)
.attr("stroke", "grey")
.data(interpolateData(2010));
//yields a mouseover label - "title" precludes need for separate mouseover event.
// dot.append("title")
// .text(function(d) { return d.name; });
//.text(function(d) {return d.AUM});
var theLabel = svg.append("g")
.attr("class", "texts")
.selectAll(".theLabel")
.data(interpolateData(2010))
.enter().append("text")
.attr("class", "text")
.text("hey")
.call(position2);
// Add an overlay for the year label.
var box = label.node().getBBox();
var overlay = svg.append("rect")
.attr("class", "overlay")
.attr("x", box.x)
.attr("y", box.y)
.attr("width", box.width)
.attr("height", box.height)
.on("mouseover", enableInteraction);
// Start a transition that interpolates the data based on year.
svg.transition()
.duration(30000)
.ease("linear")
.tween("year", tweenYear)
.attrTween("stroke-dasharray", tweenDash)
.each("end", enableInteraction);
// Positions the dots based on data.
function position(dot) {
dot .attr("cx", function(d) { return xScale(x(d)); })
.attr("cy", function(d) { return yScale(y(d)); })
.attr("r", function(d) { return radiusScale(radius(d)); })
.style("fill", function(d) {return d.fill>0 ? "green" : "red"} );//{return d.fill});
}
//function from: http://bl.ocks.org/KoGor/8163022
function tweenDash() {
var i = d3.interpolateString("0," + 5, 5 + "," + 5); // interpolation of stroke-dasharray style attr
// var l = path.node().getTotalLength();
// var i = d3.interpolateString("0," + l, l + "," + l); // interpolation of stroke-dasharray style attr
return function(t) {
var marker = d3.select(".dots");
// var p = path.node().getPointAtLength(t * l);
var p = lineTraces.node().getPointAtLength(t * 5);
marker.attr("transform", "translate(" + p.x + "," + p.y + ")");//move marker
return i(t);
}
}
function position2(theLabel) {
theLabel.attr("x", function(d) { return xScale(x(d)) + xo(d); })
.attr("y", function(d) { return yScale(y(d)) + yo(d); })
.attr("text-anchor", "end")
.style("fill", "#FFFFFF")
.text(function(d) { return d.name + ": AUM:" + Math.round(d.AUM) + ", AUA: " + Math.round(d.AUA) });//{return d.fill});
}
// Defines a sort order so that the smallest dots are drawn on top.
function order(a, b) {
return radius(b) - radius(a);
}
// After the transition finishes, you can mouseover to change the year.
function enableInteraction() {
var yearScale = d3.scale.linear()
.domain([2010, 2014])
.range([box.x + 10, box.x + box.width - 10])
.clamp(true);
// Cancel the current transition, if any.
svg.transition().duration(0);
overlay
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.on("mousemove", mousemove)
.on("touchmove", mousemove);
function mouseover() {
label.classed("active", true);
}
function mouseout() {
label.classed("active", true);
label.classed("active", false);
}
function mousemove() {
displayYear(yearScale.invert(d3.mouse(this)[0]));
}
}
// Tweens the entire chart by first tweening the year, and then the data.
// For the interpolated data, the dots and label are redrawn.
function tweenYear() {
var year = d3.interpolateNumber(2010, 2014);
return function(t) { displayYear(year(t)); };
}
// Updates the display to show the specified year.
function displayYear(year) {
dot.data(interpolateData(year), key).call(position).sort(order);
theLabel.data(interpolateData(year), key).call(position2).sort(order);
label.text(Math.round(year));
}
// Interpolates the dataset for the given (fractional) year.
function interpolateData(year) {
return companies.map(function(d) {
return {
// name: d.name + ": AUM:" + interpolateValues(d.AUM, year) + ", AUA: " + interpolateValues(d.AUA, year),
// name: d.name + ": AUM:" + d.AUM + ", AUA: " + d.AUA,
// name: interpolateValues(d.AUM, year),
name: d.name,
AUM: interpolateValues(d.AUM, year),
marketPercentage: interpolateValues(d.marketPercentage, year),
AUA: interpolateValues(d.AUA, year),
fill: interpolateValues(d.fill, year),
xOffset: interpolateValues(d.xOffset, year),
yOffset: interpolateValues(d.yOffset, year)
};
});
}
// Finds (and possibly interpolates) the value for the specified year.
function interpolateValues(values, year) {
var i = bisect.left(values, year, 0, values.length - 1),
a = values[i];
if (i > 0) {
var b = values[i - 1],
t = (year - a[0]) / (b[0] - a[0]);
return a[1] * (1 - t) + b[1] * t;
}
return a[1];
};
//});
</script>
马克 - 您构建的第二个版本运行良好。我现在正在尝试解决各个线段。我已经添加了一个属性 'toggleSwitch' 但下面的代码运行 1x 并且只捕获对象的初始状态。
var lineTraces = svg.append("g")
.selectAll(".traces")
.data([0,1,2,4,5,6,7,8,9,10,11,12])
.enter()
.append("path")
.attr("stroke-width", 2)
.attr("stroke", "grey")
.attr("class", "lineTrace")
.attr("d", line)
.each(function(d,i){
d3.select(this)
.datum([someData[i]])
.attr("nothing", function(i) {console.log(i[0])})
.attr("d", line)
.style("stroke-dasharray", function(i) {return (i[0]["toggleSwitch"]<0 ? "0,0": "3,3")})
});
控制台日志,每个对象一个:
Object { name: "TheName", Impact: 120, bubbleSize: 30.4, YoY: 11, toggleSwitch: 0, xOffset: 5, yOffset: -30 }
您链接到的示例有一个预先建立的路径,然后在其上添加了 "stroke-dasharray"。您的第一个问题是您需要为每个公司建立该路径。然后就可以补间了。
// set up a line to create the path
var line = d3.svg.line()
.x(function(d) { return xScale(x(d)); })
.y(function(d) { return yScale(y(d)); })
.interpolate("basis");
// for each company add the path
var lineTraces = svg.append("g")
.selectAll(".traces")
.attr("fill","red")
.data([0,1,2]) // 3 companies
.enter()
.append("path")
.attr("stroke-width", 2)
.attr("stroke", "grey")
.attr("class", "lineTrace")
.each(function(d,i){
// get the line data and add path
var lineData = [interpolateData(2010)[i],interpolateData(2011)[i],
interpolateData(2012)[i],interpolateData(2013)[i],interpolateData(2014)[i]];
d3.select(this)
.datum(lineData)
.attr("d", line);
});
现在在每个路径上设置转换:
lineTraces.each(function(){
var path = d3.select(this);
path.transition()
.duration(30000)
.ease("linear")
.attrTween("stroke-dasharray", tweenDash)
});
tweenDash 在哪里:
function tweenDash() {
var l = lineTraces.node().getTotalLength();
var i = d3.interpolateString("0," + l, l + "," + l); // interpolation of stroke-dasharray style attr
return function(t) {
var p = lineTraces.node().getPointAtLength(t);
return i(t);
}
}
这是一个 example。
你会发现它并不完美,时机不对。如果我有更多时间,我会尝试回来理顺它。
编辑
昨晚考虑了一下,我突然意识到有一种更简单、更简洁的方法来添加跟踪。不要预先定义路径,然后 attrTween
ing "stroke-dasharray",而是边走边构建路径:
var someData = interpolateData(2010);
// add the paths like before
var lineTraces = svg.append("g")
.selectAll(".traces")
.data([0,1,2])
.enter()
.append("path")
.attr("stroke-width", 2)
.attr("stroke", "grey")
.attr("class", "lineTrace")
.attr("d", line)
.each(function(d,i){
d3.select(this)
.datum([someData[i]])
.attr("d", line);
});
// Tweens the entire chart by first tweening the year, and then the data.
// For the interpolated data, the dots and label are redrawn.
function tweenYear() {
var year = d3.interpolateNumber(2010, 2014);
// added "addTrace" function
return function(t) { addTrace(year(t)); displayYear(year(t)); };
}
// append the data and draw the path
function addTrace(year){
var thisData = interpolateData(year);
lineTraces.each(function(d,i){
var trace = d3.select(this);
trace.datum().push(thisData[i]);
trace.attr("d", line);
});
}
我正在尝试构建一个动画时间序列图表,该图表在移动点之后显示 'trace' 或蜗牛轨迹。我一直在尝试集成 KoGor 的 http://bl.ocks.org/KoGor/8163022 但运气不佳 - 我认为问题出在 tweenDash() - 原始功能是为单个跟踪设计的 - 每个公司都有一个。 下面附上了一个工作示例——时间序列清理和可移动数据标签有效,只是跟踪方面无效。
谢谢,
RL
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.10/d3.min.js"></script>
<!DOCTYPE html>
<meta charset="utf-8">
<body bgcolor="#000000">
<title>BPS</title>
<style>
@import url(style.css);
#chart {
margin-left: -40px;
height: 506px;
display:inline;
}
#buffer {
width: 100px;
height:506px;
float:left;
}
text {
font: 10px sans-serif;
color: #ffffff;
}
.dot {
stroke: #000;
}
.axis path, .axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.label {
fill: #777;
}
.year.label {
font: 900 125px "Helvetica Neue";
fill: #ddd;
}
.year.label.active {
fill: #aaa;
}
.overlay {
fill: none;
pointer-events: all;
cursor: ew-resize;
}
</style>
<div>
<div id="buffer"></div><div id="chart"></div>
</div>
<script src="d3.v3.min.js"></script>
<script>
var source = '[{"name":"ABCD","AUM":[[2010,1000.6],[2011,1200.6],[2012,1300.1],[2013,1400.5],[2014,1600.0]],"AUA":[[2010,3000.6],[2011,3300.2],[2012,4000.0],[2013,4500.8],[2014,6000.3]],"marketPercentage":[[2010,40.4],[2011,39.7],[2012,38.5],[2013,37.1],[2014,36.5]],"fill":[[2010,0],[2011,-1],[2012,-1],[2013,-1],[2014,-1]],"xOffset":[[2010,5],[2011,5],[2012,5],[2013,5],[2014,5]],"yOffset":[[2010,-30],[2011,-20],[2012,-20],[2013,-20],[2014,-10]]},{"name":"EFGH","AUM":[[2010,32.8],[2011,43.2],[2012,58.3],[2013,78.8],[2014,92]],"AUA":[[2010,327.3],[2011,439.3],[2012,547.0],[2013,710.0],[2014,824.0]],"marketPercentage":[[2010,1.0],[2011,1.2],[2012,1.5],[2013,1.8],[2014,1.9]],"fill":[[2010,0],[2011,1],[2012,1],[2013,1],[2014,1]],"xOffset":[[2010,5],[2011,5],[2012,5],[2013,5],[2014,5]],"yOffset":[[2010,-10],[2011,-10],[2012,-10],[2013,-10],[2014,-10]]},{"name":"HIJK","AUM":[[2010,0.1],[2011,0.5],[2012,1.2],[2013,2.4],[2014,2.6]],"AUA":[[2010,159.6],[2011,176.7],[2012,199.9],[2013,235.1],[2014,269.0]],"marketPercentage":[[2010,0.1],[2011,0.1],[2012,0.1],[2013,0.1],[2014,0.1]],"fill":[[2010,0],[2011,0],[2012,0],[2013,1],[2014,1]],"xOffset":[[2010,5],[2011,5],[2012,5],[2013,5],[2014,5]],"yOffset":[[2010,-10],[2011,-10],[2012,-10],[2013,-10],[2014,-10]]}]';
// Various accessors that specify the four dimensions of data to visualize.
function x(d) { return d.AUM; }
function y(d) { return d.AUA; }
function xo(d) {return d.xOffset; }
function yo(d) {return d.yOffset; }
function radius(d) { return d.marketPercentage; }
function key(d) { return d.name; }
// Chart dimensions.
var margin = {top: 19.5, right: 19.5, bottom: 19.5, left: 39.5},
width = 960 - margin.right,
height = 500 - margin.top - margin.bottom;
// Various scales. These domains make assumptions of data, naturally.
var xScale = d3.scale.linear().domain([0, 2000]).range([0, width]),
yScale = d3.scale.linear().domain([0, 5000]).range([height, 0]),
radiusScale = d3.scale.sqrt().domain([0, 500]).range([0, 40]),
colorScale = d3.scale.category10();
// The x & y axes.
var xAxis = d3.svg.axis().orient("bottom").scale(xScale).ticks(12, d3.format(",d")),
yAxis = d3.svg.axis().scale(yScale).orient("left");
// Create the SVG container and set the origin.
var svg = d3.select("#chart").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Add the x-axis.
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.style("fill", "#FFFFFF")
.call(xAxis);
// Add the y-axis.
svg.append("g")
.attr("class", "y axis")
.style("fill", "#FFFFFF")
.call(yAxis);
// Add an x-axis label.
svg.append("text")
.attr("class", "x label")
.attr("text-anchor", "end")
.style("fill", "#FFFFFF")
.attr("x", width)
.attr("y", height - 6);
//.text("income per capita, inflation-adjusted (dollars)");
// Add a y-axis label.
svg.append("text")
.attr("class", "y label")
.attr("text-anchor", "end")
.attr("y", 6)
.attr("dy", ".75em")
.style("fill", "#FFFFFF")
.attr("transform", "rotate(-90)")
// .text("life expectancy (years)")
;
// Add the year label; the value is set on transition.
var label = svg.append("text")
.attr("class", "year label")
.attr("text-anchor", "end")
.attr("y", height - 24)
.attr("x", width)
.text(2010);
//d3.json("investments_v04ANON.json", function(companies) {
companies = JSON.parse(source)
// A bisector since many company's data is sparsely-defined.
var bisect = d3.bisector(function(d) { return d[0]; });
// Add a dot per company. Initialize the data at 2010, and set the colors.
var dot = svg.append("g")
.attr("class", "dots")
.selectAll(".dot")
.data(interpolateData(2010))
.enter().append("circle")
.attr("class", "dot")
// .style("fill", function(d) { return colorScale(color(d)); })
.style("fill", function(d) {return colorScale(interpolateData(2010)) })
.call(position)
.sort(order);
var lineTraces = svg.append("path")
.attr("class", "lineTrace")
.selectAll(".traces")
.attr("stroke-width", 2)
.attr("stroke", "grey")
.data(interpolateData(2010));
//yields a mouseover label - "title" precludes need for separate mouseover event.
// dot.append("title")
// .text(function(d) { return d.name; });
//.text(function(d) {return d.AUM});
var theLabel = svg.append("g")
.attr("class", "texts")
.selectAll(".theLabel")
.data(interpolateData(2010))
.enter().append("text")
.attr("class", "text")
.text("hey")
.call(position2);
// Add an overlay for the year label.
var box = label.node().getBBox();
var overlay = svg.append("rect")
.attr("class", "overlay")
.attr("x", box.x)
.attr("y", box.y)
.attr("width", box.width)
.attr("height", box.height)
.on("mouseover", enableInteraction);
// Start a transition that interpolates the data based on year.
svg.transition()
.duration(30000)
.ease("linear")
.tween("year", tweenYear)
.attrTween("stroke-dasharray", tweenDash)
.each("end", enableInteraction);
// Positions the dots based on data.
function position(dot) {
dot .attr("cx", function(d) { return xScale(x(d)); })
.attr("cy", function(d) { return yScale(y(d)); })
.attr("r", function(d) { return radiusScale(radius(d)); })
.style("fill", function(d) {return d.fill>0 ? "green" : "red"} );//{return d.fill});
}
//function from: http://bl.ocks.org/KoGor/8163022
function tweenDash() {
var i = d3.interpolateString("0," + 5, 5 + "," + 5); // interpolation of stroke-dasharray style attr
// var l = path.node().getTotalLength();
// var i = d3.interpolateString("0," + l, l + "," + l); // interpolation of stroke-dasharray style attr
return function(t) {
var marker = d3.select(".dots");
// var p = path.node().getPointAtLength(t * l);
var p = lineTraces.node().getPointAtLength(t * 5);
marker.attr("transform", "translate(" + p.x + "," + p.y + ")");//move marker
return i(t);
}
}
function position2(theLabel) {
theLabel.attr("x", function(d) { return xScale(x(d)) + xo(d); })
.attr("y", function(d) { return yScale(y(d)) + yo(d); })
.attr("text-anchor", "end")
.style("fill", "#FFFFFF")
.text(function(d) { return d.name + ": AUM:" + Math.round(d.AUM) + ", AUA: " + Math.round(d.AUA) });//{return d.fill});
}
// Defines a sort order so that the smallest dots are drawn on top.
function order(a, b) {
return radius(b) - radius(a);
}
// After the transition finishes, you can mouseover to change the year.
function enableInteraction() {
var yearScale = d3.scale.linear()
.domain([2010, 2014])
.range([box.x + 10, box.x + box.width - 10])
.clamp(true);
// Cancel the current transition, if any.
svg.transition().duration(0);
overlay
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.on("mousemove", mousemove)
.on("touchmove", mousemove);
function mouseover() {
label.classed("active", true);
}
function mouseout() {
label.classed("active", true);
label.classed("active", false);
}
function mousemove() {
displayYear(yearScale.invert(d3.mouse(this)[0]));
}
}
// Tweens the entire chart by first tweening the year, and then the data.
// For the interpolated data, the dots and label are redrawn.
function tweenYear() {
var year = d3.interpolateNumber(2010, 2014);
return function(t) { displayYear(year(t)); };
}
// Updates the display to show the specified year.
function displayYear(year) {
dot.data(interpolateData(year), key).call(position).sort(order);
theLabel.data(interpolateData(year), key).call(position2).sort(order);
label.text(Math.round(year));
}
// Interpolates the dataset for the given (fractional) year.
function interpolateData(year) {
return companies.map(function(d) {
return {
// name: d.name + ": AUM:" + interpolateValues(d.AUM, year) + ", AUA: " + interpolateValues(d.AUA, year),
// name: d.name + ": AUM:" + d.AUM + ", AUA: " + d.AUA,
// name: interpolateValues(d.AUM, year),
name: d.name,
AUM: interpolateValues(d.AUM, year),
marketPercentage: interpolateValues(d.marketPercentage, year),
AUA: interpolateValues(d.AUA, year),
fill: interpolateValues(d.fill, year),
xOffset: interpolateValues(d.xOffset, year),
yOffset: interpolateValues(d.yOffset, year)
};
});
}
// Finds (and possibly interpolates) the value for the specified year.
function interpolateValues(values, year) {
var i = bisect.left(values, year, 0, values.length - 1),
a = values[i];
if (i > 0) {
var b = values[i - 1],
t = (year - a[0]) / (b[0] - a[0]);
return a[1] * (1 - t) + b[1] * t;
}
return a[1];
};
//});
</script>
马克 - 您构建的第二个版本运行良好。我现在正在尝试解决各个线段。我已经添加了一个属性 'toggleSwitch' 但下面的代码运行 1x 并且只捕获对象的初始状态。
var lineTraces = svg.append("g")
.selectAll(".traces")
.data([0,1,2,4,5,6,7,8,9,10,11,12])
.enter()
.append("path")
.attr("stroke-width", 2)
.attr("stroke", "grey")
.attr("class", "lineTrace")
.attr("d", line)
.each(function(d,i){
d3.select(this)
.datum([someData[i]])
.attr("nothing", function(i) {console.log(i[0])})
.attr("d", line)
.style("stroke-dasharray", function(i) {return (i[0]["toggleSwitch"]<0 ? "0,0": "3,3")})
});
控制台日志,每个对象一个:
Object { name: "TheName", Impact: 120, bubbleSize: 30.4, YoY: 11, toggleSwitch: 0, xOffset: 5, yOffset: -30 }
您链接到的示例有一个预先建立的路径,然后在其上添加了 "stroke-dasharray"。您的第一个问题是您需要为每个公司建立该路径。然后就可以补间了。
// set up a line to create the path
var line = d3.svg.line()
.x(function(d) { return xScale(x(d)); })
.y(function(d) { return yScale(y(d)); })
.interpolate("basis");
// for each company add the path
var lineTraces = svg.append("g")
.selectAll(".traces")
.attr("fill","red")
.data([0,1,2]) // 3 companies
.enter()
.append("path")
.attr("stroke-width", 2)
.attr("stroke", "grey")
.attr("class", "lineTrace")
.each(function(d,i){
// get the line data and add path
var lineData = [interpolateData(2010)[i],interpolateData(2011)[i],
interpolateData(2012)[i],interpolateData(2013)[i],interpolateData(2014)[i]];
d3.select(this)
.datum(lineData)
.attr("d", line);
});
现在在每个路径上设置转换:
lineTraces.each(function(){
var path = d3.select(this);
path.transition()
.duration(30000)
.ease("linear")
.attrTween("stroke-dasharray", tweenDash)
});
tweenDash 在哪里:
function tweenDash() {
var l = lineTraces.node().getTotalLength();
var i = d3.interpolateString("0," + l, l + "," + l); // interpolation of stroke-dasharray style attr
return function(t) {
var p = lineTraces.node().getPointAtLength(t);
return i(t);
}
}
这是一个 example。
你会发现它并不完美,时机不对。如果我有更多时间,我会尝试回来理顺它。
编辑
昨晚考虑了一下,我突然意识到有一种更简单、更简洁的方法来添加跟踪。不要预先定义路径,然后 attrTween
ing "stroke-dasharray",而是边走边构建路径:
var someData = interpolateData(2010);
// add the paths like before
var lineTraces = svg.append("g")
.selectAll(".traces")
.data([0,1,2])
.enter()
.append("path")
.attr("stroke-width", 2)
.attr("stroke", "grey")
.attr("class", "lineTrace")
.attr("d", line)
.each(function(d,i){
d3.select(this)
.datum([someData[i]])
.attr("d", line);
});
// Tweens the entire chart by first tweening the year, and then the data.
// For the interpolated data, the dots and label are redrawn.
function tweenYear() {
var year = d3.interpolateNumber(2010, 2014);
// added "addTrace" function
return function(t) { addTrace(year(t)); displayYear(year(t)); };
}
// append the data and draw the path
function addTrace(year){
var thisData = interpolateData(year);
lineTraces.each(function(d,i){
var trace = d3.select(this);
trace.datum().push(thisData[i]);
trace.attr("d", line);
});
}