Public
Edited
Aug 1, 2023
17 forks
Importers
57 stars
Insert cell
Insert cell
Insert cell
svg = {
const width = 928;
const height = 400;

const svg = d3
.create("svg")
.attr("height", height)
.attr("viewBox", [0, 0, width, height])
.attr("style", "max-width: 100%; height: auto; font: 14.5px sans-serif;");

// Important! Make sure the SVG is attached to the DOM before calling occlusion(),
// so that the bounding boxes can be measured.
yield svg.node();

const n = 1000;
svg
.selectAll("text")
.data(rwg(n))
.join("text")
.text((d) => d)
.attr("x", () => (width * Math.random()) | 0)
.attr("y", () => (height * Math.random()) | 0);

// Raise on mouseover, set data-priority on click.
let priority = 0;
svg
.selectAll("text")
.on("mouseover", function () {
d3.select(this).attr("fill", "red").raise();
svg.call(occlusion);
})
.on("mouseout", function () {
d3.select(this).attr("fill", null);
svg.call(occlusion);
})
.on("click", function () {
const node = d3.select(this);
const cur = +node.attr("data-priority");
node
.attr("data-priority", cur ? null : ++priority)
.style("fill", cur ? null : "steelblue");
svg.call(occlusion);
});

// Introduce random changes.
do {
const i = (Math.random() * n) | 0;
svg.select(`text:nth-of-type(${i})`).raise();
svg.call(occlusion);

await Promises.delay(300);
} while (true);
}
Insert cell
html`<style>
svg { cursor: pointer; }
svg text.occluded { opacity: 0.1 }
</style>`
Insert cell
function occlusion(svg, against = "text") {
const nodes = d3
.sort(svg.selectAll(against), (node) => +node.getAttribute("data-priority"))
.reverse()
.map((node) => {
const { x, y, width, height } = node.getBoundingClientRect();
return { node, x, y, width, height };
});

const visible = [];
for (const d of nodes) {
const occluded = visible.some((e) => intersectRect(d, e));
d3.select(d.node).classed("occluded", occluded);
if (!occluded) visible.push(d);
}
return visible;
}
Insert cell
// This intersection function works for rect and text, but not so much for circles.
function intersectRect(a, b) {
return !(
a.x + a.width < b.x ||
b.x + b.width < a.x ||
a.y + a.height < b.y ||
b.y + b.height < a.y
);
}
Insert cell
// https://www.npmjs.com/package/random-words
rwg = require("random-words@1.1.0").catch(() => window.words)
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more