HTML(canvas 和 svg)中奇怪的模糊文本

Strange blurry text in HTML (canvas and svg)

我的字体有一个奇怪的行为,文本在垂直方向(和水平方向,但这很难发现)之后变得模糊 "columns"。这是在 canvas 和带有 chrome 的 svg 上测试的。两者之间的行为几乎相同。

看看 jsfiddle:https://jsfiddle.net/ryderone/m9kpjto3/170

或查看下面的代码片段。

如您所见,根据打印的位置,我有非常清晰的字符和非常模糊的字符。

// test with canvas
var canvas = document.querySelector('#canvas');
var ctx = canvas.getContext('2d');
var coef = 1; // try to hidpi canvas, increase this to increase the ratio

canvas.width = 300 * coef;
canvas.height = 300 * coef;
document.fonts.onloadingdone = () => {
  ctx.fillStyle = '#ffffff';
  ctx.imageSmoothingEnabled = false;
  ctx.textBaseline = 'middle';
  ctx.font = (13 * coef) + 'px visitor2';

  for (var i = 1; i < 19; i++) {
    var x = 10;
    var y = 10 * i * coef + i;
    ctx.fillText(" AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA   " + x + "/" + y, x | 0 + 0.5, y | 0 + 0.5);
  }
}

// same test with svg
var svg = document.querySelector('#svg');
for (var j = 1; j < 19; j++) {
  var x = 10;
  var y = 10 * j + j;
  var element = document.createElementNS('http://www.w3.org/2000/svg', 'text');
  element.setAttributeNS(null, 'x', x + '');
  element.setAttributeNS(null, 'y', y + '');
  element.style.fontFamily = 'visitor2';
  element.style.fontSize = '13px';
  element.style.fill = '#ffffff';
  var txt = document.createTextNode(" AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA   " + x + "/" + y);
  element.appendChild(txt);
  svg.appendChild(element);
}
#canvas,
#svg {
  background: black;
  width: 300px;
  height: 300px;
}

@font-face {
  font-family: visitor2;
  src: url('https://ryder-one.github.io/hmap/visitor2.woff') format('woff');
}
<canvas id="canvas"></canvas>
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="300" height="300" id="svg"></svg>

请注意,上面的 运行 按钮对于 canvas(左侧)无法正常工作,因为我没有加载字体。但是,如果您多次按 运行,它会在 jsfiddle 上运行。

我不认为这是亚像素 canvas 的事情,因为它也发生在 SVG 上,具有完全相同的行为;而且我认为这也不是设备比率问题。我不认为字体有什么问题,因为我在一个 Flash 项目中看到了完全相同的字体,而且它运行良好。

我基本上检查了 canvas 和 HTML 上所有关于模糊文本的问题。我找到了一些有趣的答案,但我没有找到任何 post 来描述这种确切的行为。

有人知道导致此行为的原因以及解决方法吗?

谢谢!

编辑 1: Here is the picture of the problem

编辑 2 : 请不要编辑 post 来制作这张图片的缩略图,因为 SO 所做的调整大小隐藏了实际问题。单击 link 可查看全尺寸图片。谢谢。

编辑 3 : 我在原文中不够清楚 post。这不是 canvas 相关的问题,因为同样的问题也发生在 SVG 上。 post 的一些观众说他们无法用他们的浏览器重现问题,canvas 和 SVG 都清晰明了。我正在考虑 chrome 相关问题或类似问题。

这主要是你字体的问题。

它的字距设置不正确,会使这一系列字符落在浮动坐标上。
在某些时候,浏览器将不得不触发字体平滑算法。不同的 OS 和浏览器默认会使用不同的字体平滑算法,不同像素密度的屏幕或多或少会有明显的伪影;这可能就是为什么其他人看不到与您的结果相同的原因(我看到了)。

除了向设计师抱怨,你无能为力,甚至可能会出现 HTML:

@font-face {
  font-family: visitor2;
  src: url('https://ryder-one.github.io/hmap/visitor2.woff') format('woff');
}
#test {
  font: 13px visitor2;
  line-height: 10px;
  padding: 10px;
}
<div id="test">
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
</div>

这是我在 macO 上的 FF 上的效果(亚像素抗锯齿):

这是它在我的 Chrome(灰度抗锯齿)上的样子:


对于 SVG 和 HTML,您可以尝试禁用抗锯齿,尽管 IIIRC 仅适用于 Chrome在 macOs 系统上 ,通过使用非标准 font-smooth CSS 属性 到 none。但是这样做,浏览器将使用类似最近邻的算法,这只会在您的文本中造成空白:

@font-face {
  font-family: visitor2;
  src: url('https://ryder-one.github.io/hmap/visitor2.woff') format('woff');
}
#test {
  font: 13px visitor2;
  line-height: 10px;
  padding: 10px;
  font-smooth: none; /* in case it works one day*/
  -webkit-font-smoothing: none; 
}
<div id="test">
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
AAAAAAAAAAAAAAAAAAAAAAAA<br>
</div>

这是我的 Chrome 上的样子:

所以是的,不再有模糊,但在第 13 个字符后你会得到一个很好的间隙。


对于,默认是透明抗锯齿,这是逻辑,因为浏览器不知道后面会画什么。
但是有一个鲜为人知的功能可以让我们在 macO 上强制使用亚像素抗锯齿。通过使用 alpha: false 选项启动我们的 2D 上下文,大多数浏览器会将字体平滑设置为子像素:

document.fonts.onloadingdone = e => {
  // tell our context it won't have transparency
  const ctx = canvas.getContext('2d', {alpha: false});
  ctx.fillStyle = 'white';
  ctx.fillRect(0,0,canvas.width,canvas.height);
  ctx.fillStyle = 'black';
  ctx.font = (13) + 'px visitor2';
  for (var i = 1; i < 19; i++) {
    var x = 10;
    var y = 10 * i + i;
    ctx.fillText(" AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA   " + x + "/" + y, x | 0 + 0.5, y | 0 + 0.5);
  }
  
  // draw close-up
  ctx.imageSmoothingEnabled = false;
  ctx.drawImage(canvas, -70, 70, canvas.width * 5, canvas.height * 5);
};
@font-face {
  font-family: visitor2;
  src: url('https://ryder-one.github.io/hmap/visitor2.woff') format('woff');
}
#canvas {
  font: 13px visitor2; 
}
<canvas id="canvas" height="200"></canvas>

所以至少现在,您可以在两种抗锯齿方法之间进行选择。