Davit Mkrtchyan
Davit Mkrtchyan

Reputation: 469

Konva.TextPath doesn't render whole text

Good day, reader.

I am using Konva.TextPath, to create curved text. I am calculating path based on text length and angle it should be curved(ex. from 270 to 90 is half curved , 359 from 0, is nearly the circle it self). When I pass path to Konva.Path, it draws perfectly, but with TextPath (in some cases ) it renders with without last letters, and with wrong letter spacings. I have seen this issue on github, but I am not sure if this is same issue.

You can see attached pictures, with it's paths You can find calculations on codesandbox

If more info needed please ask.

Thanks.

Path 1
M6.587352859507078 6.779702002611884,
A665.1292665214339, 665.1292665214339, 0, 0, 0, 271.78815041484745 4.465329704342594 

Path 1 :

Path 2
M8.968283494812425 14.597383097232182,
A993.126844893427, 993.126844893427, 0, 0, 0, 268.2165426505044 12.334957814595896

Path 2 :

Upvotes: 0

Views: 481

Answers (2)

Vanquished Wombat
Vanquished Wombat

Reputation: 9535

This is not an answer but provides some insight into a potential DIY solution in case you need it.

Generically, the algorithm for text on a path is conceptually simple but a bit of a hassle to implement. Konva gives you the same facilities as raw HTML5 canvas commands in respect to this task so there is no need to abandon Konva.

The concept is

  1. there is a path and a string;
  2. get the straight-line placement of the chars in the string (accounts for kerning pairs to give pleasing spacing) - easily achieved;
  3. find the position on the path where each character should start and end - not so easy;
  4. draw text.

The complex step is 3 because there is no way to predict the path. It 'could' be a straight line, or a circle, or an ellipse, but you have to assume an unpredictable mad jagged loopy thing.

I am ignoring any alignment or extra character padding to keep things simple.

For each character , you will know the starting point (x, y) to draw - this is the start of the path for the first character , and the end point of the previous character for every subsequent character in the string. Knowing the starting point is good, now you have to find the end point of the character space.

A quick discussion of the path.getPointAtLength() method is required now. The canvas knows the length of any path you ask it to draw, and you can ask it the (x, y) point for any distance along that path (The Konva source has code for this - it's an interesting read!). So, given a path of length of say 100, you can ask for the (x, y) point at length 42 with path.getPointAtLength(42) and Konva will tell you.

So what ? We can use that to go fishing for the character end point on the curve.

You will need an algorithm that does this:

  1. take an initial guess at a length - something like length at character start + this character width is reasonable.
  2. Ask for the point at that position on the path.
  3. Compute the point-point straight-line length between character start and that point.
  4. If the path is curved / bent then this length will be < character width and you need to try again with a longer distance, if you overshoot then go slightly shorter.
  5. Repeat steps 2-4, adjusting length by increments each time.

What you want is a binary chop approach so that your steps reduce each time thus reaching perfection at some point. Although you should define a tolerance so as to give up when a close-enough point is found.

Remember, what you are doing here is feeling for the point on the path where the end-point of the character you want to draw will look reasonable.

Once you have the character positions you do a little more math to work out the rotation angle and top-left position of each character. You can then output the text.

If you need alignment or character spacing you can add those features quite easily once you have the main algorithm running.

There's a similar blog post on my blog.

Have fun.

Upvotes: 2

lavrton
lavrton

Reputation: 20363

Look like the rendering logic of Konva.TextPath is just not perfect, so it has inconsistent letter spacings.

About the missing last letter. You are calculating text width as

const textLength =
      textNode.getTextWidth() +
      (textNode.text().length - 1) * textNode.letterSpacing();

That is a correct solution for straight Konva.Text, but Konva.Path is using approximations when rendering over the specified path. So resulted text may take a larger length. So there is no space for the last letter. As workaround you can slightly increase textLength. For example by adding textNode.fontSize().

Upvotes: 0

Related Questions