I am working with a svg element which has a line chart and the label at the end of the each line.
A minimum sample is below
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720">
<rect width="1280" height="720" fill="black" stroke="bound" style="overflow: visible;"></rect>
<rect x="70" y="70" width="1120" height="600" fill="black"></rect>
<g style="transform: translate(70px, 70px);">
<g >
<path fill="none" stroke="white" opacity="1" d="M0,385.8783581344254L46.666666666666664,340.04110795732606L93.33333333333333,340.04110795732606L140,340.04110795732606L186.66666666666666,340.04110795732606L233.33333333333334,340.04110795732606L280,261.5473775717186L326.6666666666667,261.5473775717186L373.3333333333333,261.5473775717186L420,261.5473775717186L466.6666666666667,261.5473775717186L513.3333333333333,193.79191415980043L560,193.79191415980043L606.6666666666666,193.79191415980043L653.3333333333334,174.38265408552468L700,174.38265408552468L746.6666666666666,140.17685505574866L793.3333333333334,140.17685505574866L840,140.17685505574866L886.6666666666666,140.17685505574866L933.3333333333334,140.17685505574866L980,99.07632474477359L1026.6666666666665,99.07632474477359L1073.3333333333335,99.07632474477359L1120,97.28970086328088"></path>
<path fill="none" stroke="white" opacity="1
" d="M0,498.798228969007L46.666666666666664,498.798228969007L93.33333333333333,508.9184060721062L140,508.9184060721062M233.33333333333334,405.6265984654729L280,405.6265984654729L326.6666666666667,405.6265984654729L373.3333333333333,405.6265984654729L420,456.7774936061383L466.6666666666667,426.0869565217393L513.3333333333333,426.0869565217393L560,405.6265984654729L606.6666666666666,405.6265984654729L653.3333333333334,221.48337595907955L700,221.48337595907955L746.6666666666666,221.48337595907955L793.3333333333334,201.02301790281317L840,211.25319693094582L886.6666666666666,170.33248081841413L933.3333333333334,170.33248081841413L980,170.33248081841413L1026.6666666666665,180.56265984654772L1073.3333333333335,155.26826115061547L1120,124.24046541693643"></path>
<path fill="none" stroke="white" opacity="1" d="M0,577.4117647058824L46.666666666666664,577.4117647058824L93.33333333333333,577.4117647058824L140,577.4117647058824L186.66666666666666,577.4117647058824M280,545.3510436432637L326.6666666666667,539.2789373814043L373.3333333333333,539.2789373814043L420,539.2789373814043L466.6666666666667,551.4231499051233L513.3333333333333,551.4231499051233L560,551.4231499051233L606.6666666666666,551.4231499051233L653.3333333333334,479.8498122653327L700,459.8247809762202L746.6666666666666,459.8247809762202L793.3333333333334,459.8247809762202L840,459.8247809762202L886.6666666666666,459.8247809762202L933.3333333333334,479.8498122653327L980,456.43070787637083L1026.6666666666665,459.94397759103623L1073.3333333333335,454.75671750181556L1120,296.39468690702114"></path>
<path fill="none" stroke="white" opacity="1" d="M0,438.6554621848753L46.666666666666664,438.6554621848753L93.33333333333333,438.6554621848753L140,357.98319327731105L186.66666666666666,358.34658187599433L233.33333333333334,358.34658187599433L280,141.17647058823525L326.6666666666667,141.17647058823525L373.3333333333333,141.17647058823525L420,141.17647058823525L466.6666666666667,141.17647058823525L513.3333333333333,70.5882352941176L560,70.5882352941176L606.6666666666666,70.5882352941176L653.3333333333334,70.5882352941176L700,70.5882352941176L746.6666666666666,0L793.3333333333334,0L840,0L886.6666666666666,0L933.3333333333334,23.5294117647058L980,23.5294117647058L1026.6666666666665,23.5294117647058L1073.3333333333335,23.5294117647058L1120,23.5294117647058"></path>
</g>
<g style="opacity: 1;">
<text text-anchor="left" fill="white" alignment-baseline="middle" x="1120" y="23.5294117647058" style="overflow: visible;">Rwanda:61.25%</text>
<text text-anchor="left" fill="white" alignment-baseline="middle" x="1120" y="97.28970086328088" style="overflow: visible;">Cuba:53.41%</text>
<text text-anchor="left" fill="white" alignment-baseline="middle" x="1120" y="124.24046541693643" style="overflow: visible;">Nicaragua:50.55%</text>
<text text-anchor="left" fill="white" alignment-baseline="middle" x="1120" y="296.39468690702114" style="overflow: visible;">Chad:32.26%</text>
</g>
</g>
</svg>
The entire text element for label at the end of line chart is not visible for all of them. For example, for and
the entire text elements respectively,
Rwanda:61.25% and Nicaragua:50.55%
are not visible. But for other two, and
then entire text elements respectively Cuba:53.41% and Chad:32.26%
are visible.
Is there any way for me to know beforehand, by using javascript
to know which text elements will be entirely
visible and which will be partially
visible.
I tried with getBBox()
of each of the text elements but they were not of help to me. I initially thought that if x width
is greater than viewbox width than that would be the candidate for
partially visible` elements. But it did not work.
document.querySelector('.labelRwanda').getBBox().x document.querySelector('.labelRwanda').getBBox().width;
document.querySelector('.labelCuba').getBBox().x document.querySelector('.labelCuba').getBBox().width;
document.querySelector('.labelNicaragua').getBBox().x document.querySelector('.labelNicaragua').getBBox().width;
document.querySelector('.labelChad').getBBox().x document.querySelector('.labelChad').getBBox().width;
Update
Based on @Michael Mullany's suggestion, I tried the following where I used getBoundingClientRect()
instead of getBBox()
so that I don't need to account for any transform coming from the parent element. But I still could not get it to do the job accurately
const parent = document.querySelector('svg').getBoundingClientRect().right;
const ele = document.querySelectorAll('.Label>text');
ele.forEach(
(a,i)=>{
const child = a.getBoundingClientRect().right;
const diff = parent-child;
(diff<0)?a.setAttribute('x',`${parseFloat(a.getAttribute('x')) diff}`):null
}
);
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720">
<rect width="1280" height="720" fill="black" stroke="bound" style="overflow: visible;"></rect>
<rect x="70" y="70" width="1120" height="600" fill="black"></rect>
<g style="transform: translate(70px, 70px);">
<g >
<path fill="none" stroke="white" opacity="1" d="M0,385.8783581344254L46.666666666666664,340.04110795732606L93.33333333333333,340.04110795732606L140,340.04110795732606L186.66666666666666,340.04110795732606L233.33333333333334,340.04110795732606L280,261.5473775717186L326.6666666666667,261.5473775717186L373.3333333333333,261.5473775717186L420,261.5473775717186L466.6666666666667,261.5473775717186L513.3333333333333,193.79191415980043L560,193.79191415980043L606.6666666666666,193.79191415980043L653.3333333333334,174.38265408552468L700,174.38265408552468L746.6666666666666,140.17685505574866L793.3333333333334,140.17685505574866L840,140.17685505574866L886.6666666666666,140.17685505574866L933.3333333333334,140.17685505574866L980,99.07632474477359L1026.6666666666665,99.07632474477359L1073.3333333333335,99.07632474477359L1120,97.28970086328088"></path>
<path fill="none" stroke="white" opacity="1
" d="M0,498.798228969007L46.666666666666664,498.798228969007L93.33333333333333,508.9184060721062L140,508.9184060721062M233.33333333333334,405.6265984654729L280,405.6265984654729L326.6666666666667,405.6265984654729L373.3333333333333,405.6265984654729L420,456.7774936061383L466.6666666666667,426.0869565217393L513.3333333333333,426.0869565217393L560,405.6265984654729L606.6666666666666,405.6265984654729L653.3333333333334,221.48337595907955L700,221.48337595907955L746.6666666666666,221.48337595907955L793.3333333333334,201.02301790281317L840,211.25319693094582L886.6666666666666,170.33248081841413L933.3333333333334,170.33248081841413L980,170.33248081841413L1026.6666666666665,180.56265984654772L1073.3333333333335,155.26826115061547L1120,124.24046541693643"></path>
<path fill="none" stroke="white" opacity="1" d="M0,577.4117647058824L46.666666666666664,577.4117647058824L93.33333333333333,577.4117647058824L140,577.4117647058824L186.66666666666666,577.4117647058824M280,545.3510436432637L326.6666666666667,539.2789373814043L373.3333333333333,539.2789373814043L420,539.2789373814043L466.6666666666667,551.4231499051233L513.3333333333333,551.4231499051233L560,551.4231499051233L606.6666666666666,551.4231499051233L653.3333333333334,479.8498122653327L700,459.8247809762202L746.6666666666666,459.8247809762202L793.3333333333334,459.8247809762202L840,459.8247809762202L886.6666666666666,459.8247809762202L933.3333333333334,479.8498122653327L980,456.43070787637083L1026.6666666666665,459.94397759103623L1073.3333333333335,454.75671750181556L1120,296.39468690702114"></path>
<path fill="none" stroke="white" opacity="1" d="M0,438.6554621848753L46.666666666666664,438.6554621848753L93.33333333333333,438.6554621848753L140,357.98319327731105L186.66666666666666,358.34658187599433L233.33333333333334,358.34658187599433L280,141.17647058823525L326.6666666666667,141.17647058823525L373.3333333333333,141.17647058823525L420,141.17647058823525L466.6666666666667,141.17647058823525L513.3333333333333,70.5882352941176L560,70.5882352941176L606.6666666666666,70.5882352941176L653.3333333333334,70.5882352941176L700,70.5882352941176L746.6666666666666,0L793.3333333333334,0L840,0L886.6666666666666,0L933.3333333333334,23.5294117647058L980,23.5294117647058L1026.6666666666665,23.5294117647058L1073.3333333333335,23.5294117647058L1120,23.5294117647058"></path>
</g>
<g style="opacity: 1;">
<text id="labelRwanda" text-anchor="left" fill="white" alignment-baseline="middle" x="1120" y="23.5294117647058" style="overflow: visible;">Rwanda:61.25%</text>
<text id="labelCuba" text-anchor="left" fill="white" alignment-baseline="middle" x="1120" y="97.28970086328088" style="overflow: visible;">Cuba:53.41%</text>
<text id="labelNicaragua" text-anchor="left" fill="white" alignment-baseline="middle" x="1120" y="124.24046541693643" style="overflow: visible;">Nicaragua:50.55%</text>
<text id="labelChad" text-anchor="left" fill="white" alignment-baseline="middle" x="1120" y="296.39468690702114" style="overflow: visible;">Chad:32.26%</text>
</g>
</g>
</svg>
</body>
</html>
CodePudding user response:
If you set the text-anchor attribute to "end" all the texts will align to the right. Would that be OK?
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720">
<rect width="1280" height="720" fill="black" stroke="bound"/>
<rect x="70" y="70" width="1120" height="600" fill="black"/>
<g transform="translate(70 70)">
<g >
<path fill="none" stroke="white" opacity="1" d="M0,385.8783581344254L46.666666666666664,340.04110795732606L93.33333333333333,340.04110795732606L140,340.04110795732606L186.66666666666666,340.04110795732606L233.33333333333334,340.04110795732606L280,261.5473775717186L326.6666666666667,261.5473775717186L373.3333333333333,261.5473775717186L420,261.5473775717186L466.6666666666667,261.5473775717186L513.3333333333333,193.79191415980043L560,193.79191415980043L606.6666666666666,193.79191415980043L653.3333333333334,174.38265408552468L700,174.38265408552468L746.6666666666666,140.17685505574866L793.3333333333334,140.17685505574866L840,140.17685505574866L886.6666666666666,140.17685505574866L933.3333333333334,140.17685505574866L980,99.07632474477359L1026.6666666666665,99.07632474477359L1073.3333333333335,99.07632474477359L1120,97.28970086328088"></path>
<path fill="none" stroke="white" opacity="1" d="M0,498.798228969007L46.666666666666664,498.798228969007L93.33333333333333,508.9184060721062L140,508.9184060721062M233.33333333333334,405.6265984654729L280,405.6265984654729L326.6666666666667,405.6265984654729L373.3333333333333,405.6265984654729L420,456.7774936061383L466.6666666666667,426.0869565217393L513.3333333333333,426.0869565217393L560,405.6265984654729L606.6666666666666,405.6265984654729L653.3333333333334,221.48337595907955L700,221.48337595907955L746.6666666666666,221.48337595907955L793.3333333333334,201.02301790281317L840,211.25319693094582L886.6666666666666,170.33248081841413L933.3333333333334,170.33248081841413L980,170.33248081841413L1026.6666666666665,180.56265984654772L1073.3333333333335,155.26826115061547L1120,124.24046541693643"></path>
<path fill="none" stroke="white" opacity="1" d="M0,577.4117647058824L46.666666666666664,577.4117647058824L93.33333333333333,577.4117647058824L140,577.4117647058824L186.66666666666666,577.4117647058824M280,545.3510436432637L326.6666666666667,539.2789373814043L373.3333333333333,539.2789373814043L420,539.2789373814043L466.6666666666667,551.4231499051233L513.3333333333333,551.4231499051233L560,551.4231499051233L606.6666666666666,551.4231499051233L653.3333333333334,479.8498122653327L700,459.8247809762202L746.6666666666666,459.8247809762202L793.3333333333334,459.8247809762202L840,459.8247809762202L886.6666666666666,459.8247809762202L933.3333333333334,479.8498122653327L980,456.43070787637083L1026.6666666666665,459.94397759103623L1073.3333333333335,454.75671750181556L1120,296.39468690702114"></path>
<path fill="none" stroke="white" opacity="1" d="M0,438.6554621848753L46.666666666666664,438.6554621848753L93.33333333333333,438.6554621848753L140,357.98319327731105L186.66666666666666,358.34658187599433L233.33333333333334,358.34658187599433L280,141.17647058823525L326.6666666666667,141.17647058823525L373.3333333333333,141.17647058823525L420,141.17647058823525L466.6666666666667,141.17647058823525L513.3333333333333,70.5882352941176L560,70.5882352941176L606.6666666666666,70.5882352941176L653.3333333333334,70.5882352941176L700,70.5882352941176L746.6666666666666,0L793.3333333333334,0L840,0L886.6666666666666,0L933.3333333333334,23.5294117647058L980,23.5294117647058L1026.6666666666665,23.5294117647058L1073.3333333333335,23.5294117647058L1120,23.5294117647058"></path>
</g>
<g style="opacity: 1;">
<text text-anchor="end" fill="white" alignment-baseline="middle" x="1120" y="23.5294117647058" style="overflow: visible;">Rwanda:61.25%</text>
<text text-anchor="end" fill="white" alignment-baseline="middle" x="1120" y="97.28970086328088" style="overflow: visible;">Cuba:53.41%</text>
<text text-anchor="end" fill="white" alignment-baseline="middle" x="1120" y="124.24046541693643" style="overflow: visible;">Nicaragua:50.55%</text>
<text text-anchor="end" fill="white" alignment-baseline="middle" x="1120" y="296.39468690702114" style="overflow: visible;">Chad:32.26%</text>
</g>
</g>
</svg>
CodePudding user response:
A modern (standard) Web Component <svg-linegraph>
can help.
To make data entry as easy as possible, we'll do them in HTML
<svg-linegraph>
<path d="mx y....">Cuba</path>
<path stroke="red" stroke-width="3" d="mx y....">Nicaragua</path>
</svg-linegraph>
But this means the <path>
Elements are now technically an UnknownHTMLElement
, and not an SVG path.
This is easily fixed by reading all of these <path>
and adding the outerHTML to an existing <svg>
(that will add the paths in the correct SVG NameSpace)
While parsing all <path>
we can set the user defined attributes or defaults.
We are still lazy... Those <path>
can be in any sized viewPort
.
So in one more step we parse the, now valid SVG!!, paths again, get the boundaries of each path, and thus automagically calculate the correct viewPort
Positioning the country label is easy, we get the last point in each <path>
and position a <text>
on the correct X,Y location. (and gave the viewPort an extra 100 units to draw the labels right of the chart)
All code required to create a <svg-linegraph>
Web Component is:
Note: I simplified your original d-path
values with: https://yqnn.github.io/svg-path-editor
You can, ofcourse, use any valid d-path
<script>
customElements.define("svg-linegraph", class extends HTMLElement {
connectedCallback() {
setTimeout(() => { // ensure <svg-linegraph> innerHTML is parsed
this.innerHTML = `<svg style="background:grey">${
[...this.querySelectorAll("path")].map(path => {
const cloneAttribute= (x,v)=> path.setAttribute(x, path.getAttribute(x)||v);
cloneAttribute("fill", "none"); // prefer user defined settings
cloneAttribute("stroke", "black");
cloneAttribute("stroke-width", "2");
return path.outerHTML;
}).join("")}<g>COUNTRYLABELS GO HERE</g></svg>`;
let xaxis=[], yaxis=[]; // find viewPort boundaries
this.querySelector("g").innerHTML = [...this.querySelectorAll("path")].map(path => {
let {x:labelX,y:labelY} = path.getPointAtLength(path.getTotalLength()); // last path point
let {x,y,width,height} = path.getBBox();
xaxis.push(x,x width); yaxis.push(y,y height);
return `<text fill="red" stroke="black" font-family="Arial" font-size="20px" alignment-baseline="middle"
x="${labelX 5}" y="${labelY}">${path.innerHTML}</text>`;
}).join("");
xaxis.sort((a,b)=>a-b); yaxis.sort((a,b)=>a-b); // sort by number!
let viewbox = `${xaxis.at(0)} ${yaxis.at(0)-10} ${xaxis.at(-1) 100} ${yaxis.at(-1)}`;
this.querySelector("svg").setAttribute("viewBox",viewbox);
})
}
})
</script>
<svg-linegraph>
<path d="m0 386 47-46 46 0 47 0 47 0 46 0 47-78 47 0 46 0 47 0 47 0 46-68 47 0 47 0 46-20 47 0 47-34 46 0 47 0 47 0 46 0 47-41 47 0 46 0 47-2">Cuba</path>
<path stroke="red" stroke-width="3" d="m0 499 47 0 46 10 47 0m93-103 47 0 47 0 46 0 47 51 47-31 46 0 47-20 47 0 46-185 47 0 47 0 46-20 47 10 47-41 46 0 47 0 47 11 46-26 47-31">Nicaragua</path>
<path stroke="red" stroke-width="4" d="m0 577 47 0 46 0 47 0 47 0m93-32 47-6 46 0 47 0 47 12 46 0 47 0 47 0 46-71 47-20 47 0 46 0 47 0 47 0 46 20 47-24 47 4 46-5 47-159">Chad</path>
<path d="m0 439 47 0 46 0 47-81 47 0 46 0 47-217 47 0 46 0 47 0 47 0 46-70 47 0 47 0 46 0 47 0 47-71 46 0 47 0 47 0 46 24 47 0 47 0 46 0 47 0">Rwanda</path>
</svg-linegraph>