Интервал между буквами в элементе canvas - PullRequest
31 голосов
/ 21 января 2012

Вопрос говорит сам за себя. Я искал вокруг и начинаю беспокоиться, что это невозможно.

У меня есть этот элемент холста, к которому я рисую текст. Я хочу установить интервал между буквами, аналогичный атрибуту CSS letter-spacing. Под этим я подразумеваю увеличение количества пикселей между буквами, когда рисуется строка.

Мой код для рисования текста выглядит примерно так: ctx - это контекстная переменная canvas.

ctx.font = "3em sheepsans";
ctx.textBaseline = "middle";
ctx.textAlign = "center";
ctx.fillStyle = "rgb(255, 255, 255)";
ctx.fillText("Blah blah text", 1024 / 2, 768 / 2);

Я пытался добавить ctx.letterSpacing = "2px"; перед рисованием, но безрезультатно. Есть ли способ сделать это просто с помощью простой настройки, или мне нужно будет сделать функцию для индивидуального рисования каждого символа с учетом расстояния?

Ответы [ 10 ]

33 голосов
/ 21 февраля 2013

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

ctx.font = "3em sheepsans";
ctx.textBaseline = "middle";
ctx.textAlign = "center";
ctx.fillStyle = "rgb(255, 255, 255)";
var ctext = "Blah blah text".split("").join(String.fromCharCode(8202))
ctx.fillText(ctext, 1024 / 2, 768 / 2);

Это вставит пробел между каждой буквой.

Использование 8201 (вместо 8202) вставит немного более широкое тонкое пространство

Дополнительные параметры пробелов см. В этом списке пространств Unicode

Этот метод поможет вам гораздо легче сохранить кернинг шрифта, чем ручное позиционирование каждой буквы, однако вы не сможете уменьшить расстояние между этими буквами.

18 голосов
/ 21 января 2012

Я не уверен, что должен работать (согласно спецификациям), но в некоторых браузерах (Chrome) вы можете установить свойство CSS letter-spacing для самого элемента <canvas>, и оно будетприменяться ко всему тексту, нарисованному в контексте.(Работает в Chrome v56, не работает в Firefox v51 или IE v11.)

Обратите внимание, что в Chrome v56 необходимо повторно получать контекст Canvas 2d (и переустанавливать любые значения, которые вам интересны) после каждогоизменить стиль letter-spacing;кажется, что интервал запечен в получаемый вами 2-мерный контекст.

Пример: https://jsfiddle.net/hg4pbsne/1/

var inp = document.querySelectorAll('input'),
    can = document.querySelector('canvas'),
    ctx = can.getContext('2d');
    can.width = can.offsetWidth;

[].forEach.call(inp,function(inp){ inp.addEventListener('input', redraw, false) });
redraw();

function redraw(){
  ctx.clearRect(0,0,can.width,can.height);
  can.style.letterSpacing = inp[0].value + 'px';

  ctx = can.getContext('2d');
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';
  ctx.font = '4em sans-serif';
  ctx.fillText('Hello', can.width/2, can.height*1/4);
  
  can.style.letterSpacing = inp[1].value + 'px';
  ctx = can.getContext('2d');
  ctx.textAlign = 'center';
  ctx.textBaseline = 'middle';
  ctx.font = '4em sans-serif';
  ctx.fillText('World', can.width/2, can.height*3/4);
};
canvas { background:white }
canvas, label { display:block; width:400px; margin:0.5em auto }
<canvas></canvas>
<label>hello spacing: <input type="range" min="-20" max="40" value="1" step="0.1"></label>
<label>world spacing: <input type="range" min="-20" max="40" value="1" step="0.1"></label>

Оригинальный кросс-браузерный ответ:

Это невозможно;HTML5 Canvas не обладает всеми возможностями преобразования текста CSS в HTML.Я бы посоветовал вам сочетать соответствующие технологии для каждого использования.Используйте HTML-слой с Canvas и, возможно, даже SVG, каждый из которых делает то, что у него получается лучше всего.

Обратите также внимание на то, что «сворачивание собственного» - рисование каждого символа с пользовательским смещением - приведет к плохим результатам для большинства шрифтов,с учетом наличия пар букв кернинга и хинтинга шрифтов с выравниванием по пикселям.

6 голосов
/ 21 января 2012

Вы не можете установить межбуквенный интервал как свойство контекста Canvas. Эффект можно получить только с помощью ручного интервала, извините. (Как, например, рисование каждой буквы вручную, увеличивая x на некоторое количество пикселей на каждой)

Для записи вы можете установить несколько свойств текста, используя ctx.font, но межбуквенный интервал не является одним из них. Те, которые вы можете установить: "font-style font-option font-weight font-size / line-height font-family"

Например, технически вы можете написать ctx.font = "bold normal normal 12px/normal Verdana" (или любое упущение любого из них), и оно будет правильно проанализировано.

3 голосов
/ 07 ноября 2016

Не верно. Вы можете добавить свойство letter-spacing к элементу canvas в css, и оно отлично работает. Не нужно сложных обходных путей. Я только что понял это прямо сейчас в своем проекте холста. т.е .: холст { ширина: 480 пикселей; высота: 350 пикселей; поле: 30px авто 0; отступы: 15px 0 0 0; фон: розовый; дисплей: блок; граница: 2px пунктирная коричневая; межбуквенный интервал: 0,1em; }

3 голосов
/ 03 декабря 2015

Чтобы учесть «пары букв кернинга» и т.п., я написал следующее. Следует принять это во внимание, и грубое тестирование предполагает, что это так. Если у вас есть какие-либо комментарии по этому поводу, я хотел бы указать на мой вопрос по этому вопросу ( Добавление межбуквенного интервала в HTML-канве )

В основном он использует measureText (), чтобы получить ширину всей строки, а затем удаляет первый символ строки и измеряет ширину оставшейся строки, а также использует разницу для вычисления правильного позиционирования - таким образом, принимая во внимание кернинг пар и тому подобное. Смотрите данную ссылку для получения дополнительной псевдокод.

Вот HTML:

<canvas id="Test1" width="800px" height="200px"><p>Your browser does not support canvas.</p></canvas>

Вот код:

this.fillTextWithSpacing = function(context, text, x, y, spacing)
{
    //Start at position (X, Y).
    //Measure wAll, the width of the entire string using measureText()
    wAll = context.measureText(text).width;

    do
    {
    //Remove the first character from the string
    char = text.substr(0, 1);
    text = text.substr(1);

    //Print the first character at position (X, Y) using fillText()
    context.fillText(char, x, y);

    //Measure wShorter, the width of the resulting shorter string using measureText().
    if (text == "")
        wShorter = 0;
    else
        wShorter = context.measureText(text).width;

    //Subtract the width of the shorter string from the width of the entire string, giving the kerned width of the character, wChar = wAll - wShorter
    wChar = wAll - wShorter;

    //Increment X by wChar + spacing
    x += wChar + spacing;

    //wAll = wShorter
    wAll = wShorter;

    //Repeat from step 3
    } while (text != "");
}

Код для тестирования демо / глазного яблока:

element1 = document.getElementById("Test1");
textContext1 = element1.getContext('2d');

textContext1.font = "72px Verdana, sans-serif";
textContext1.textAlign = "left";
textContext1.textBaseline = "top";
textContext1.fillStyle = "#000000";

text = "Welcome to go WAVE";
this.fillTextWithSpacing(textContext1, text, 0, 0, 0);
textContext1.fillText(text, 0, 100);

В идеале я бы выбрасывал несколько случайных строк и делал бы попиксельное сравнение. Я также не уверен, насколько хорош кернинг Верданы по умолчанию, хотя я понимаю, что он лучше, чем Arial - предложения по другим шрифтам с благодарностью приняты.

Так что ... пока все выглядит хорошо. На самом деле это выглядит идеально. Все еще надеемся, что кто-то укажет на любые недостатки в процессе.

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

3 голосов
/ 19 марта 2013

вот некоторый coffeescript, который позволяет вам установить кернинг в вашем контексте следующим образом:

tctx = tcanv.getContext('2d')
tctx.kerning = 10
tctx.fillStyle = 'black'
tctx.fillText 'Hello World!', 10, 10

код поддержки:

_fillText = CanvasRenderingContext2D::fillText
CanvasRenderingContext2D::fillText = (str, x, y, args...) ->

  # no kerning? default behavior
  return _fillText.apply this, arguments unless @kerning?

  # we need to remember some stuff as we loop
  offset = 0

  _.each str, (letter) =>

    _fillText.apply this, [
      letter
      x + offset + @kerning
      y
    ].concat args # in case any additional args get sent to fillText at any time

    offset += @measureText(letter).width + @kerning

JavaScript будет

var _fillText,
  __slice = [].slice;

_fillText = CanvasRenderingContext2D.prototype.fillText;

CanvasRenderingContext2D.prototype.fillText = function() {
  var args, offset, str, x, y,
    _this = this;

  str = arguments[0], x = arguments[1], y = arguments[2], args = 4 <= arguments.length ? __slice.call(arguments, 3) : [];
  if (this.kerning == null) {
    return _fillText.apply(this, arguments);
  }
  offset = 0;

  return _.each(str, function(letter) {
    _fillText.apply(_this, [letter, x + offset + _this.kerning, y].concat(args));
    offset += _this.measureText(letter).width + _this.kerning;
  });
};
0 голосов
/ 02 мая 2019

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

// object holding x and y coordinates
var vectors = {'x':{1:100, 2:200}, 'y':{1:0, 2:100}
// replace the first letter of a word
var newtext = YOURSTRING.replace(/^\b[a-z]/g, function(oldtext) {
    // return it uppercase
    return oldtext.toUpperCase(); 
});
// split string by spaces
newtext = newtext.split(/\s+/);

// line height
var spacing = 10 ;
// initial adjustment to position
var spaceToAdd = 5;
// for each word in the string draw it based on the coordinates + spacing
for (var c = 0; c < newtext.length; c++) {
    ctx.fillText(newtext[c], vectors.x[i], vectors.y[i] - spaceToAdd);
    // increment the spacing 
    spaceToAdd += spacing;
}               
0 голосов
/ 01 ноября 2017

интервал между буквами в холсте ПОДДЕРЖИВАЕТСЯ, я использовал это

canvas = document.getElementById('canvas');
canvas.style.letterSpacing = '2px';
0 голосов
/ 07 сентября 2017

На самом деле концепция межбуквенного отступа не поддерживается.

Так что я использовал javascript для этого.

var value = $('#sourceText1').val().split("").join(" ");

OR

var sample_text = "Praveen Chelumalla";
var text = sample_text.split("").join(" ");
0 голосов
/ 25 мая 2016

Я использую:

ctx.font = "32px Tahoma";//set font
ctx.scale(0.75,1);//important! the scale
ctx.fillText("LaFeteParFete test text", 2, 274);//draw
ctx.setTransform(1,0,0,1,0,0);//reset transform
...