This demo is utilised with svg and canvas and brush features.
DOM structure: div.box > canvas + svg
, div is relative positioned and the graphic elements are absolute and the svg is on the top of canvas.
SVG has some performance issues when nodes quantity reach 10k. d3.brush is a good feature but it is not working on canvas. So I put them together.
xxxxxxxxxx
<head>
<meta charset="utf-8">
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.12.0/d3.min.js" integrity="sha256-+9Mf3cAVmxxudDsr1XwXUeRZFtvdWVYdq5/vcgiYyNU=" crossorigin="anonymous"></script>
<style>
body { margin:0;position:fixed;top:0;right:0;bottom:0;left:0; }
canvas, svg {
display: block;
position: absolute;
top: 0;
left: 0;
}
.box {
position: relative;
width: 1100px;
margin: 2em auto;
}
</style>
</head>
<body>
<style>
canvas, svg {
display: block;
position: absolute;
top: 0;
left: 0;
}
.box {
position: relative;
width: 900px;
margin: 2em auto;
}
</style>
<div class="box" id="box"></div>
<script>
var w = 900, h = 450,
p = {top: 20, right: 20, bottom: 40, left: 50},
cw = w - p.left - p.right,
ch = h - p.top - p.bottom;
var data = d3.range(1000)
.map(function (d, i) {
return {
x: Math.random() * cw,
y: Math.random() * ch,
r: Math.random() * 5 + 3
};
});
var point = {x: 0, y: 0},
area = [[0, 0], [0, 0]];
var box = d3.select("#box");
box.on("mousemove", function () {
var t = d3.mouse(this);
point.x = t[0]; point.y = t[1];
})
.on("click", function () { area = [[0, 0], [0, 0]]; });
var canvas = box.append("canvas").attr("width", w).attr("height", h);
var ctx = canvas.node().getContext("2d");
var svg = box.append("svg").attr("width", w).attr("height", h);
function canvasDraw () {
ctx.save();
ctx.clearRect(0, 0, w, h);
ctx.translate(p.left, p.top);
ctx.strokeStyle = "#666";
ctx.fillStyle = "rgba(250,0,0,1)";
ctx.lineWidth = 1;
var i = 0, length = data.length,
c,
hc,
isMouseOver = false,
isFenced = false;
while (i < length) {
c = data[i];
ctx.beginPath();
ctx.arc(c.x, ch - c.y, c.r, 0, Math.PI * 2, false);
isFenced = c.x >= area[0][0] &&
c.x <= area[1][0] &&
(ch - c.y) >= area[0][1] &&
(ch - c.y) <= area[1][1];
isMouseOver = ctx.isPointInPath(point.x, point.y);
if (isFenced) { ctx.fill(); }
if (isMouseOver) { hc = c; }
ctx.stroke();
i++;
}
if (hc) {
ctx.beginPath();
ctx.arc(hc.x, ch - hc.y, hc.r, 0, Math.PI * 2, false);
ctx.fill();
}
ctx.restore();
window.requestAnimationFrame(canvasDraw);
}
function SVGDraw () {
var yScale, xScale, yAxis, xAxis;
yScale = d3.scaleLinear().domain([0, ch]).range([ch, 0]);
xScale = d3.scaleLinear().domain([0, cw]).range([0, cw]);
yAxis = d3.axisLeft().scale(yScale);
xAxis = d3.axisBottom().scale(xScale);
var brush = d3.brush()
.extent([[0, 0], [cw, ch]])
.on("brush", function () { area = d3.event.selection; });
svg.append("g")
.attr("transform", "translate(" + [p.left, p.top] + ")")
.call(yAxis);
svg.append("g")
.attr("transform", "translate(" + [p.left, p.top + ch] + ")")
.call(xAxis);
svg.append("g")
.attr("transform", "translate(" + [p.left, p.top] + ")")
.call(function (g) {
g.call(brush);
// active one brush area
brush.move(g, [[300,100], [500, 250]]);
});
}
SVGDraw();
canvasDraw();
</script>
</body>
https://cdnjs.cloudflare.com/ajax/libs/d3/5.12.0/d3.min.js