javascript - 如何使用 d3.js 绘制螺旋气泡图
问题描述
我想用 d3.js 绘制螺旋气泡图。d3.js 版本是 6.6.0。
但是,我不能...
请给我建议!
■■■■ 理想状态■■■■
气泡像这个网站一样呈螺旋状排列。
https://www.pubnub.com/blog/fun-with-d3js-data-visualization-eye-candy-with-streaming-json/
■■■■ 当前状态■■■■
我的气泡图不是螺旋形的。在这里,我的气泡图。
这是我的源代码。
var data_set = {
children: [
{name: "AAA", val: 50},
{name: "BBB", val: 45},
{name: "CCC", val: 40},
{name: "DDD", val: 35},
{name: "EEE", val: 30},
{name: "FFF", val: 25},
{name: "GGG", val: 20},
{name: "HHH", val: 15},
{name: "III", val: 10},
{name: "JJJ", val: 5},
{name: "KKK", val: 3},
{name: "LLL", val: 2},
{name: "NNN", val: 1}
]
}
var width=400, height=400;
var zoom = this.zoom
var bubble = d3.pack().size([width, height]).padding(0);
var nodes = d3.hierarchy( data_set ).sum(function(d){ return d.val })
.sort(function(a, b) { return a.value - b.value; });
;
var bubble_data = bubble(nodes).descendants();
var no_root_bubble = bubble_data;
var max_val = d3.max(no_root_bubble, function(d){ return d.r ;});
var min_val = d3.min(no_root_bubble, function(d){return d.r ; });
var color_scale = d3.scaleLinear().domain([min_val, max_val]).range(d3.schemeCategory10);
var color_scale_num = d3.scaleLinear().domain([min_val, max_val]).range([0, 9]);
var font_scale = d3.scaleLinear().domain([min_val, max_val]).range([9, 28]);
var bubbles = d3.select("#content").selectAll(".bubble").data(no_root_bubble)
.enter()
.append("g")
.attr("class", "bubble")
.attr("transform", function(d) { return "translate(" + d.x * zoom + "," + d.y * zoom + ")"; });
bubbles.append("circle")
.attr("r", function(d) { return d.r * zoom; })
.style("fill", function(d,i){
return d3.schemeCategory10[Math.round(color_scale_num(d.r))];
})
.attr("fill-opacity", 0.6);
;
bubbles.append("text")
.attr("text-anchor", "middle")
.attr("dominant-baseline", "central")
.text(function(d){ return d.data.name ; })
.style("font-size", function(d){ return font_scale(d.r);})
.style("fill", 'white')
最好的祝福
解决方案
使用buildSpiralLayout
功能:
const checkIntersection = (nodes, node, angle, radius) => {
const x = radius * Math.sin(angle);
const y = radius * -Math.cos(angle);
return nodes.some(n => Math.hypot(n.x-x, n.y - y) <= n.val + node.val);
};
const buildSpiralLayout = nodes => {
const ordered = nodes.sort((a,b) => a.val - b.val);
let angle = 0;
let radius = 10;
return ordered.reduce((all, node, index) => {
angle = (index === 0) ? 0 : angle + Math.PI / 3;
while (checkIntersection(all, node, angle, radius)) radius++;
const x = radius * Math.sin(angle);
const y = radius * -Math.cos(angle);
all.push({...node, x, y});
return all;
}, []);
}
const data = [
{name: "AAA", val: 50},
{name: "BBB", val: 45},
{name: "CCC", val: 40},
{name: "DDD", val: 35},
{name: "EEE", val: 30},
{name: "FFF", val: 25},
{name: "GGG", val: 20},
{name: "HHH", val: 15},
{name: "III", val: 10},
{name: "JJJ", val: 9},
{name: "KKK", val: 8},
{name: "LLL", val: 7},
{name: "NNN", val: 6}
].map((item, index) => ({...item, id: index}));
const layout = buildSpiralLayout(data);
const svg = d3.select('svg');
const g = svg.append('g').attr('transform', 'translate(200,200)');
const items = g.selectAll('g.item')
.data(layout, d => d.id)
.enter()
.append('g')
.classed('item', true)
.attr('transform', d => `translate(${d.x},${d.y})`)
items.append('circle')
.attr('r', d => d.val)
.style('fill', 'blue');
items.append('text')
.text(d => d.name)
.attr('text-anchor', 'middle')
.attr('alignment-baseline', 'middle')
text {
font-family: "Ubuntu";
font-size: 10px;
fill: white;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>
<svg width="400" height="400" />
推荐阅读
- sql - 如何在 SQL Server Management Studio 18 中获取两个表的 SQL 中的 2 个小数百分比
- sqlite - 如何将在线图像字符串放入 SQLite 数据库并在 Flutter 中离线检索它们?
- vue.js - vue.js 组件是否需要名称选项?
- karate - 在启用并行执行并通过 maven 命令完成执行时获取总执行时间
- php - Laravel redirect()->back() 返回 iframe
- c# - 如何打断说动词以收集输入?
- python - 根据输入创建包含三个单词短语的词典
- python - Numpy where 行为
- amazon-web-services - AWS cloudwatch SNS 服务仪表板“没有可显示的指标”
- azure-active-directory - 如何通过 Keycloak 社交登录从 Azure AD 获取附加属性