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>
所以至少现在,您可以在两种抗锯齿方法之间进行选择。
我的字体有一个奇怪的行为,文本在垂直方向(和水平方向,但这很难发现)之后变得模糊 "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>
所以至少现在,您可以在两种抗锯齿方法之间进行选择。