SVG - create path for textpath dynamically?

Viewed 1102

I want to have a curved text along a path (half circle) in SVG. I have followed this tutorial, which is great: https://css-tricks.com/snippets/svg/curved-text-along-path/

The problem is that the path presented there works only for this specific text - Dangerous Curves Ahead. If you leave only Dangerous word that's what happens: https://codepen.io/anon/pen/pqqVGa - it no longer works (the text is no more evenly spreaded across the path).

I want to have it work regardless of text length. How to achieve that?

2 Answers

Using the attributes lengthAdjust and textLength you can adjust the length of the text and the height of the letters, thereby placing the text of the desired length on a segment of a fixed length

<svg id="svg1" version="1.1" xmlns="http://www.w3.org/2000/svg" 
    xmlns:xlink="http://www.w3.org/1999/xlink"  width="500" height="300" viewBox="0 0 500 300">  

<path id="path1" fill="none" stroke="black" d="M30,151 Q215,21 443,152 " /> 

<text id="txt1" lengthAdjust="spacingAndGlyphs" textLength="400" font-size="24">
<textPath id="result" method="align" spacing="auto" startOffset="1%" xlink:href="#path1">
<tspan dy="-10"> very long text very long text very long text </tspan>

</textPath>
</text>
 
</svg>

Using the attribute startOffset =" 10% " you can adjust the position of the first character of the phrase

<svg id="svg1" version="1.1" xmlns="http://www.w3.org/2000/svg" 
    xmlns:xlink="http://www.w3.org/1999/xlink"  width="500" height="300" viewBox="0 0 500 300" >  

<path id="path1" fill="none" stroke="black" d="M30,151 Q215,21 443,152 " /> 

<text id="txt1" lengthAdjust="spacingAndGlyphs" textLength="400" font-size="24">
<textPath id="result" method="align" spacing="auto" startOffset="15%" xlink:href="#path1">
<tspan dy="-10"> very long text very long text very long text </tspan>

</textPath>
</text>
 
</svg>

and make animation using this attribute (click canvas)

<svg id="svg1" version="1.1" xmlns="http://www.w3.org/2000/svg" 
    xmlns:xlink="http://www.w3.org/1999/xlink"  width="500" height="300" viewBox="0 0 500 300">  

<path id="path1" fill="none" stroke="black" d="M30,151 Q215,21 443,152 " /> 

<text id="txt1" lengthAdjust="spacingAndGlyphs" textLength="200" font-size="24">
 <textPath id="result" method="align" spacing="auto" startOffset="-100%" xlink:href="#path1">
    <tspan dy="-10"> Very long text Very long text Very long text </tspan>
  <animate
   begin="svg1.click"
   dur="15s"
   attributeName="startOffset"
   values="-100%;1%;1%;100%;1%;1%;-100%"
   repeatCount="5"/> 
 </textPath>
</text>
 
  <text x="200" y="150" font-size="24" fill="orange" >Click me </text>
 
</svg>  

This is assuming that the initial text size (35) is too small.

let curveLength = curve.getTotalLength();
let fs = 35;//the initial font size
test.setAttributeNS(null, "style", `font-size:${fs}px`)
while(test.getComputedTextLength() < curveLength){
  fs++
  test.setAttributeNS(null, "style", `font-size:${fs}px`)
}
body {
  background-color: #333;
}

text {
  fill: #FF9800;
}
<svg viewBox="0 0 500 500">
    <path id="curve" d="M73.2,148.6c4-6.1,65.5-96.8,178.6-95.6c111.3,1.2,170.8,90.3,175.1,97" />
    <text id="test">
      <textPath xlink:href="#curve">
        Dangerous
      </textPath>
      </text>
  </svg>

UPDATE

The OP is commenting:

Thanks for the response. Instead of adjusting the font size, I would prefer to create a new path that is longer / smaller and matches the text width. Not sure how to do this tho. – feerlay

Please read the comments in the code. In base of the length of the text I'm calculating the new path, but I'm assuming a lot of things: I'm assuming the new path starts in the same point as the old one.

let textLength = test.getComputedTextLength();
// the center of the black circle
let c = {x:250,y:266}
// radius of the black circle
let r = 211;
// the black arc starts at point p1
let p1 = {x:73.2,y:150}
// the black arc ends at point p2
let p2 = {x:426.8,y:150}
// distance between p1 and p2
let d = dist(p1, p2);
// the angle of the are begining at p1 and ending at p2
let angle = Math.asin(.5*d/r);

// the radius of the new circle
let newR = textLength / angle; 
// the distance between p1 and the new p2
let newD = 2 * Math.sin(angle/2) * newR;
// the new attribute c for the path #curve
let D = `M${p1.x},${p1.y} A`
D += `${newR}, ${newR} 0 0 1 ${p1.x + newD},${p1.y} `
document.querySelector("#curve").setAttributeNS(null,"d",D);
// a function to calculate the distance between two points
function dist(p1, p2) {
  let dx = p2.x - p1.x;
  let dy = p2.y - p1.y;
  return Math.sqrt(dx * dx + dy * dy);
}
body {
  background-color: #333;
}

text {
  fill: #FF9800;
}; 
<svg viewBox="0 0 500 500">
    <path id="black_circle" d="M73.2,148.6c4-6.1,65.5-96.8,178.6-95.6c111.3,1.2,170.8,90.3,175.1,97" />
  
  <path id ="curve"  d="M73.2,150 A 211,211 0 0 1 426.8,150" fill="#777" />
    <text id="test">
      <textPath xlink:href="#curve">
        Dangerous curves
      </textPath>
      </text>

  
  </svg>

Related