Capturing/Saving d3.js Sunburst 图表的当前状态
Capturing/Saving the current state of d3.js Sunburst Chart
我是 d3.js 的新手。我在 d3.js 可缩放旭日图 http://bl.ocks.org/mbostock/4348373 中工作。当用户放大特定弧线时,我需要捕捉旭日图的这种状态。当用户返回旭日图或再次加载图表时,他应该看到他离开时的状态。
注意:我不想序列化 svg 元素来显示旭日的状态。如果我对其进行序列化,那么图表将是静态的,用户无法单击旭日并遍历其他弧线。
建议的解决方案:
我想到的一个解决方案是模拟鼠标点击 sunburst 节点,直到最后一个节点用户查看为止。
我无法为此设计算法。
请指导我是否还有其他解决方案..
你说的方法很容易实现。我为它做了一个小样本。
单击 Start Save
按钮开始保存 Sunburst 的状态。执行一些缩放操作后单击 Stop Save
按钮。您可以对图表进行任何进一步的更改。现在,单击 Load
按钮将显示图表的保存状态。
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"/>
不知道你打算把旭日的状态保存到哪里。我建议 Localstorage 是一个不错的选择。
希望这对您有所帮助。
我是 d3.js 的新手。我在 d3.js 可缩放旭日图 http://bl.ocks.org/mbostock/4348373 中工作。当用户放大特定弧线时,我需要捕捉旭日图的这种状态。当用户返回旭日图或再次加载图表时,他应该看到他离开时的状态。
注意:我不想序列化 svg 元素来显示旭日的状态。如果我对其进行序列化,那么图表将是静态的,用户无法单击旭日并遍历其他弧线。
建议的解决方案: 我想到的一个解决方案是模拟鼠标点击 sunburst 节点,直到最后一个节点用户查看为止。 我无法为此设计算法。 请指导我是否还有其他解决方案..
你说的方法很容易实现。我为它做了一个小样本。
单击 Start Save
按钮开始保存 Sunburst 的状态。执行一些缩放操作后单击 Stop Save
按钮。您可以对图表进行任何进一步的更改。现在,单击 Load
按钮将显示图表的保存状态。
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"/>
不知道你打算把旭日的状态保存到哪里。我建议 Localstorage 是一个不错的选择。
希望这对您有所帮助。