Raphael JS 沿路径排列文字

8

我正在寻找一个例子或对一个概念的确认。希望在应用程序中使用Raphael JS,并希望能够像Illustrator等图形设计应用程序一样扭曲文本。

沿路径扭曲的文本示例


1
这是来自2010年的内容:https://dev59.com/NE3Sa4cB1Zd3GeqPsRgI - CM Kanode
谢谢CM Kanode。我在搜索中早就找到了这个。它没有展示将文本定位到路径的概念。我在这里非常懒,您认为将文本定位到路径只是一些简单的数学问题,还是您提供的示例证明了在Raphael中定位会成为一个问题? - ablemike
看看这个fork - 作者声称他添加了textPath支持。不过只针对SVG。 - Andrei
我没有深入研究它。只是简要地查看了这个概念,似乎很不错。初步看来,我倾向于认为它需要大量手动调整。希望Andrei提到的分支提供了您要寻找的解决方案。 - CM Kanode
我曾经使用Raphael,仅使用printpath.getPointAtLength完成了类似的事情。我记得alpha属性存在一个错误,当路径通过PI/2时必须进行更正,但它非常实用。只是为了澄清:单个字符没有倾斜;倾斜是通过整个字符的平移和旋转来完成的。 - Kevin Nielsen
2个回答

7
这是Chris Wilson的代码改编后的一个函数,具有以下特点:
  • 支持IE8 / VML模式Gecko / Firefox支持 (通过定义旋转原点 - 否则,IE8和Firefox会疯狂地将文本扔到页面上)
  • 在Gecko浏览器中使文字不那么丑陋(例如,Firefox),否则它们会任意增加字母间距
  • 支持手动定义字体大小和字母间距,以及动态“填充路径”的大小和间距
  • 支持手动字距调整(逐个字符微调字母间距)。路径上的文本经常会创建非常丑陋的字母空格;这允许您定义手动调整来修复这些问题,方法为:
    • 字符串中的数字位置,或者
    • 字符,或者
    • 字符对(应用于基于前面字符的字符实例,例如下面的示例紧缩“ae”对,加宽“rn”对)

JSBIN DEMO

function textOnPath( message, path, fontSize, letterSpacing, kerning, geckoKerning) {
    // only message and path are required, other args are optional
    // if fontSize or letterSpacing are undefined, they are calculated to fill the path
    // 10% of fontSize is usually good for manual letterspacing

    // Gecko, i.e. Firefox etc, inflates and alters the letter spacing
    var gecko = /rv:([^\)]+)\) Gecko\/\d{8}/.test(navigator.userAgent||'') ? true : false;

    var letters = [], places = [], messageLength = 0;
    for (var c=0; c < message.length; c++) {
        var letter = paper.text(0, 0, message[c]).attr({"text-anchor" : "middle"});
        letters.push(letter);

        if (kerning) {
            if(gecko && geckoKerning) {
                kerning = geckoKerning;
            }
            var character = letter.attr('text'), kern = 0;
            var predecessor = letters[c-1] ? letters[c-1].attr('text') : '';

            if (kerning[c]) {
                kern = kerning[c];
            } else if (kerning[character]) {
                if( typeof kerning[character] === 'object' ) {
                    kern = kerning[character][predecessor] || kerning[character]['default'] || 0;
                } else {
                    kern = kerning[character];
                }
            }
            if(kerning['default'] ) {
                kern = kern + (kerning['default'][predecessor] || 0);
            }            
            messageLength += kern;
        }

        places.push(messageLength);
        //spaces get a width of 0, so set min at 4px
        messageLength += Math.max(4.5, letter.getBBox().width);
    }

    if( letterSpacing ){
        if (gecko) {
            letterSpacing = letterSpacing * 0.83;
        }
    } else {
        letterSpacing = letterSpacing || path.getTotalLength() / messageLength;
    }
    fontSize = fontSize || 10 * letterSpacing;

    for (c = 0; c < letters.length; c++) {
        letters[c].attr("font-size", fontSize + "px");
        p = path.getPointAtLength(places[c] * letterSpacing);
        var rotate = 'R' + (p.alpha < 180 ? p.alpha + 180 : p.alpha > 360 ? p.alpha - 360 : p.alpha )+','+p.x+','+p.y;
    letters[c].attr({ x: p.x, y: p.y, transform: rotate });
    }
}

您的解决方案帮了我很多,非常感谢!但是我遇到了一个问题,就是如何将文本调整为准确居中于路径上。我在这里求助:链接。也许您可以帮忙看一下? - zorza
文本在路径上的对齐功能将是一个有用的特性。我大致查看了一下,并做到了这一步-http://jsbin.com/UJIYAco/18/edit-这是一个起点,适用于GC/FF/IE8+,但是正如您所看到的,字符总是向右倾斜太多,有时会聚集在末尾。这个方法似乎很合理,但它需要一些方法来抵消向右的移动-出于某种原因,它要么低估文本长度,要么高估路径长度。 - user56reinstatemonica8
谢谢你!它对我来说似乎足够好了!但现在我有一个不同的问题。我需要在路径上打印的文本在悬停时更改其属性(特别是颜色)。考虑到它不是 Raphael Paper 元素,使用您的函数是否可能实现? - zorza
@zorza 如果你已经解决了问题,能否分享一下你的做法,例如作为你问题的答案? - user56reinstatemonica8
这个函数有一个bug和一个需要改进的地方。1)它不能在路径上居中文本。有时这可能是想要的,但没有选项会让对齐文本变得很麻烦。2)字母旋转有问题,因为a)它依赖于Raphael的错误角度计算(不一致),b)它没有考虑路径方向。 - friendzis
这是我根据您的意见修改后的版本:https://gist.github.com/friendzis/9362577 - friendzis

6

使用Kevin Nielsen所建议的path.getPointAtLength方法并不太困难:

path = paper.path("M50,100c40,-50 270,50 300,0").attr("stroke", "#CCC");
message = "I want to do this in RaphaelJS";

//since not every letter is the same width, get the placement for each letter 
//along the length of the string
//however, Raphael appears to group the width of letters into intervals of 4px,
//so this won't be perfect        
for (; c < message.length; c += 1) {
        letter = paper.text(0, 0, message[c]).attr({"text-anchor" : "start"});
    letters.push(letter);
    places.push(message_length);
    //spaces get a width of 0, so set min at 4px
    message_length += Math.max(4, letter.getBBox().width);
}

ratio = path.getTotalLength() / message_length;
fontsize = 10 * ratio;

for (c = 0; c < letters.length; c += 1) {
    letters[c].attr("font-size", fontsize + "px"); 
    p = path.getPointAtLength(places[c] * ratio);
    //there does appear to be a bug in p.alpha around 180. Here's the fix
    letters[c].attr({ x: p.x, y: p.y, transform: 'r' + (p.alpha < 180 ? p.alpha + 180 : p.alpha)});
}

jsFiddle


不错的方法,点赞!但是在我的测试中,这只适用于Webkit(Chrome、Safari等)和IE9+。在Firefox和IE8中,文本会被抛出并且不遵循路径。我在下面发布了一个改进版本(带有旋转原点),它适用于IE8+、Firefox和Webkit,并具有一些额外的微调和功能。 - user56reinstatemonica8

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接