Canvas只有在使用fabric.js的点击事件时才能呈现自定义Web字体。

14

我一直在处理与自定义Web字体和fabric.js有关的问题。我的应用程序使用了很多自定义Web字体,当我向画布添加iText时,我会初始化它们:

var text = new fabric.IText("My Text", {
    fontFamily: "Some Custom Font Family",
    fontSize: 50,
    top: 0,
    left: 0,
    fill: "#000000"
  });

  canvas.add(text);
  canvas.bringToFront(text);
  canvas.setActiveObject(text);

  canvas.renderAll();

这可以工作,但只有当我点击画布上的iText并与其交互时。一旦字体加载完成,就不再是问题。问题在于最初以及第一次添加iText。

我做了很多研究,找到了这个主题:

在Fabric.js中使用远程Web字体初始化加载文本

但那没有帮助我。那里提供的jsfiddle有完全相同的问题:

http://jsfiddle.net/vvL6f/6/

只需用刷新后的浏览器(例如Chrome CMD + Shift + R)打开此fiddle即可。您将看到一旦打开fiddle,自定义Web字体未加载,但是当您单击右侧的iText时会立即加载。

现在,我们该如何解决?

建议的方法是将useNative设置为false,让Cufon来渲染文本,但这没有起作用。

我像这样在CSS文件中加载Web字体:

@font-face {
    font-family: 'ApolloASM';
    src: url('ApolloASM-webfont.eot');
    src: url('ApolloASM-webfont.eot?#iefix') format('embedded-opentype'),
         url('ApolloASM-webfont.woff2') format('woff2'),
         url('ApolloASM-webfont.woff') format('woff'),
         url('ApolloASM-webfont.ttf') format('truetype'),
         url('ApolloASM-webfont.svg#apollo_asmregular') format('svg');
    font-weight: normal;
    font-style: normal;
}

你解决了这个问题吗? - ptCoder
不好意思,没有,很遗憾 :-( - DonMB
请查看以下内容:http://www.atomicjetpacks.com/blog/8/how-to-detect-if-a-webfont-really-loaded-ii - https://github.com/JenniferSimonds/FontDetect这里有它的工作原理解释:https://dev59.com/H2ct5IYBdhLWcg3wI6N6#12316349 - ptCoder
我不确定为什么,但对我来说,通过请求 https://fonts.googleapis.com/css?family=Font 而不是 https://fonts.googleapis.com/css?family=Font:100, 200,400,900 可以正常工作,我猜测这是因为加载时间更短。足够“快速”在 canvas 之前加载。 - Horacio
5个回答

17

@Prominic的回答已经接近正确。我也有一个使用自定义字体和Fabric的画布应用程序。我像你一样在CSS中加载所有字体。关键是仅在样式表中声明@font-face是不够的。除非在文档中使用,否则浏览器不会请求字体文件。而且您必须确保在初始化画布之前加载并应用Web字体。这可以通过一些工作来实现,我将在下面的示例中进行说明。缺点是您可能需要预加载应用程序为用户提供的所有字体。


  1. 声明Web字体

    在样式表中添加@font-face语句。只需确保它在<head>部分中加载即可。让我们使用您的示例:
 @font-face {
    font-family: 'ApolloASM';
    src: url('ApolloASM-webfont.eot');
    src: url('ApolloASM-webfont.eot?#iefix') format('embedded-opentype'),
         url('ApolloASM-webfont.woff2') format('woff2'),
         url('ApolloASM-webfont.woff') format('woff'),
         url('ApolloASM-webfont.ttf') format('truetype'),
         url('ApolloASM-webfont.svg#apollo_asmregular') format('svg');
    font-weight: normal;
    font-style: normal;
}

小贴士:如果您在应用程序中使用斜体和/或粗体字体,则建议为这些样式添加字体,以便在不同的浏览器中获得一致的结果。例如,Android浏览器4.2无法计算字体的斜体,并将其显示为普通字体。

  1. 强制请求字体文件

如上所述,您需要实际在DOM中使用字体来强制加载字体。只需添加一个带有一些文本并使用您的字体进行样式设置的div即可:

<div style="font-family:'ApolloASM'">&nbsp;</div>

小贴士:别忘了也要添加斜体和粗体变体

  1. 在DOM load事件之后初始化FabricJS画布

你需要在浏览器加载字体后同步Canvas的初始化(或至少是文字添加),否则可能会出现未经样式修饰的文本闪烁效果(FOUT)。幸运的是,浏览器仅当所有字体文件都加载完毕后才会触发load事件,这可以用于安全地向画布添加带样式的文本:

window.addEventListener('load', function onLoad() {
    var canvas = new Fabric.Canvas({...});
    var text = new fabric.Text("Web Font Example", {
        left: 200,
        top: 30,
        fontFamily: 'ApolloASM',
        fill: '#000',
        fontSize: 60
    });

    canvas.add(text);
    canvas.renderAll();
})

小提示: 您可以将这些字体<div>包装在另一个中并在DOM load事件后删除它,以避免破坏您可能拥有的任何布局。


1
强制请求字体文件,非常有效,感谢@Bernardo :D - David Torroija
2
你刚刚帮我避免了一个大麻烦..谢谢 +1 - Abhinav
这个解决方案会增加HTTP请求,对SEO有轻微的负面影响。如果有90个字体,这将创建90个HTTP请求。 - HOY
现在最好使用WebFont Loader,并使用fontactive回调来触发正确字体的文本渲染。请参见下面@Naoyuki的答案。 - Bernardo Domingues
这是互联网上最好的答案。谢谢@BernardoDomingues。 - undefined
显示剩余2条评论

4
你可以使用Web字体加载器。

    <script src="//ajax.googleapis.com/ajax/libs/webfont/1.5.18/webfont.js">    </script>
    <script>
    var c = new fabric.Canvas('canvas');
    WebFont.load({
     google: {
      families: ['Fredoka One']
     },
     active: function() {
            var text = new fabric.IText('Hello world !', {
                fontFamily: 'Fredoka One',
                top: 100,
                left: 100,
                fontSize: 20
            });
         c.add(text);
     },
    });
    </script>

在这里查看JSFiddle 链接


1
发生的情况是当您的脚本首次渲染画布时,Web字体尚未加载。
我认为最好的方法是使用HTML标签在``中加载Web字体。这将在DOM顶部加载字体,以便在浏览器呈现画布时准备好字体供您的画布脚本使用。以下是示例。

https://jsfiddle.net/j47k79e8/1/

<link href='http://fonts.googleapis.com/css?family=Ranchers' rel='stylesheet' type='text/css'>

如果你仍想使用Javascript方法来加载web字体,你可以在运行FabricJS脚本之前等待文档加载。只需将其包装在文档就绪语句中。

$(document).ready(function(){
  // FabricJS scripts
});

https://jsfiddle.net/ahxpeu0v/3/


我已经通过自己的CSS在头部加载了字体。它们也应该得到正确的初始化。请参见OP,我刚刚编辑过。将其包装在文档准备好的语句中不起作用,因为我根据需要调用addText函数。 - DonMB
看起来你的字体是自托管的?这是我另一个想法...尽量减少尽可能多的延迟变量。我的另一个想法是尝试不同的字体。目标只是找到问题可能出在哪里——可能是特定字体独有的问题?在我看来,一切都正确。虽然我还没有与IText合作过,但也许IText加载自定义字体的方式是独特的。 - PromInc

0

您只需要在插入文本后加入canvas.renderAll();命令,就像这样:

canvas = new fabric.Canvas('c'); 
var text = new fabric.Text("Web Font Example", {
    left: 200,
    top: 30,
    fontFamily: 'Ranchers',
    fill: '#000',
    fontSize: 60
});

canvas.add(text);
canvas.renderAll();

代码片段:http://jsfiddle.net/vvL6f/107/


可能是因为字体尚未缓存,所以在首次加载时无法正常工作。 - Shomz
我已经这样做了 - 请查看我的原始帖子。@Shomz 是的,这也是我怀疑的,但我不知道如何在这种情况下解决它。 - DonMB
@DonMB,我尝试了几种方法,但最终删除了我的答案,因为所有版本都或多或少存在同样的问题。而且我不想使用超时。可能会有人想出什么好办法,耐心等待吧。 - Shomz
感谢您的努力。是的,我也考虑过超时问题,但那只会减慢我的应用程序速度 - 不好。 - DonMB

0

@Bernardo Domingues所提供的答案对我来说不好,因为我有超过500个字体的<select>,而我不想预加载所有这些字体,因为会受到流量限制。

所以我通过每500毫秒重复调用Canvas.renderAll()方法来解决这个问题。一遍又一遍。这样,在我将字体更改为尚未被浏览器加载的新字体时 - 首先它将被浏览器下载(我不知道也不关心需要多少时间)。当新字体加载完成时 - 我的代码将在500毫秒内更新整个画布,瞧!我的字体也更新了 :)


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