{
const container = d3.create("div");
const height = 200;
const canvas = container
.append("canvas")
.attr("width", width * dpi)
.attr("height", height * dpi)
.style("width", width + "px")
.style("height", height + "px");
const svg = container
.append("svg")
.style("position", "absolute")
.style("pointer-events", "none")
.style("top", 0)
.style("left", 0)
.attr("width", width)
.attr("height", height);
svg.append("rect").attr("fill", "none").attr("stroke", "black");
const ctx = canvas.node().getContext("2d");
ctx.scale(dpi, dpi);
const radius = 1.5 * Math.PI;
function drawEnd(
x1,
y1,
length,
height,
padding,
max_width,
inverse = false
) {
const line1_length = max_width - x1,
line2_length = length - (inverse ? line1_length * -1 : line1_length);
const penalty = inverse ? -10 : 10;
const x2 = max_width,
y2 = y1,
x3 = x2,
y3 = y1 + padding + height * 2,
x4 = line2_length + penalty,
y4 = y3,
x5 = x4,
y5 = y1 + padding + height,
x6 = x3,
y6 = y5,
x7 = x6,
y7 = y1 + height,
x8 = x1,
y8 = y7;
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y2);
inverse
? ctx.arc(
x2,
y3 - height - padding * 0.5,
(y3 - y2) / 2,
Math.PI * 1.5,
Math.PI * 0.5,
true
)
: ctx.arc(
x2,
y3 - height - padding * 0.5,
(y3 - y2) / 2,
Math.PI * 1.5,
Math.PI * 0.5,
false
);
ctx.lineTo(x3, y3);
ctx.lineTo(x4, y4);
ctx.lineTo(x5, y5);
ctx.lineTo(x6, y6);
inverse
? ctx.arc(
x6,
y6 - padding * 0.5,
(y6 - y7) / 2,
Math.PI * 0.5,
Math.PI * 1.5,
false
)
: ctx.arc(
x6,
y6 - padding * 0.5,
(y6 - y7) / 2,
Math.PI * 0.5,
Math.PI * 1.5,
true
);
ctx.lineTo(x7, y7);
ctx.lineTo(x8, y8);
ctx.lineTo(x1, y1);
ctx.strokeStyle = "white";
ctx.stroke();
ctx.fillStyle = "rgba(0,0,155,0.2)";
ctx.fill();
}
drawEnd(50, 20, 100, 10, 10, 100, false);
drawEnd(60, 40, 100, 10, 10, 20, true);
drawEnd(50, 60, 100, 10, 10, 100, false);
function drawRect(x1, y1, x2, y2) {
ctx.beginPath();
ctx.moveTo(x1, y1);
ctx.lineTo(x2, y1);
ctx.arc(x2, y1 + radius, radius, -Math.PI / 2, Math.PI / 2, false);
ctx.lineTo(x1, y2);
ctx.arc(x1, y2 - radius, radius, Math.PI / 2, -Math.PI / 2, false);
ctx.fillStyle = "blue";
ctx.fill();
}
drawRect(5, 5, 100, 15);
const data = [1, 2, 13, 20, 23].map((i) => ({
x: i
}));
let index = new Flatbush(data.length);
const scale = d3
.scaleLinear()
.range([10, width])
.domain(d3.extent(data, (d) => d.x));
data.forEach((d, i) => {
ctx.beginPath();
const x = scale(d.x);
ctx.rect(x, 150, 10, 10);
ctx.fillStyle = "red";
ctx.fill();
ctx.closePath();
index.add(x, 150, x + 10, 150 + 10);
});
index.finish();
canvas.on("mousemove", mousemoved);
function mousemoved(e) {
const [x, y] = d3.pointer(e, canvas.node());
const results = index.search(x, y, x, y);
const feature = data[results];
canvas.style("cursor", null);
svg.select("rect").attr("x", -100);
if (results.length === 0) return;
canvas.style("cursor", "pointer");
svg
.select("rect")
.datum(feature)
.attr("x", scale(feature.x))
.attr("y", 150)
.attr("width", 11)
.attr("height", 11);
}
return container.node();
}