我会这样解决问题:构建一个强制导向的布局,从其中一个教程开始(可能是this one,因为它构建了一些使用循环填充进行初始化的东西)。添加 D3 的缩放行为。
var force = d3.layout.force()
// force layout settings
var zoom = d3.behavior.zoom()
// etc.
到目前为止,一切都很好。除了力布局喜欢在[width/2, height/2] 周围闲逛,但如果你以[0, 0] 为中心,它会使缩放更容易。与geometric zooming 斗争一会儿,直到你意识到这个问题确实需要semantic zooming。实现语义缩放。去喝杯咖啡吧。
找出圆圈大小与缩放级别之间的关系,以便您判断何时需要发现下一个级别。像这样的:
// expand when this percent of the screen is covered
var coverageThreshold = 0.6;
// the circles should be scaled to this size
var maxRadius = 20;
// the size of the visualization
var width = 960;
// which means this is the magic scale factor
var scaleThreshold = maxRadius / (coverageThreshold * width)
// note: the above is probably wrong
现在,实现空间数据过滤器。当你缩小时,你基本上想要隐藏任何已经缩小到视野之外的数据点,这样你就不会浪费 gpu 时间来计算它们的表示。此外,找出一种算法来确定用户正在放大哪个节点。这很可能使用Voronoi tessalation。了解比您想象的更多的几何知识。
我们还需要解决一个数学问题。我们将让子节点代替父节点,因此我们需要根据父节点的总大小来缩放它们的大小。这会很烦人,需要一些调整才能正确,除非你知道正确的算法......我不知道。
// size of the parent node
var parentRadius = someNumberPossiblyCalculated;
// area of the parent node
var parentArea = 2 * Math.PI * parentRadius;
// percent of the the parent's area that will be covered by children
// (here be dragons)
var childrenCoverageRatio = 0.8;
// total area covered by children
var childrenArea = parentArea * childrenCoverageArea;
// the total of the radiuses of the children
var childTotal = parent.children
.map(radiusFn)
.reduce(function(a, b) { return a + b; });
// the child radius function
// use this to generate the child elements with d3
// (optimize that divide in production!)
var childRadius = function(d) {
return maxRadius * radiusFn(d) / childTotal;
};
// note: the above is probably wrong
好的,现在我们已经准备好制作魔法酱的部分了。在zoom 处理程序中,对照您的参考点检查d3.event.scale。如果用户已放大过去,请快速执行以下步骤:
- 隐藏屏幕外的父元素
- 移除被放大的父节点
- 将该父节点的子节点添加到父节点的 x 和 y 位置的布局中
- 明确运行
force.tick() 几次,让孩子们稍微分开
- 可能使用循环包装布局来使此过程更清洁
好的,现在我们有了一个不错的带有缩放功能的小力布局。当您放大时,您将达到某个阈值,希望由可视化代码自动计算。当您这样做时,您正在放大的节点“爆炸”成它的所有组成节点。
现在弄清楚如何构建您的代码,以便您可以“重置”事物,让您继续放大并让它再次发生。这可能是递归的,但将比例缩小几个数量级并同时将 SVG 元素扩展成反比可能会更简洁。
现在缩小。首先,您需要为反向过程设置一个明显的缩放阈值,即控件中的滞后效应,这将有助于防止在有人骑着鼠标滚轮时出现跳跃的可视化。你放大它会扩大,然后你必须再缩小一点,然后它会再次收缩。
好的,当您达到缩小阈值时,您只需放下子元素并将父元素添加回子元素位置的质心。
var parent.x = d3.mean(parent.children, function(d) { return d.x; });
var parent.y = d3.mean(parent.children, function(d) { return d.y; });
此外,当您缩小时,开始显示您在放大时隐藏的那些节点。
正如@Lars 所说,这可能需要一点时间。