I am working with an svg text
element and I need to create separate <text></text>
element per character of a particular word, Lorem
in this case.
For example,
const svg = document.querySelector("svg");
const svgns = "http://www.w3.org/2000/svg"
//vboxDim
var vboxW = 200;
var vboxH = 200;
//assigning svg element attribute
svg.setAttribute('class', 'layer1');
svg.setAttribute('xmlns', svgns);
svg.setAttribute('viewBox', `0 0 ${vboxW} ${vboxH}`);
//make background
var fill1 = '#F1C40F';
let bg = document.createElementNS(svgns, 'rect');
bg.setAttribute('class', 'bg');
bg.setAttribute('id', 'bg');
bg.setAttribute("width", `${vboxW}`);
bg.setAttribute("height", `${vboxH}`);
bg.setAttribute("fill", fill1);
svg.appendChild(bg);
var txtArray1 = ['L', 'o', 'r', 'e', 'm'];
var textX = svg.viewBox.baseVal.width / 2;
var textY = svg.viewBox.baseVal.height / 2;
for (var i = 0; i < txtArray1.length; i ) {
let text1 = document.createElementNS(svgns, "text")
text1.setAttribute('class', 't0' i)
text1.setAttribute('id', 't0' i);
text1.setAttribute('x', textX);
text1.setAttribute('y', textY);
text1.setAttribute('fill','green');
text1.setAttribute('dominant-baseline', 'middle'); //positions text in the middle
text1.setAttribute('text-anchor', 'middle'); //positions text in the middle
text1.textContent = txtArray1[i];
svg.appendChild(text1);
var el = document.getElementById(`t0${i}`);
var width = el.getBBox().width;
textX = textX width;
}
let text2 = document.createElementNS(svgns, "text")
text2.setAttribute('class', 'word')
text2.setAttribute('id', 'word');
text2.setAttribute('x', (svg.viewBox.baseVal.width / 2));
text2.setAttribute('y', (svg.viewBox.baseVal.height / 2) - 50);
text2.setAttribute('fill','brown');
text2.setAttribute('dominant-baseline', 'middle'); //positions text in the middle
text2.setAttribute('text-anchor', 'middle'); //positions text in the middle
text2.textContent = 'Lorem';
svg.appendChild(text2);
<!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>
<style></style>
</head>
<body>
<svg>
<script href="index.js"></script>
</svg>
</body>
</html>
I want these individual characters to be aligned if they were created as a word
While designing, my initial idea was to get the BBox().width
of the immediately created character and increase the x
of the next element by the same value. But the final element is nowhere closer to element in .
What is the best strategy to create individual text elements and yet align them (green color) as they would appear in a sentence (brown color) ?
Edit
I have tried what @Robert Longson suggested. I am not sure if it was done wrongly, but it is not accurate.
const svg = document.querySelector("svg");
const svgns = "http://www.w3.org/2000/svg"
//vboxDim
var vboxW = 200;
var vboxH = 200;
//assigning svg element attribute
svg.setAttribute('class', 'layer1');
svg.setAttribute('xmlns', svgns);
svg.setAttribute('viewBox', `0 0 ${vboxW} ${vboxH}`);
//make background
var fill1 = '#F1C40F';
let bg = document.createElementNS(svgns, 'rect');
bg.setAttribute('class', 'bg');
bg.setAttribute('id', 'bg');
bg.setAttribute("width", `${vboxW}`);
bg.setAttribute("height", `${vboxH}`);
bg.setAttribute("fill", fill1);
// append the new rectangle to the svg
svg.appendChild(bg);
var txtArray1 = ['L', 'o', 'r', 'e', 'm'];
var textX = svg.viewBox.baseVal.width / 2;
var textY = svg.viewBox.baseVal.height / 2;
let text2 = document.createElementNS(svgns, "text")
text2.setAttribute('class', 'word')
text2.setAttribute('id', 'word');
text2.setAttribute('x', (svg.viewBox.baseVal.width / 2));
text2.setAttribute('y', (svg.viewBox.baseVal.height / 2) - 50);
text2.setAttribute('fill','brown');
text2.setAttribute('dominant-baseline', 'middle'); //positions text in the middle
text2.setAttribute('text-anchor', 'middle'); //positions text in the middle
text2.textContent = 'Lorem';
svg.appendChild(text2);
for (var i = 0; i < txtArray1.length; i ) {
var x = document.getElementById('word');
let text1 = document.createElementNS(svgns, "text")
text1.setAttribute('class', 't0' i)
text1.setAttribute('id', 't0' i);
text1.setAttribute('x', x.getStartPositionOfChar(i).x);
text1.setAttribute('y', x.getStartPositionOfChar(i).y);
text1.setAttribute('fill','green');
text1.setAttribute('dominant-baseline', 'middle'); //positions text in the middle
text1.setAttribute('text-anchor', 'middle'); //positions text in the middle
text1.textContent = txtArray1[i];
svg.appendChild(text1);
}
text2.setAttribute('y', 75);
<!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>
<style></style>
</head>
<body>
<link rel="stylesheet" href="style.css">
</link>
<svg>
<script href="index.js"></script>
</svg>
</body>
</html>
CodePudding user response:
Call getStartPositionOfChar to find out where each character is.
When you make the copy you don't need to set text-anchor or dominant-baseline because the original character positions already embody that.
const svg = document.querySelector("svg");
const svgns = "http://www.w3.org/2000/svg"
//vboxDim
var vboxW = 200;
var vboxH = 200;
//assigning svg element attribute
svg.setAttribute('class', 'layer1');
svg.setAttribute('xmlns', svgns);
svg.setAttribute('viewBox', `0 0 ${vboxW} ${vboxH}`);
//make background
var fill1 = '#F1C40F';
let bg = document.createElementNS(svgns, 'rect');
bg.setAttribute('class', 'bg');
bg.setAttribute('id', 'bg');
bg.setAttribute("width", `${vboxW}`);
bg.setAttribute("height", `${vboxH}`);
bg.setAttribute("fill", fill1);
// append the new rectangle to the svg
svg.appendChild(bg);
var txtArray1 = ['L', 'o', 'r', 'e', 'm'];
var textX = svg.viewBox.baseVal.width / 2;
var textY = svg.viewBox.baseVal.height / 2;
let text2 = document.createElementNS(svgns, "text")
text2.setAttribute('class', 'word')
text2.setAttribute('id', 'word');
text2.setAttribute('x', (svg.viewBox.baseVal.width / 2));
text2.setAttribute('y', (svg.viewBox.baseVal.height / 2) - 50);
text2.setAttribute('fill','brown');
text2.setAttribute('dominant-baseline', 'middle'); //positions text in the middle
text2.setAttribute('text-anchor', 'middle'); //positions text in the middle
text2.textContent = 'Lorem';
svg.appendChild(text2);
for (var i = 0; i < txtArray1.length; i ) {
var x = document.getElementById('word');
let text1 = document.createElementNS(svgns, "text")
text1.setAttribute('class', 't0' i)
text1.setAttribute('id', 't0' i);
text1.setAttribute('x', x.getStartPositionOfChar(i).x);
text1.setAttribute('y', x.getStartPositionOfChar(i).y);
text1.setAttribute('fill','green');
text1.textContent = txtArray1[i];
svg.appendChild(text1);
}
text2.setAttribute('y', 75);
<!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>
<style></style>
</head>
<body>
<link rel="stylesheet" href="style.css">
</link>
<svg>
<script href="index.js"></script>
</svg>
</body>
</html>
CodePudding user response:
if you use display: inline-flex on the parent container all child elements will be aligned in one row. if you now set justify-content: flex-start and align-items:center they'll be horizontally aligned to the left and vertically centered. if you add a margin-right to .word you'll get spaces between words. Additionally you can play around with flex-flow: row wrap for automatic line breaks at the end