问题
I am a newbie to d3.js . I am working in d3.js zoomable sunburst chart http://bl.ocks.org/mbostock/4348373. When the user zoom into a particular arc and I need to capture this state of the sunburst diagram . When the user comes back to the sunburst diagram or load the graph again he should see the state where he left.
Note : I dont want to serialise the svg elements to show the state of the sunburst. If i serialise it then the chart will be static and user cant click on the sunburst and traverse to other arcs.
Proposed Solution : one solution came to my mind is simulate mouse clicks on the sunburst nodes till the last node user looks into. I am not able to devise an algorithm for this . Please guide me whether any other solution is possible ..
回答1:
The approach you said is easy to implement. I have made a small sample for it.
Click the Start Save
button to start saving the state of Sunburst. Click Stop Save
button after performing a few zooming actions. You can make any further changes to the chart. Now, clicking Load
button will show you the saved state of the chart.
var width = 500,
height = 500,
radius = Math.min(width, height) / 2;
var x = d3.scale.linear()
.range([0, 2 * Math.PI]);
var y = d3.scale.sqrt()
.range([0, radius]);
var color = d3.scale.category10();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + (height / 2 + 10) + ") rotate(-90 0 0)");
var partition = d3.layout.partition()
.value(function(d) {
return d.size;
});
var arc = d3.svg.arc()
.startAngle(function(d) {
return Math.max(0, Math.min(2 * Math.PI, x(d.x)));
})
.endAngle(function(d) {
return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx)));
})
.innerRadius(function(d) {
return Math.max(0, y(d.y));
})
.outerRadius(function(d) {
return Math.max(0, y(d.y + d.dy));
});
var root = getData();
var savedData = partition.nodes(root);
var g = svg.selectAll("g")
.data(partition.nodes(root))
.enter().append("g");
var path = g.append("path")
.attr("d", arc)
.style("fill", function(d) {
return color((d.children ? d : d.parent).name);
})
.on("click", click);
var text = g.append("text")
.attr("x", function(d) {
return y(d.y);
})
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
.attr("transform", function(d) {
return "rotate(" + computeTextRotation(d) + ")";
})
.text(function(d) {
return d.name;
})
.style("fill", "white");
function computeTextRotation(d) {
var angle = x(d.x + d.dx / 2) - Math.PI / 2;
return angle / Math.PI * 180;
}
var saveData = false;
var savedData = [];
d3.select("#saveBtn").on("click", function() {
if (d3.select(this).attr("value") == "Start Save") {
savedData = [];
d3.select(this).attr("value", "Stop Save");
saveData = true;
} else {
d3.select(this).attr("value", "Start Save");
saveData = false;
}
});
d3.select("#loadBtn").on("click", function() {
var root = g.filter(function(d) {
return d.name == "Root"
}).data();
click(root[0]);
savedData.forEach(function(val) {
var node = g.filter(function(d, i) {
return i == val
}).data();
click(node[0]);
});
});
function click(d, i) {
if (saveData) {
if(d.name=="Root"){
savedData = [];
} else{
savedData.push(i);
}
}
// fade out all text elements
if (d.size !== undefined) {
d.size += 100;
};
text.transition().attr("opacity", 0);
path.transition()
.duration(750)
.attrTween("d", arcTween(d))
.each("end", function(e, i) {
// check if the animated element's data e lies within the visible angle span given in d
if (e.x >= d.x && e.x < (d.x + d.dx)) {
// get a selection of the associated text element
var arcText = d3.select(this.parentNode).select("text");
// fade in the text element and recalculate positions
arcText.transition().duration(750)
.attr("opacity", 1)
.attr("transform", function() {
return "rotate(" + computeTextRotation(e) + ")"
})
.attr("x", function(d) {
return y(d.y);
});
}
});
}
// Word wrap!
var insertLinebreaks = function(t, d, width) {
alert(0)
var el = d3.select(t);
var p = d3.select(t.parentNode);
p.append("g")
.attr("x", function(d) {
return y(d.y);
})
.attr("transform", function(d) {
return "rotate(" + computeTextRotation(d) + ")";
})
.append("foreignObject")
.attr('x', -width / 2)
.attr("width", width)
.attr("height", 200)
.append("xhtml:p")
.attr('style', 'word-wrap: break-word; text-align:center;')
.html(d.name);
alert(1)
el.remove();
alert(2)
};
d3.select(self.frameElement).style("height", height + "px");
// Interpolate the scales!
function arcTween(d) {
var xd = d3.interpolate(x.domain(), [d.x, d.x + d.dx]),
yd = d3.interpolate(y.domain(), [d.y, 1]),
yr = d3.interpolate(y.range(), [d.y ? 20 : 0, radius]);
return function(d, i) {
return i ? function(t) {
return arc(d);
} : function(t) {
x.domain(xd(t));
y.domain(yd(t)).range(yr(t));
return arc(d);
};
};
}
function getData() {
return {
"name": "Root",
"children": [{
"name": "A1",
"children": [{
"name": "B1",
"size": 30
}, {
"name": "B2",
"size": 40
}, {
"name": "B3",
"children": [{
"name": "C1",
"size": 10
}, {
"name": "C2",
"size": 15
}]
}]
}, {
"name": "A2",
"children": [{
"name": "B4",
"size": 40
}, {
"name": "B5",
"size": 30
}, {
"name": "B6",
"size": 10
}]
}, {
"name": "A3",
"children": [{
"name": "B7",
"size": 50
}, {
"name": "B8",
"size": 15
}
]
}]
}
};
path {
stroke: #fff;
fill-rule: evenodd;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="main"></div>
<input type="button" value="Start Save" id="saveBtn"/>
<input type="button" value="Load" id="loadBtn"/>
I don't know where do you plan to save the state of the sunburst. I would suggest that Localstorage would be a nice option.
Hope this helps.
来源:https://stackoverflow.com/questions/29739838/capturing-saving-the-current-state-of-d3-js-sunburst-chart