Reputation: 357
I have an svg with a bunch of different paths. An example of one looks as follows:
<path d="M2943648.169104896,472020.4539518825L2943637.730875478,472048.6771215173" />
I'm attempting to find a way to get which section of the line has been clicked. I could add an onclick function which returns d
, but this will only give me the full string as listed above, and I need just the relevant portion. As in, if a user clicks the first part of the line it'll return the correct coordinates (2943648.169104896,472020.4539518825
) or if they click on the second segment, it will show the other coordinates that are in the value for d. Some of these paths have many different line segments.
I found an answer on here with a deprecated method of doing this and I was wondering what the latest way to do this is. Thank you.
Upvotes: 5
Views: 682
Reputation: 21193
I took the path to Array segments code from another SO answer:
Split a svg path d to array of objects
wrapped it in code that creates new d-paths, incrementally adding every segment
replace the mouseover
with a click
remove the specific color for a path segment
because N paths are overlayed some extra anti-aliasing kicks in
<svg-path-extractor colors="orange,green,blue,gold,hotpink,red">
<div id=label></div>
<svg viewBox="0 0 24 24">
<path d="M 0 0 C 15 2 3 18 7 21 A 1 1 0 0 0 14 6 L 0 10 C 0 10 20 30 20 20 q -2 -4 4.13 -1 Z"></path>
<g id=segments></g>
</svg>
</svg-path-extractor>
<script>
customElements.define("svg-path-extractor", class extends HTMLElement {
connectedCallback() {
let addPath = (d, stroke) => {}
setTimeout(() => {
let colors = this.getAttribute("colors").split `,`;
let segments = [...this.querySelectorAll("svg path")].map(p => {
return this.pathToArray(p);
}).flat().map(seg => Object.keys(seg).map(key => seg[key]).join ` `);
segments.map((seg, idx, arr) => {
let d = arr.slice(0, idx + 1).join(" ");
let p = document.createElementNS("http://www.w3.org/2000/svg", "path");
p.setAttribute("d", d);
p.setAttribute("fill", "none");
p.setAttribute("stroke", colors.shift());
p.onmouseover = (evt) => {
let label = `${idx}d=${d}`.replace(seg,`<b>${seg}</b>`);
this.querySelector("#label").innerHTML = label;
}
this.querySelector("svg #segments").prepend(p);
})
});
}
pathToArray(path) {
if (typeof path != "string") path = path.getAttribute("d");
const PATH_COMMANDS = {
M: ["x", "y"],
m: ["dx", "dy"],
H: ["x"],
h: ["dx"],
V: ["y"],
v: ["dy"],
L: ["x", "y"],
l: ["dx", "dy"],
Z: [],
C: ["x1", "y1", "x2", "y2", "x", "y"],
c: ["dx1", "dy1", "dx2", "dy2", "dx", "dy"],
S: ["x2", "y2", "x", "y"],
s: ["dx2", "dy2", "dx", "dy"],
Q: ["x1", "y1", "x", "y"],
q: ["dx1", "dy1", "dx", "dy"],
T: ["x", "y"],
t: ["dx", "dy"],
A: ["rx", "ry", "rotation", "large-arc", "sweep", "x", "y"],
a: ["rx", "ry", "rotation", "large-arc", "sweep", "dx", "dy"]
};
const items = path.replace(/[\n\r]/g, '').
replace(/-/g, ' -').
replace(/(\d*\.)(\d+)(?=\.)/g, '$1$2 ').
trim().
split(/\s*,|\s+/);
const segments = [];
let currentCommand = '';
let currentElement = {};
while (items.length > 0) {
let it = items.shift();
if (PATH_COMMANDS.hasOwnProperty(it))
currentCommand = it;
else
items.unshift(it);
currentElement = {
type: currentCommand
};
PATH_COMMANDS[currentCommand].forEach((prop) => {
it = items.shift(); // TODO sanity check
currentElement[prop] = it;
});
if (currentCommand === 'M') {
currentCommand = 'L';
} else if (currentCommand === 'm') {
currentCommand = 'l';
}
segments.push(currentElement);
}
return segments
}
});
</script>
<style>
body {
font: 12px Arial
}
b{
font-size:1.2em;
color:darkgreen;
}
svg {
width: 180px;
background: pink;
}
svg #segments path{
cursor:pointer;
}
</style>
Upvotes: 1