2015-01-23 2 views
0

Я пытаюсь построить анимированную диаграмму временных рядов, которая показывает следы «следа» или улитки после движущейся точки. Я пытаюсь интегрировать KoGor's http://bl.ocks.org/KoGor/8163022, но мне не повезло - я думаю, что проблема кроется в tweenDash() - Исходная функция была разработана для одной трассы - у нее одна для каждой компании. Приведенный ниже рабочий пример - работают очищающие метки и подвижные метки данных, а не аспект трассировки.Добавление следов в анимированную диаграмму пузырьков d3.js

Спасибо,

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 { 
 
\t width: 100px; 
 
\t height:506px; 
 
\t 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 + ")") 
 
\t .style("fill", "#FFFFFF") 
 
    .call(xAxis); 
 

 
// Add the y-axis. 
 
svg.append("g") 
 
    .attr("class", "y axis") 
 
\t .style("fill", "#FFFFFF") 
 
    .call(yAxis); 
 

 
// Add an x-axis label. 
 
svg.append("text") 
 
    .attr("class", "x label") 
 
    .attr("text-anchor", "end") 
 
\t .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") 
 
\t \t \t \t .style("fill", "#FFFFFF") 
 

 
    .attr("transform", "rotate(-90)") 
 
// .text("life expectancy (years)") 
 
    \t ; 
 

 
// 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) { 
 
\t 
 
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); 
 
\t 
 
\t 
 
    var lineTraces = svg.append("path") 
 
    \t \t .attr("class", "lineTrace") 
 
\t \t .selectAll(".traces") 
 
\t \t .attr("stroke-width", 2) 
 
\t \t .attr("stroke", "grey") 
 
\t \t .data(interpolateData(2010)); 
 

 

 
    //yields a mouseover label - "title" precludes need for separate mouseover event. 
 
// dot.append("title") 
 
// \t .text(function(d) { return d.name; }); 
 
//.text(function(d) {return d.AUM}); 
 
\t 
 
var theLabel = svg.append("g") 
 
\t .attr("class", "texts") 
 
\t .selectAll(".theLabel") 
 
\t .data(interpolateData(2010)) 
 
\t .enter().append("text") 
 
\t .attr("class", "text") 
 
\t .text("hey") 
 
\t .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) 
 
\t .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)); }) 
 
\t \t .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 
 
    
 
\t 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); 
 
    } 
 
    } 
 

 
\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); }) 
 
\t \t .attr("text-anchor", "end") 
 
\t \t .style("fill", "#FFFFFF") 
 
\t \t .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); 
 
\t 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 { 
 
// \t \t name: d.name + ": AUM:" + interpolateValues(d.AUM, year) + ", AUA: " + interpolateValues(d.AUA, year), 
 
// \t \t 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), 
 
\t \t fill: interpolateValues(d.fill, year), 
 
\t \t xOffset: interpolateValues(d.xOffset, year), 
 
\t \t 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>

Mark- второй вариант вы построили очень хорошо работает. Теперь я пытаюсь обратиться к отдельным сегментам линии. Я добавил атрибут «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 } 

ответ

2

Примера вы связаны был заранее установленным путь, а затем attrTweened на «обводку-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 ТРАЕКТОРИЙ «обводка-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); 
    }); 
} 

Это produces much better results.

+0

@ user1515373, см. Новый ответ – Mark

+0

Отметьте это действительно отличная работа, извинившись за то, что так долго отвечали. Я только построил первую версию и прочитал вашу оценку для второй версии, это направление, о котором я думал, создавая путь по пути. Я посмотрю, смогу ли я заставить его «стереть» путь, когда скруббер вернется вовремя. – user1515373

+0

@ user1515373, я в замешательстве. Вы хотите, чтобы часть строки 2010 была разбита? – Mark

Смежные вопросы