var ttSel = d3.select('body').selectAppend('div.tooltip.tooltip-hidden') var sel = d3.select('#graph').html('') var c = d3.conventions({ sel, margin: {left: 30}, layers: 'cs', }) var [ctx, svg] = c.layers d3.drawAxis(c) var colorScale = d3.scaleLinear().range(['#f0f', '#0f0']) var n = 1000000 var data = d3.range(n).map(i => { var r = i/n var px = c.x(r) var py = c.y(Math.random()) var color = colorScale(r + Math.random()/5) ctx.fillStyle = color ctx.fillRect(px, py, 1, 1) return {px, py, color} }) var highlightCircle = svg.append('circle') .at({r: 10, fill: 'none'}) .st({pointerEvents: 'none'}) var bisect = d3.bisector(d => d.px) svg.append('rect') .at({width: c.width, height: c.height, fillOpacity: 0}) .call(d3.attachTooltip) .on('mousemove', function(){ var startT = performance.now() var [px, py] = d3.mouse(this) var index = bisect.left(data, px) var minPoint = null var minDist = Infinity var lxDist = 0 var rxDist = 0 var i = 0 while (lxDist < minDist && rxDist < minDist){ lxDist = checkPoint(data[index - i]) rxDist = checkPoint(data[index + i]) i++ } function checkPoint(d){ if (!d) return Infinity var dx = d.px - px var dy = d.py - py var dist = Math.sqrt(dx*dx + dy*dy) if (dist < minDist){ minDist = dist minPoint = d } return Math.abs(px - d.px) } var timeDelta = performance.now() - startT ttSel.text(' found in ' + d3.format('.3f')(timeDelta) + 'ms with ' + d3.format('05')(i*2) + ' comparisons') highlightCircle .translate([minPoint.px, minPoint.py]) .at({stroke: minPoint.color, strokeWidth: 4}) }) .on('mouseout', function(){ highlightCircle.at({strokeWidth: 0}) })