You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解决Canvas中Fontello图标显示为空方块的问题?

问题描述

尝试在Canvas中使用自定义Fontello字体导出图标为PNG,但无论使用哪种字符编码写法,都只显示空方块⎕。字体已在文档中正确加载,在DOM元素中能正常渲染,但Canvas绘制始终失败。

原始Canvas代码

ctx.font = `30px fontello`;
ctx.fillText('\ue804', canvas.width / 2, canvas.height / 2);
const image = canvas.toDataURL('image/png');

DOM测试可行代码

const span = document.createElement('span');
span.style.fontFamily = 'fontello'
span.innerHTML = '&#xe804'
document.body.appendChild(span)

尝试过的无效写法

ctx.font = `30px "fontello"`;
ctx.fillText('\\ue804', canvas.width / 2, canvas.height / 2);
ctx.fillText('\uE804', canvas.width / 2, canvas.height / 2);
ctx.fillText('&#xe804', canvas.width / 2, canvas.height / 2);
ctx.fillText('&#xE804', canvas.width / 2, canvas.height / 2);

完整代码片段

JavaScript

const canvas = document.createElement('canvas');
canvas.width = 30; 
canvas.height = 30; 
const ctx = canvas.getContext('2d');
const circleStrokeColor = filter.filter_color.polyline; 
const circleFillColor = filter.filter_color.polygon; 
const circleRadius = Math.min(canvas.width, canvas.height) / 2 - 1;
ctx.strokeStyle = this.hexToRgb(circleStrokeColor);
ctx.fillStyle = this.hexToRgb(circleFillColor);
ctx.beginPath();
ctx.arc(canvas.width / 2, canvas.height / 2, circleRadius, 0, 2 * Math.PI);
ctx.fill();
ctx.lineWidth = 1;
ctx.stroke();
ctx.fillStyle = this.hexToRgb(circleStrokeColor);
ctx.textAlign = 'center';
ctx.textBaseline = 'middle';
ctx.font = `30px fontello`;
ctx.fillText('\ue804', canvas.width / 2, canvas.height / 2);
const image = canvas.toDataURL('image/png');
console.log(image)
return image

CSS

@font-face {
  font-family: 'fontello';
  src: url('../font/fontello.eot?41849995');
  src: url('../font/fontello.eot?41849995#iefix') format('embedded-opentype'),
       url('../font/fontello.woff2?41849995') format('woff2'),
       url('../font/fontello.woff?41849995') format('woff'),
       url('../font/fontello.ttf?41849995') format('truetype'),
       url('../font/fontello.svg?41849995#fontello') format('svg');
  font-weight: normal;
  font-style: normal;
}
/* Chrome hack: SVG is rendered more smooth in Windozze. 100% magic, uncomment if you need it. */
/* Note, that will break hinting! In other OS-es font will be not as sharp as it could be */
/*
@media screen and (-webkit-min-device-pixel-ratio:0) {
  @font-face {
    font-family: 'fontello';
    src: url('../font/fontello.svg?41849995#fontello') format('svg');
  }
}
*/
[class^="icon-"]:before, [class*=" icon-"]:before {
  font-family: "fontello";
  font-style: normal;
  font-weight: normal;
  speak: never;

  display: inline-block;
  text-decoration: inherit;
  width: 1em;
  margin-right: .2em;
  text-align: center;
  /* opacity: .8; */

  /* For safety - reset parent styles, that can break glyph codes*/
  font-variant: normal;
  text-transform: none;

  /* fix buttons height, for twitter bootstrap */
  line-height: 1em;

  /* Animation center compensation - margins should be symmetric */
  /* remove if not needed */
  margin-left: .2em;

  /* you can be more comfortable with increased icons size */
  /* font-size: 120%; */

  /* Font smoothing. That was taken from TWBS */
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;

  /* Uncomment for 3D effect */
  /* text-shadow: 1px 1px 1px rgba(127, 127, 127, 0.3); */
}

.icon-gebouwen:before { content: '\e800'; } /* '' */
.icon-groen:before { content: '\e801'; } /* '' */
.icon-kabels-leidingen:before { content: '\e802'; } /* '' */
.icon-kunstwerken:before { content: '\e803'; } /* '' */
.icon-legenda:before { content: '\e804'; } /* '' */
.icon-meubilair:before { content: '\e805'; } /* '' */
.icon-riolering:before { content: '\e806'; } /* '' */
.icon-rioolgemalen:before { content: '\e807'; } /* '' */
.icon-speeltoestellen:before { content: '\e808'; } /* '' */
.icon-spoor-masten:before { content: '\e809'; } /* '' */
.icon-sport:before { content: '\e80a'; } /* '' */
.icon-verhardingen:before { content: '\e80b'; } /* '' */
.icon-verlichting:before { content: '\e80c'; } /* '' */
.icon-vri:before { content: '\e80d'; } /* '' */
.icon-water:before { content: '\e80e'; } /* '' */
解决建议

1. 等待字体完全加载后再绘制Canvas

Canvas绘制文字时,字体必须完成加载,否则会回退到默认字体导致方块。使用FontFace API显式控制加载流程:

const font = new FontFace('fontello', 'url("../font/fontello.ttf?41849995")');
font.load().then(() => {
  document.fonts.add(font);
  drawCanvasIcon(); // 字体加载完成后执行绘制
});

function drawCanvasIcon() {
  const canvas = document.createElement('canvas');
  canvas.width = 30; 
  canvas.height = 30; 
  const ctx = canvas.getContext('2d');
  // ... 保留原有的圆形绘制代码
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';
  ctx.font = `20px fontello`; // 调整字体大小适配画布
  ctx.fillText('\ue804', canvas.width / 2, canvas.height / 2);
  const image = canvas.toDataURL('image/png');
  console.log(image);
  return image;
}

2. 调整字体格式加载优先级

部分浏览器对WOFF2格式在Canvas中的支持存在延迟,调整@font-face的src顺序,优先加载TTF格式:

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

3. 修正字体大小与画布尺寸的匹配问题

你的Canvas尺寸为30x30,设置30px字体大小会导致图标超出画布范围,视觉上表现为空方块。缩小字体大小或增大画布:

ctx.font = `20px fontello`; // 缩小字体适配30x30画布
// 或者增大画布尺寸
canvas.width = 40;
canvas.height = 40;
ctx.font = `30px fontello`;

4. 确认字符编码的正确性

  • 仅使用纯Unicode转义符:'\ue804'是正确写法,无需额外转义
  • 不要使用HTML实体(如&#xe804),fillText不解析HTML,会直接渲染实体文本

内容的提问来源于stack exchange,提问作者Delyan Ninov

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.22 11:06:59