I have a data structure like this (assume that the data structure is non-negotiable):
data = {
segments : [
{x : 20, size : 10,
I would try to flatten the colors before you actually start creating the elements. If changes to the data occur I would then update this flattened data structure and redraw. The flattened data needs to be stored somewhere to make real d3 transitions possible.
Here is a longer example that worked for me. Yon can see it in action here.
Here is the code:
var data = {
segments : [
{x : 20, size : 10, colors : ['#ff0000','#00ff00']},
{x : 40, size : 20, colors : ['#0000ff','#000000']}
]
};
function pos(d,i) { return d.x + (i * d.size); } // rect position
function size(d,i) { return d.size; } // rect size
function f(d,i) { return d.color; } // rect color
function flatten(data) {
// converts the .colors to a ._colors list
data.segments.forEach( function(s,i) {
var list = s._colors = s._colors || [];
s.colors.forEach( function(c,j) {
var obj = list[j] = list[j] || {}
obj.color = c
obj.x = s.x
obj.size = s.size
});
});
}
function changeRect(chain) {
return chain
.transition()
.attr('x',pos)
.attr('y',pos)
.attr('width',size)
.attr('height',size)
.attr('fill',f)
.style('fill-opacity', 0.5)
}
vis = d3
.select('#container')
.append('svg')
.attr('width',200)
.attr('height',200);
// add the top-level svg element and size it
function update(){
flatten(data);
// add the nested svg elements
var all = vis.selectAll('g')
.data(data.segments)
all.enter().append('g');
all.exit().remove();
// Add a rectangle for each color
var rect = all.selectAll('rect')
.data(function (d) { return d._colors; }, function(d){return d.color;})
changeRect( rect.enter().append('rect') )
changeRect( rect )
rect.exit().remove()
}
function changeLater(time) {
setTimeout(function(){
var ds = data.segments
ds[0].x = 10 + Math.random() * 100;
ds[0].size = 10 + Math.random() * 100;
ds[1].x = 10 + Math.random() * 100;
ds[1].size = 10 + Math.random() * 100;
if(time == 500) ds[0].colors.push("orange")
if(time == 1000) ds[1].colors.push("purple")
if(time == 1500) ds[1].colors.push("yellow")
update()
}, time)
}
update()
changeLater(500)
changeLater(1000)
changeLater(1500)
Important here is the flatten function which does the data conversion and stores/reuses the result as _colors property in the parent data element. Another important line is;
.data(function (d) { return d._colors; }, function(d){return d.color;})
which specifies where to get the data (first parameter) AND what the unique id for each data element is (second parameter). This helps identifying existing colors for transitions, etc.