Canvas仅使用fabric.js在点击事件时渲染自定义Web字体

Canvas only renders custom webfont on click event with fabric.js

本文关键字:事件 自定义 字体 Web fabric js Canvas      更新时间:2023-09-26

我在自定义web字体和fabric.js方面遇到了一个持续的问题。我的应用程序使用了很多自定义的网络字体,当我向画布添加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,自定义web字体不会加载,但当你点击右侧的iText时会立即加载。

现在,我们该如何解决这个问题?

建议的方法是将useNative设置为false,并让Cufon呈现文本,但这并不奏效。

我在一个CSS文件中加载我的网络字体,如下所示:

@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;
}

@Prominic的答案就在那里。我还有一个画布应用程序,它使用Fabric的自定义字体。我和你一样在CSS中加载所有字体。问题是,仅仅在样式表中声明@font-face是不够的。除非在文档中使用字体文件,否则浏览器不会请求该字体文件并且您必须确保在初始化画布之前加载并应用webfont。这可以通过一点工作来实现,我将在下面的例子中进行说明。缺点是,您可能需要预加载应用程序提供给用户的所有字体。


  1. 声明您的webfont

    在样式表中添加@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;
}

额外提示:如果你在应用程序中使用斜体和/或粗体,如果你想在不同的浏览器中获得一致的结果,建议为这些样式添加字体。例如,安卓浏览器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事件后将其删除,以避免破坏您可能拥有的任何布局。

您可以使用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在这里。

发生的情况是,当脚本首次渲染画布时,尚未加载webfont。

我认为执行此操作的最佳方式是在中使用HTML标记加载webfont。这将在DOM顶部加载字体,以便在浏览器渲染时为画布脚本准备好字体。

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

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

尽管如此,如果您仍然想使用Javascript方法来加载webfont,您可以等待文档加载后再运行FabricJS脚本。只需将其包装在文档准备语句中即可。

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

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

您只需要在插入文本后放入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();

Fiddle:http://jsfiddle.net/vvL6f/107/

@Bernardo Domingues的回答对我不好,因为我有500多种字体的<select>,由于交通限制,我不想预加载所有字体。

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