viewof selection = {
const height = 600;
const marginTop = 0;
const marginRight = 0;
const marginBottom = 0;
const marginLeft = 0;
console.log("rendered");
const x = d3
.scaleLinear()
.domain([0, d3.max(data, (d) => d[xVariable])])
.nice()
.range([marginLeft, width - marginRight]);
const y = d3
.scaleLinear()
.domain([0, d3.max(data, (d) => d[yVariable])])
.nice()
.range([height - marginBottom, marginTop]);
const canvas = DOM.canvas(width, height);
const ctx = canvas.getContext("2d");
canvas.style.display = "block";
canvas.style.maxWidth = "100%";
canvas.style.margin = "auto";
data.forEach((d) => {
d.brushed = 0;
d.radius = 3;
});
function drawPoints() {
ctx.clearRect(0, 0, width, height);
data.forEach((d) => {
ctx.globalAlpha = 0.5;
ctx.beginPath();
ctx.arc(x(d[xVariable]), y(d[yVariable]), d.radius, 0, 2 * Math.PI);
ctx.fillStyle = colorScale(d[colorVariable]);
ctx.fill();
});
}
drawPoints();
gsap.ticker.add(drawPoints);
const svg = d3
.create("svg")
.attr("viewBox", [0, 0, width, height])
.style("position", "absolute")
.style("top", marginTop + "px")
.style("left", marginLeft + "px")
.style("width", "100%")
.style("height", "100%")
.style("pointer-events", "none");
const brush = d3
.brush()
.extent([
[marginLeft, marginTop],
[width - marginRight, height - marginBottom]
])
.on("start brush end", brushed);
svg
.append("g")
.attr("class", "brush")
.style("pointer-events", "all")
.call(brush);
function brushed(event) {
const selection = event.selection;
if (selection) {
const [[x0, y0], [x1, y1]] = selection;
data.forEach((d) => {
const isBrushed =
x0 <= x(d[xVariable]) &&
x(d[xVariable]) <= x1 &&
y0 <= y(d[yVariable]) &&
y(d[yVariable]) <= y1;
gsap.to(d, {
brushed: isBrushed ? 1 : 0,
radius: isBrushed ? 6 : 3,
duration: 0.1,
ease: "power2.inOut"
});
});
} else {
gsap.to(data, {
brushed: 0,
radius: 3,
duration: 3,
ease: "power2.inOut"
});
}
}
return htl.html`
<div style="position: relative">
${canvas}
${svg.node()}
</div>
`;
}