|
<!DOCTYPE html> |
|
<html lang="en"> |
|
<head> |
|
<meta charset="utf-8"> |
|
<link href='https://fonts.googleapis.com/css?family=Lato:300,900' rel='stylesheet' type='text/css'> |
|
|
|
<style> |
|
:root { |
|
--annotation-color: #E8336D; |
|
} |
|
|
|
body{ |
|
background-color: whitesmoke; |
|
} |
|
|
|
svg { |
|
background-color: white; |
|
font-family: 'Lato'; |
|
} |
|
|
|
line { |
|
stroke:#eae4e4; |
|
} |
|
|
|
.editable .annotation-subject, .editable .annotation-textbox { |
|
cursor: move; |
|
} |
|
|
|
.annotation path { |
|
stroke: var(--annotation-color); |
|
fill: rgba(0,0,0,0); |
|
} |
|
|
|
.annotation path.connector-arrow{ |
|
fill: var(--annotation-color); |
|
} |
|
|
|
.annotation text { |
|
fill: var(--annotation-color); |
|
} |
|
|
|
.annotation-title { |
|
font-weight: bold; |
|
} |
|
|
|
circle.handle { |
|
stroke-dasharray: 5; |
|
stroke: var(--annotation-color); |
|
fill: rgba(255, 255, 255, .5); |
|
cursor: move; |
|
|
|
stroke-opacity: .4; |
|
} |
|
|
|
circle.handle.highlight { |
|
stroke-opacity: 1; |
|
} |
|
|
|
.annotation-note-bg { |
|
fill: rgba(255, 255, 255, 0); |
|
} |
|
.dedicated-line { |
|
stroke: #00897b; |
|
stroke-width: 5px; |
|
} |
|
.embedded-line { |
|
stroke: #e4b0b0; |
|
stroke-width: 5px; |
|
} |
|
.embedded path { |
|
stroke: #006d62; |
|
fill: #006d62; |
|
fill-opacity: 0.25; |
|
stroke-width: 2px; |
|
} |
|
.dedicated path { |
|
stroke: #b68c8c; |
|
fill: #b68c8c; |
|
fill-opacity: 0.25; |
|
stroke-width: 2px; |
|
} |
|
|
|
</style> |
|
</head> |
|
<body> |
|
<svg width=960 height=500> |
|
<defs> <pattern id="half-fill" width="10" height="10"> |
|
<line x1="2.5" x2="2.5" y1="0" y2="10" class="dedicated-line"></line> |
|
<line x1="7.5" x2="7.5" y1="0" y2="10" class="embedded-line"></line> |
|
</pattern> </defs> |
|
</svg> |
|
<script src="https://d3js.org/d3.v4.js"></script> |
|
<script src="https://cdn.rawgit.com/susielu/d3-annotation/master/d3-annotation.js"></script> |
|
|
|
<script> |
|
var svg = d3.select("svg"), |
|
width = 1000, |
|
height = 500; |
|
|
|
var color = { |
|
// "a": "#e4b0b0", |
|
"a": "#00897b", |
|
"b": "#e4b0b0", |
|
"c": "url(#half-fill)" |
|
} |
|
let groups = ["embedded", "dedicated"] |
|
|
|
const nodes = d3.range(600).map((d,i) => { |
|
if (i < 25) { |
|
return { color: "c", embedded: true, dedicated: true, center: 250 } |
|
} |
|
if (i < 400) { |
|
return { color: "b", embedded: false, dedicated: true, center: 415 } |
|
} |
|
return { color: "a", embedded: true, dedicated: false, center: 100 } |
|
|
|
}) |
|
|
|
|
|
var simulation = d3.forceSimulation() |
|
.force("collision", d3.forceCollide(6).iterations(1)) |
|
.force("x", d3.forceX(a => a.center).strength(0.05)) |
|
.force("y", d3.forceY(0).strength(0.05)) |
|
.force("center", d3.forceCenter(width / 2, height / 2)) |
|
.alphaDecay(0.001) |
|
.alpha(0.25) |
|
.nodes(nodes) |
|
.on("tick", ticked) |
|
.stop() |
|
|
|
simulation.tick() |
|
|
|
var node = svg.append("g") |
|
.attr("class", "nodes") |
|
.selectAll("circle") |
|
.data(nodes) |
|
.enter().append("circle") |
|
.attr("r", 5) |
|
.style("stroke", "black") |
|
.attr("cx", d => d.x) |
|
.attr("cy", d => d.y) |
|
.style("fill", d => color[d.color]) |
|
|
|
setTimeout(() => { simulation.restart() }, 2500); |
|
|
|
let points = groups.map(p => nodes |
|
.filter(d => d[p]) |
|
.map(d => ({ x: d.x, y: d.y, r: 5 }))) |
|
|
|
|
|
let circle = points.map( p => d3.packEnclose(p)) |
|
|
|
const annotations = [{ |
|
note: { label: "embedded in other teams", |
|
}, |
|
data: {id : "Data Visualization Professionals"}, |
|
dy: 93, |
|
dx: -230, |
|
x: circle[0].x, |
|
y: circle[0].y, |
|
type: d3.annotationCalloutCircle, |
|
className: "embedded", |
|
subject: { |
|
radius: circle[0].r, |
|
radiusPadding: 5 |
|
} |
|
}, |
|
{ |
|
note: { label: "in teams dedicated to data visualization", |
|
}, |
|
data: {id : "Data Visualization Professionals"}, |
|
dy: 93, |
|
dx: 230, |
|
x: circle[1].x, |
|
y: circle[1].y, |
|
className: "dedicated", |
|
type: d3.annotationCalloutCircle, |
|
subject: { |
|
radius: circle[1].r, |
|
radiusPadding: 5 |
|
} |
|
} |
|
] |
|
|
|
const makeAnnotations = d3.annotation() |
|
.annotations(annotations) |
|
.accessors({ x: d => d.x , y: d => d.y, title: d => d.id }) |
|
|
|
svg.insert("g", "g") |
|
.attr("class", "annotation-test") |
|
.call(makeAnnotations) |
|
|
|
function ticked() { |
|
node |
|
.attr("cx", d => d.x) |
|
.attr("cy", d => d.y); |
|
|
|
makeAnnotations.annotations() |
|
.forEach((d, i) => { |
|
let points = nodes |
|
.filter(d => d[groups[i]]) |
|
.map(d => ({ x: d.x, y: d.y, r: 5})) |
|
console.log("points", points) |
|
circle = d3.packEnclose(points) |
|
d.position = { x: circle.x, y: circle.y } |
|
d.subject.radius = circle.r + 2 |
|
}) |
|
|
|
makeAnnotations.update() |
|
} |
|
|
|
|
|
|
|
</script> |
|
</body> |
|
</html> |