'How to place equally spaced svg text in order
I have a svg element generated with javascript, that looks like this
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720">
<rect />
<text >
<tspan >this is line 1 test 1</tspan>
</text>
<text >
<tspan>this is line 2 test 2</tspan>
</text>
<text>
<tspan>this is line 3 test 3</tspan>
</text>
</svg>
I am trying to place each tspan in the equal distance for asethetics. Currently I am doing this with the following js
// select svg
const svg = document.querySelector("svg");
// variable for the namespace
const svgns = "http://www.w3.org/2000/svg"
//make background
var fill1 = "#e6e6e6";
let bg = document.createElementNS(svgns, 'rect');
bg.setAttribute("class", "bg");
bg.setAttribute("id", "bg");
bg.setAttribute("width", "1280");
bg.setAttribute("height", "720");
bg.setAttribute("fill", fill1);
// append the new rectangle to the svg
svg.appendChild(bg);
/*line1 start */
var size = 15; //change only for the lines
var content1 = 'this is line 1 test 1'; //change as per the lines
var text1 = document.createElementNS(svgns, "text")
text1.setAttribute("class", "t1")
text1.setAttribute("id", "t1");
text1.setAttribute("font-size", size.toString());
text1.setAttribute("font-family", "Monospace");
var constX = 10.23;
var constY = 135.05;
var tspan1 = document.createElementNS(svgns, "tspan");
tspan1.setAttribute("class", "tsp1");
tspan1.setAttribute("id", "tsp1");
tspan1.setAttribute("x", constX.toString());
tspan1.setAttribute("y", constY.toString());
tspan1.textContent = content1;
text1.appendChild(tspan1);
svg.appendChild(text1);
/*line1 finish */
/*line2 start */
var size = 15; //change only for the lines
var content1 = 'this is line 2 test 2'; //change as per the lines
var text1 = document.createElementNS(svgns, "text")
text1.setAttribute("class", "t2")
text1.setAttribute("id", "t2");
text1.setAttribute("font-size", size.toString());
text1.setAttribute("font-family", "Monospace");
var constX = 10.23;
var el = document.getElementById('tsp1');
var constY = el.getExtentOfChar(0).y;
var tspan1 = document.createElementNS(svgns, "tspan");
tspan1.setAttribute("class", "tsp2");
tspan1.setAttribute("id", "tsp2");
tspan1.setAttribute("x", constX.toString());
tspan1.setAttribute("y", constY.toString());
tspan1.textContent = content1;
text1.appendChild(tspan1);
svg.appendChild(text1);
/*line2 finish */
/*line3 start */
var size = 15; //change only for the lines
var content1 = 'this is line 3 test 3'; //change as per the lines
var text1 = document.createElementNS(svgns, "text")
text1.setAttribute("class", "t3")
text1.setAttribute("id", "t3");
text1.setAttribute("font-size", size.toString());
text1.setAttribute("font-family", "Monospace");
var constX = 10.23;
var el = document.getElementById('tsp2');
var constY = el.getExtentOfChar(0).y;
var tspan1 = document.createElementNS(svgns, "tspan");
tspan1.setAttribute("class", "tsp3");
tspan1.setAttribute("id", "tsp3");
tspan1.setAttribute("x", constX.toString());
tspan1.setAttribute("y", constY.toString());
tspan1.textContent = content1;
text1.appendChild(tspan1);
svg.appendChild(text1);
/*line3 finish */
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720">
</svg>
But the order of these lines are reversed. How can I achieve equal spacing with the correct order?
The following piece is generating the y
of the tspan
var el = document.getElementById('tsp2');
var constY = el.getExtentOfChar(0).y;
Solution 1:[1]
Well this is kind of a weird way to place SVG text - normally you just specify x and y on the text element - but I'm sure you have your reasons.
The problem here is that you're using the y attribute returned from getExtentOfChar - which is giving you the top of the bounding box of the previous tspan and using that as your y for the next tspan. But the y for a tspan sets the BASELINE position, not the bounding box top - so that tspan is being painted above the previous one. You can fix this by adding the height attribute (or for better spacing - 2x the height attribute returned from getExtentOfChar) - like so.
// select svg
const svg = document.querySelector("svg");
// variable for the namespace
const svgns = "http://www.w3.org/2000/svg"
//make background
var fill1 = "#e6e6e6";
let bg = document.createElementNS(svgns, 'rect');
bg.setAttribute("class", "bg");
bg.setAttribute("id", "bg");
bg.setAttribute("width", "1280");
bg.setAttribute("height", "720");
bg.setAttribute("fill", fill1);
// append the new rectangle to the svg
svg.appendChild(bg);
/*line1 start */
var size = 15; //change only for the lines
var content1 = 'this is line 1 test 1'; //change as per the lines
var text1 = document.createElementNS(svgns, "text")
text1.setAttribute("class", "t1")
text1.setAttribute("id", "t1");
text1.setAttribute("font-size", size.toString());
text1.setAttribute("font-family", "Monospace");
var constX = 10.23;
var constY = 135.05;
var tspan1 = document.createElementNS(svgns, "tspan");
tspan1.setAttribute("class", "tsp1");
tspan1.setAttribute("id", "tsp1");
tspan1.setAttribute("x", constX.toString());
tspan1.setAttribute("y", constY.toString());
tspan1.textContent = content1;
text1.appendChild(tspan1);
svg.appendChild(text1);
/*line1 finish */
/*line2 start */
var size = 15; //change only for the lines
var content1 = 'this is line 2 test 2'; //change as per the lines
var text1 = document.createElementNS(svgns, "text")
text1.setAttribute("class", "t2")
text1.setAttribute("id", "t2");
text1.setAttribute("font-size", size.toString());
text1.setAttribute("font-family", "Monospace");
var constX = 10.23;
var el = document.getElementById('tsp1');
var constY = el.getExtentOfChar(0).y + 2 * el.getExtentOfChar(0).height;
console.log(constY);
var tspan1 = document.createElementNS(svgns, "tspan");
tspan1.setAttribute("class", "tsp2");
tspan1.setAttribute("id", "tsp2");
tspan1.setAttribute("x", constX.toString());
tspan1.setAttribute("y", constY.toString());
tspan1.textContent = content1;
text1.appendChild(tspan1);
svg.appendChild(text1);
/*line2 finish */
/*line3 start */
var size = 15; //change only for the lines
var content1 = 'this is line 3 test 3'; //change as per the lines
var text1 = document.createElementNS(svgns, "text")
text1.setAttribute("class", "t3")
text1.setAttribute("id", "t3");
text1.setAttribute("font-size", size.toString());
text1.setAttribute("font-family", "Monospace");
var constX = 10.23;
var el = document.getElementById('tsp2');
var constY = el.getExtentOfChar(0).y + 2 * el.getExtentOfChar(0).height;
console.log(constY);
var tspan1 = document.createElementNS(svgns, "tspan");
tspan1.setAttribute("class", "tsp3");
tspan1.setAttribute("id", "tsp3");
tspan1.setAttribute("x", constX.toString());
tspan1.setAttribute("y", constY.toString());
tspan1.textContent = content1;
text1.appendChild(tspan1);
svg.appendChild(text1);
/*line3 finish */
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1280 720">
</svg>
Solution 2:[2]
or dump all svga JavaScript (says the person who always includes JS in his answers)
and do it with SVG keyPoints?
<svg viewBox="0 0 200 50">
<path id="vline" fill="none" stroke="red" d="m10,0v50"></path>
<text>this is line 1
<animateMotion dur="0.001s" fill="freeze" keyPoints="0;0.25" keyTimes="0;1" calcMode="linear">
<mpath href="#vline"></mpath>
</animateMotion>
</text>
<text>this is line 2
<animateMotion dur="0.001s" fill="freeze" keyPoints="0;.5" keyTimes="0;1" calcMode="linear">
<mpath href="#vline"></mpath>
</animateMotion>
</text>
<text>this is line 3
<animateMotion dur="0.001s" fill="freeze" keyPoints="0;.75" keyTimes="0;1"calcMode="linear">
<mpath href="#vline"></mpath>
</animateMotion>
</text>
</svg>
and add https://developer.mozilla.org/en-US/docs/Web/SVG/Attribute/dominant-baseline for better alignment
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | Michael Mullany |
Solution 2 |