Reputation: 412
I have an SVG exported from Adobe Illustrator with several paths like this, which produces a small polygon I intend to use as a text box
<svg viewbox="387 390 74 20">
<g>
<path class="st37" d="M452,408h-56c-4.42,0-8-3.58-8-8l0,0c0-4.42,3.58-8,8-8h56c4.42,0,8,3.58,8,8l0,0 C460,404.42,456.42,408,452,408z" />
</g>
</svg>
I'd like to dynamically add text to it. I've seen many similar questions here, but most of them involed specifying a x and y property for a text element based on the x and y property the path element. My path, however, does not have such properties.
I've tried to use a textPath element with xlink:href pointing to my path. I gets attached to the path, but the text wraps my path element instead of being inside it.
So, is there a way to achieve this? I'm open to different solutions here. My dream would be to use javascript to get the path element and add the text from there. But I could also edit the base SVG file to add a text or any other relevant element and attributes to make this work, as long as I can change the text dynamically from javascript later. And since this SVG is produced by Illustrator, I could also try different export options there in order to get a proper output for my goal.
Upvotes: 7
Views: 8753
Reputation: 21271
Since you can edit your base SVG align
create a proper SVG to work with
path
is a background label starting (red circle) at a large offset x=452 y=408viewBox="0 0 80 20"
after that use JavaScript to add text dynamically.
No need for text x,y calculations, pathLength
and startoffset
do the work
Or if you go fancy you can create the blue line dynamically from getBBox()
That will also work with your current path; just more calculations required
It is all about coordinates,
and positioning the blue line (with stroke="transparent" then):
playground:
<svg viewbox="387 390 74 20">
<path fill="lightgreen" d="M452,408h-56c-4.42,0-8-3.58-8-8l0,0c0-4.42,3.58-8,8-8h56c4.42,0,8,3.58,8,8l0,0C460,404.42,456.42,408,452,408z" />
<circle cx="452" cy="408" r="2" fill="red"/>
<circle cx="388" cy="400" r="2" fill="green"/>
<path id="P" pathLength="100" d="M388 400h72" stroke="blue"/>
<text>
<textPath href="#P" startoffset="50" text-anchor="middle" dominant-baseline="middle"
fill="green" font-size="14px">My Text</textPath>
</text>
</svg>
Upvotes: 3
Reputation: 412
Thanks for the answers! I end up using a tweaked version of Paul LeBeau's function to take into account the structure suggested by Danny '365CSI' Engelman so I don't have to use translate to center the text vertically.
let label = document.querySelector("#mylabel");
addLabelTextPath(label, "Something");
function addLabelTextPath(bgPath, labelText) {
let bbox = bgPath.getBBox();
let x = bbox.x + bbox.width / 2;
let y = bbox.y + bbox.height / 2;
// Create the path line
let pathElem = document.createElementNS(bgPath.namespaceURI, "path");
pathElem.setAttribute("pathLength", 100);
pathElem.setAttribute("d", `M${bbox.x} ${y}h${bbox.width}`);
pathElem.id = `baseline-${bgPath.id}`;
// Create a <text> element
let textElem = document.createElementNS(bgPath.namespaceURI, "text");
// Create a <textPath> element
let textPath = document.createElementNS(bgPath.namespaceURI, "textPath");
textPath.setAttribute("href", `#${pathElem.id}`);
//Center text
textPath.setAttribute("dominant-baseline", "Middle");
textPath.setAttribute("startOffset", 50);
textPath.setAttribute("text-anchor", "middle");
// Give it a class that will determine the text size, colour, etc
textPath.classList.add("label-text");
// Set the text
textPath.textContent = labelText;
// Add the elements directly after the label background path
bgPath.after(pathElem);
pathElem.after(textElem);
textElem.appendChild(textPath);
}
.st37 {
fill: lightblue;
}
.label-text {
font-size: 10px;
fill: darkblue;
}
<svg viewbox="387 390 74 20">
<g>
<path id="mylabel" class="st37" d="M452,408h-56c-4.42,0-8-3.58-8-8l0,0c0-4.42,3.58-8,8-8h56c4.42,0,8,3.58,8,8l0,0 C460,404.42,456.42,408,452,408z" />
</g>
</svg>
Upvotes: 1
Reputation: 101938
Here's some sample code that takes a label path and adds a <text>
element after it with whatever text you choose.
let label1 = document.querySelector("#label1");
addLabelText(label1, "Something");
function addLabelText(bgPath, labelText)
{
let bbox = bgPath.getBBox();
let x = bbox.x + bbox.width / 2;
let y = bbox.y + bbox.height / 2;
// Create a <text> element
let textElem = document.createElementNS(bgPath.namespaceURI, "text");
textElem.setAttribute("x", x);
textElem.setAttribute("y", y);
// Centre text horizontally at x,y
textElem.setAttribute("text-anchor", "middle");
// Give it a class that will determine the text size, colour, etc
textElem.classList.add("label-text");
// Set the text
textElem.textContent = labelText;
// Add this text element directly after the label background path
bgPath.after(textElem);
}
.st37 {
fill: linen;
}
.label-text {
font-size: 10px;
fill: rebeccapurple;
transform: translate(0, 3px); /* adjust vertical position to centre text */
}
<svg viewbox="387 390 74 20">
<g>
<path id="label1" class="st37" d="M452,408h-56c-4.42,0-8-3.58-8-8l0,0c0-4.42,3.58-8,8-8h56c4.42,0,8,3.58,8,8l0,0 C460,404.42,456.42,408,452,408z" />
</g>
</svg>
Upvotes: 4