Я нарисовал масштабируемый график упаковки кругов (aka flare graph) с помощью d3. js ( версия 3.4.11 )
displayFlareGraph: function (containerId, options, json) {
options.width = options.width || 1280; // circle width
options.height = options.height || 800; // circle height
options.radius = options.radius || 720; // circle radius
var w = options.width,
h = options.height,
r = options.radius,
x = d3.scale.linear().range([0, r]),
y = d3.scale.linear().range([0, r]),
node,
root;
var color = (key) => Palette.pickColor("labels_colors", key);
var pack = d3.layout.pack()
.size([r, r])
.value(function (d) {
return d.size;
})
var vis = d3.select(containerId).insert("svg:svg", "h2")
.attr("width", w)
.attr("height", h)
.append("svg:g")
.attr("transform", "translate(" + (w - r) / 2 + "," + (h - r) / 2 + ")");
function zoom(d, i) {
var k = r / d.r / 2;
x.domain([d.x - d.r, d.x + d.r]);
y.domain([d.y - d.r, d.y + d.r]);
var t = vis.transition()
.duration(d3.event.altKey ? 7500 : 750);
t.selectAll("circle")
.attr("cx", function (d) {
return x(d.x);
})
.attr("cy", function (d) {
return y(d.y);
})
.attr("r", function (d) {
return k * d.r;
})
t.selectAll("text")
.attr("x", function (d) {
return x(d.x);
})
.attr("y", function (d) {
return d.children ? y(d.y) : y(d.y + 10);
})
.style("opacity", function (d) {
return k * d.r > 20 ? 1 : 0;
});
node = d;
d3.event.stopPropagation();
} //zoom
node = root = json;
var nodes = pack.nodes(root);
vis.selectAll("circle")
.data(nodes)
.enter().append("svg:circle")
.attr("class", function (d) {
return d.children ? "parent" : "child";
})
.attr("cx", function (d) {
return d.x;
})
.attr("cy", function (d) {
return d.y;
})
.attr("r", function (d) {
return d.r;
})
.attr("fill", d => color(d.height))
.on("click", function (d) {
return zoom(node == d ? root : d);
});
vis.selectAll("text")
.data(nodes)
.enter().append("svg:text")
.attr("class", function (d) {
return d.children ? "parent" : "child";
})
.attr("x", function (d) {
return d.x;
})
.attr("y", function (d) {
return d.children ? d.y : d.y + 10;
})
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.style("opacity", function (d) {
return d.r > 20 ? 1 : 0;
})
.text(function (d) {
return d.name;
});
d3.select(window).on("click", function () {
zoom(root);
});
} //displayFlareGraph
Я хочу раскрасить круги сейчас и я попытался использовать свойство fill
, подобное этому
.attr("r", function (d) {
return d.r;
})
.attr("fill", d => color(d.height))
, где моя функция color
возвращает случайный цвет RGB. У меня по умолчанию CSS, который делает цвета кругов. Но fill
, похоже, не отменяет эти настройки.
var displayFlareGraph = function(containerId, options, json) {
options.width = options.width || 1280; // circle width
options.height = options.height || 800; // circle height
options.radius = options.radius || 720; // circle radius
var w = options.width,
h = options.height,
r = options.radius,
x = d3.scale.linear().range([0, r]),
y = d3.scale.linear().range([0, r]),
node,
root;
var color = (key) => {
return {
"r": ~~(Math.random() * 255),
"g": ~~(Math.random() * 255),
"b": ~~(Math.random() * 255),
"opacity": 0.9
}
}
var pack = d3.layout.pack()
.size([r, r])
.value(function(d) {
return d.size;
})
var vis = d3.select(containerId).insert("svg:svg", "h2")
.attr("width", w)
.attr("height", h)
.append("svg:g")
.attr("transform", "translate(" + (w - r) / 2 + "," + (h - r) / 2 + ")");
function zoom(d, i) {
var k = r / d.r / 2;
x.domain([d.x - d.r, d.x + d.r]);
y.domain([d.y - d.r, d.y + d.r]);
var t = vis.transition()
.duration(d3.event.altKey ? 7500 : 750);
t.selectAll("circle")
.attr("cx", function(d) {
return x(d.x);
})
.attr("cy", function(d) {
return y(d.y);
})
.attr("r", function(d) {
return k * d.r;
})
t.selectAll("text")
.attr("x", function(d) {
return x(d.x);
})
.attr("y", function(d) {
return d.children ? y(d.y) : y(d.y + 10);
})
.style("opacity", function(d) {
return k * d.r > 20 ? 1 : 0;
});
node = d;
d3.event.stopPropagation();
} //zoom
node = root = json;
var nodes = pack.nodes(root);
vis.selectAll("circle")
.data(nodes)
.enter().append("svg:circle")
.attr("class", function(d) {
return d.children ? "parent" : "child";
})
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.attr("r", function(d) {
return d.r;
})
.attr("fill", d => color(d.height))
.on("click", function(d) {
return zoom(node == d ? root : d);
});
vis.selectAll("text")
.data(nodes)
.enter().append("svg:text")
.attr("class", function(d) {
return d.children ? "parent" : "child";
})
.attr("x", function(d) {
return d.x;
})
.attr("y", function(d) {
return d.children ? d.y : d.y + 10;
})
.attr("dy", ".35em")
.attr("text-anchor", "middle")
.style("opacity", function(d) {
return d.r > 20 ? 1 : 0;
})
.text(function(d) {
return d.name;
});
d3.select(window).on("click", function() {
zoom(root);
});
} //displayFlareGraph
//https://observablehq.com/@d3/zoomable-circle-packing
$(document).ready(function() {
displayFlareGraph('#graph', {
width: 400,
height: 300,
radius: 400
}, NODES);
})
const NODES = {
"children": [{
"name": "#0",
"children": [{
"name": "back",
"size": 0.086
}, {
"name": "time",
"size": 0.036
}, {
"name": "dance",
"size": 0.036
}, {
"name": "make",
"size": 0.034
}, {
"name": "summer",
"size": 0.029
}, {
"name": "stay",
"size": 0.029
}, {
"name": "white",
"size": 0.022
}, {
"name": "lying",
"size": 0.022
}, {
"name": "whoa",
"size": 0.021
}]
}, {
"name": "#1",
"children": [{
"name": "heart",
"size": 0.086
}, {
"name": "broke",
"size": 0.072
}, {
"name": "story",
"size": 0.061
}, {
"name": "life",
"size": 0.05
}, {
"name": "give",
"size": 0.048
}, {
"name": "start",
"size": 0.025
}, {
"name": "time",
"size": 0.022
}]
}, {
"name": "#2",
"children": [{
"name": "make",
"size": 0.087
}, {
"name": "beautiful",
"size": 0.071
}, {
"name": "world",
"size": 0.068
}, {
"name": "baby",
"size": 0.033
}, {
"name": "hair",
"size": 0.03
}, {
"name": "understand",
"size": 0.029
}, {
"name": "light",
"size": 0.025
}, {
"name": "hard",
"size": 0.025
}, {
"name": "ground",
"size": 0.024
}, {
"name": "smile",
"size": 0.023
}, {
"name": "heads",
"size": 0.022
}, {
"name": "hear",
"size": 0.021
}, {
"name": "flip",
"size": 0.021
}, {
"name": "overwhelmed",
"size": 0.02
}, {
"name": "nana",
"size": 0.02
}]
}, {
"name": "#3",
"children": [{
"name": "words",
"size": 0.044
}, {
"name": "tears",
"size": 0.041
}, {
"name": "rock",
"size": 0.036
}, {
"name": "stop",
"size": 0.031
}, {
"name": "half",
"size": 0.027
}, {
"name": "heart",
"size": 0.026
}]
}, {
"name": "#4",
"children": [{
"name": "gonna",
"size": 0.12
}, {
"name": "wanna",
"size": 0.093
}, {
"name": "girl",
"size": 0.047
}, {
"name": "steal",
"size": 0.042
}, {
"name": "meet",
"size": 0.038
}, {
"name": "belongs",
"size": 0.037
}, {
"name": "hold",
"size": 0.036
}, {
"name": "getcha",
"size": 0.034
}, {
"name": "alright",
"size": 0.029
}]
}, {
"name": "#5",
"children": [{
"name": "love",
"size": 0.23
}, {
"name": "things",
"size": 0.063
}, {
"name": "make",
"size": 0.047
}, {
"name": "perfect",
"size": 0.024
}, {
"name": "heart",
"size": 0.021
}]
}, {
"name": "#6",
"children": [{
"name": "night",
"size": 0.14
}, {
"name": "song",
"size": 0.093
}, {
"name": "danced",
"size": 0.074
}, {
"name": "forget",
"size": 0.069
}, {
"name": "remember",
"size": 0.056
}, {
"name": "home",
"size": 0.035
}, {
"name": "afraid",
"size": 0.034
}, {
"name": "line",
"size": 0.029
}, {
"name": "wildest",
"size": 0.02
}, {
"name": "dreaming",
"size": 0.02
}]
}, {
"name": "#7",
"children": [{
"name": "feel",
"size": 0.082
}, {
"name": "tonight",
"size": 0.039
}, {
"name": "gonna",
"size": 0.038
}, {
"name": "eyes",
"size": 0.037
}, {
"name": "calls",
"size": 0.034
}, {
"name": "longer",
"size": 0.031
}, {
"name": "heart",
"size": 0.031
}, {
"name": "saved",
"size": 0.028
}, {
"name": "turning",
"size": 0.027
}, {
"name": "hold",
"size": 0.026
}, {
"name": "falling",
"size": 0.025
}, {
"name": "love",
"size": 0.021
}, {
"name": "light",
"size": 0.02
}]
}, {
"name": "#8",
"children": [{
"name": "baby",
"size": 0.13
}, {
"name": "wanna",
"size": 0.077
}, {
"name": "moving",
"size": 0.031
}, {
"name": "kiss",
"size": 0.031
}, {
"name": "girl",
"size": 0.029
}, {
"name": "stay",
"size": 0.026
}, {
"name": "feel",
"size": 0.026
}, {
"name": "find",
"size": 0.023
}, {
"name": "good",
"size": 0.021
}, {
"name": "change",
"size": 0.021
}, {
"name": "hold",
"size": 0.02
}]
}, {
"name": "#9",
"children": [{
"name": "forever",
"size": 0.09
}, {
"name": "live",
"size": 0.062
}, {
"name": "life",
"size": 0.053
}, {
"name": "young",
"size": 0.04
}, {
"name": "kids",
"size": 0.03
}, {
"name": "happenin",
"size": 0.022
}]
}]
}
body>svg {
position: absolute;
top: -80px;
left: -160px;
}
text {
font-size: 11px;
pointer-events: none;
}
text.parent {
fill: #1f77b4;
}
circle {
fill: #ccc;
stroke: #999;
pointer-events: all;
}
circle.parent {
fill: #1f77b4;
fill-opacity: .1;
stroke: steelblue;
}
circle.parent:hover {
stroke: #ff7f0e;
stroke-width: .5px;
}
circle.child {
pointer-events: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3-cloud/1.2.4/d3.layout.cloud.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="graph"></div>