首页 > 解决方案 > 如何使用 d3.js 绘制螺旋气泡图

问题描述

我想用 d3.js 绘制螺旋气泡图。d3.js 版本是 6.6.0。

但是,我不能...

请给我建议!

■■■■ 理想状态■■■■

气泡像这个网站一样呈螺旋状排列。

https://www.pubnub.com/blog/fun-with-d3js-data-visualization-eye-candy-with-streaming-json/

理想图表 (i.stack.imgur.com)

■■■■ 当前状态■■■■

我的气泡图不是螺旋形的。在这里,我的气泡图。

当前图表 (i.stack.imgur.com)

这是我的源代码。

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')

最好的祝福

标签: javascriptsvgd3.js

解决方案


使用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" />


推荐阅读