Странный размытый текст в HTML (canvas и svg) - PullRequest
1 голос
/ 29 апреля 2019

У меня странное поведение со шрифтом, текст расплывчатый после вертикальных (и горизонтальных, но это труднее обнаружить) «столбцов».Это проверено на холсте и на 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>

Обратите внимание, что кнопка запуска выше не работает должным образом для холста (левая сторона), так как я не делал шрифт faceпогрузка.Но он работает на jsfiddle, если вы нажмете кнопку run несколько раз.

Я не думаю, что это субпиксельная функция canvas, поскольку это происходит также в SVG с точно таким же поведением;и я не думаю, что это тоже соотношение устройств.Я не думаю, что что-то не так с этим шрифтом, потому что я видел точно такой же шрифт, используемый во флеш-проекте, и он отлично работает.

Я в основном проверил все SO вопросы о размытом тексте на холсте,и HTML в целом.Я нашел несколько интересных ответов, но я не нашел ни одной публикации, описывающей это точное поведение.

Кто-нибудь имеет представление о том, что может вызвать такое поведение и как его решить?

Спасибо!

РЕДАКТИРОВАТЬ 1: Вот изображение проблемы

РЕДАКТИРОВАТЬ 2: Пожалуйста, не редактируйтенапишите, чтобы сделать эскиз этой картинки, так как изменение размера, сделанное SO, скрывает актуальную проблему.Нажмите на ссылку, чтобы увидеть картинку в полном размере.Спасибо.

РЕДАКТИРОВАТЬ 3: Я не был достаточно ясен в оригинальном сообщении.Это не проблема, связанная с холстом, поскольку та же проблема возникает и в SVG.Некоторые из зрителей поста сказали, что не смогли воспроизвести проблему с помощью своего браузера, холст и SVG были четкими.Я думаю о проблеме с хромом или о чем-то подобном.

1 Ответ

1 голос
/ 02 мая 2019

В основном это вина вашего шрифта.

Его кернинг установлен неправильно и заставит эту последовательность символов попадать в плавающие координаты.
В какой-то момент браузер должен будет запустить алгоритм сглаживания шрифтов. Различные ОС и браузеры по умолчанию будут использовать разные алгоритмы сглаживания шрифтов, а экран с разной плотностью пикселей будет иметь более или менее заметные артефакты; Вероятно, поэтому другие не видят того же результата, что и ваш (я знаю).

Не так много, что вы можете сделать, не обращая внимания на дизайнера, это может даже появиться с 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>

Вот как это выглядит на моем FF на macOs (субпиксельное сглаживание):

screenshot of sub-pixel antialiasing with close-up view

А вот как это выглядит на моем Chrome (сглаживание в градациях серого):

screenshot of grayscale antialiasing with close-up view


Для 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:

screenshot of same text with no antialiasing

Так что да, размытия больше нет, но вы получите хороший разрыв после 13-го символа.


Для по умолчанию используется сглаживание прозрачности, что логично, поскольку браузер не знает, что будет отрисовано.
Но есть малоизвестная особенность, которая позволяет нам принудительно использовать субпиксельное сглаживание в macO. Инициируя наш 2D-контекст с параметром alpha: false, большинство браузеров устанавливают сглаживание шрифта как субпиксель:

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>

Так что теперь, по крайней мере, у вас есть возможность выбирать между обоими методами сглаживания.

...