//Distance Calculations
const disToLine = (p, a, b) => {
let sqr = (n) => n * n,
disSqr = (a, b) => sqr(a.x - b.x) + sqr(a.y - b.y),
lSqr = disSqr(a, b);
if (!lSqr) return disSqr(p, a);
let t = ((p.x - a.x) * (b.x - a.x) + (p.y - a.y) * (b.y - a.y)) / lSqr;
t = Math.max(0, Math.min(1, t));
return Math.sqrt(
disSqr(p, { x: a.x + t * (b.x - a.x), y: a.y + t * (b.y - a.y) })
);
};
//Calculates the absolute coordinates of a line
const calculateAbsoluteCords = (line) => {
let getSlope = ([p1, p2]) => (p1.y - p2.y) / (p1.x - p2.x),
rec = line.getBoundingClientRect(),
coords = [
{ x: +line.getAttribute("x1"), y: +line.getAttribute("y1") },
{ x: +line.getAttribute("x2"), y: +line.getAttribute("y2") }];
if (getSlope(coords) <= 0)
coords = [
{ x: rec.x, y: rec.y + rec.height },
{ x: rec.x + rec.width, y: rec.y }
];
else
coords = [
{ x: rec.x, y: rec.y },
{ x: rec.x + rec.width, y: rec.y + rec.height }
];
return coords;
};
//gets all lines in range of a given point
const getLinesInRange = (point, minimumDistance, svg) => {
let linesInRange = [],
lines = svg.querySelectorAll("line");
lines.forEach(line => {
let [p1, p2] = calculateAbsoluteCords(line),
dis = disToLine(point, p1, p2);
if (dis <= minimumDistance) {
line.classList.add("closeTo");
linesInRange.push({ dis: dis, line: line });
} else line.classList.remove("closeTo");
});
return linesInRange.sort((a,b) => a.dis > b.dis ? 1 : -1).map(l => l.line);
};
let minDist = 3, el = {};
['mouseRange', 'rangeDisplay', 'mouseRangeDisplay', 'numberOfLines', 'numberInRange', 'numberOfLinesDisplay', 'clicked', 'svgContainer']
.forEach(l => {el[l] = document.getElementById(l); })
el.svgContainer.addEventListener("mousemove", (e) => {
el.numberInRange.textContent = getLinesInRange({ x: e.clientX, y: e.clientY }, minDist, el.svgContainer).length;
});
el.svgContainer.addEventListener("click", (e) => {
let lines = getLinesInRange({ x: e.clientX, y: e.clientY }, minDist, el.svgContainer);
el.clicked.textContent = lines.map((l) => l.getAttribute("stroke")).join(', ');
});
el.mouseRange.addEventListener("input", () => {
minDist = parseInt(el.mouseRange.value);
el.mouseRangeDisplay.textContent = minDist;
});
el.numberOfLines.addEventListener("input", () => {
let numOfLines = parseInt(el.numberOfLines.value);
el.numberOfLinesDisplay.textContent = numOfLines;
generateLines(numOfLines);
});
let generateLines = (total) => {
let lineCount = el.svgContainer.querySelectorAll('line').length;
if(lineCount > total) {
let lines = el.svgContainer.querySelectorAll(`line:nth-last-child(-n+${lineCount-total})`);
lines.forEach(l => l.remove());
}
for(let i=lineCount; i<total; i++) {
var newLine = document.createElementNS('http://www.w3.org/2000/svg','line')
newLine.setAttribute('id','line2');
['x1','y1','x2','y2'].map(attr => newLine.setAttribute(attr,Math.floor(Math.random()*500)));
newLine.setAttribute("stroke", '#' + Math.floor(Math.random()*16777215).toString(16));
el.svgContainer.appendChild(newLine);
}
}
generateLines(10);
.closeTo {
filter: drop-shadow(0 0 3px rgba(0,0,0,1));
}
Range: <input type="range" min="1" max="50" id="mouseRange" value="3" /><span id="mouseRangeDisplay">3</span>
#Lines: <input type="range" min="0" max="1000" id="numberOfLines" value="10" step="10" /><span id="numberOfLinesDisplay">10</span>
In Range: <span id="numberInRange">3</span>
<br/>
<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" id="svgContainer" style="width:500px;height:500px;background:#F1F1F1;">
</svg><br/>
Clicked: <span id="clicked"></span>
svg line:hover { stroke-width: 6px; }
, not very nice but does the trick to an extent. – Moskowitz