Обтекание текстом в элементе <canvas> - PullRequest
32 голосов
/ 29 мая 2010

Я пытаюсь добавить текст на изображение, используя элемент <canvas>. Сначала изображение рисуется и на изображении рисуется текст. Пока все хорошо.

Но проблема в том, что если текст слишком длинный, он обрезается в начале и в конце канвой. Я не планирую изменять размер холста, но мне было интересно, как обернуть длинный текст в несколько строк, чтобы он отображался полностью. Кто-нибудь может указать мне правильное направление?

Ответы [ 10 ]

27 голосов
/ 17 мая 2013

Обновлена ​​версия ответа @ mizar, исправлена ​​одна серьезная и одна незначительная ошибка.

function getLines(ctx, text, maxWidth) {
    var words = text.split(" ");
    var lines = [];
    var currentLine = words[0];

    for (var i = 1; i < words.length; i++) {
        var word = words[i];
        var width = ctx.measureText(currentLine + " " + word).width;
        if (width < maxWidth) {
            currentLine += " " + word;
        } else {
            lines.push(currentLine);
            currentLine = word;
        }
    }
    lines.push(currentLine);
    return lines;
}

Мы использовали этот код в течение некоторого времени, но сегодня мы пытались выяснить, почему какой-то текст не рисовался, и обнаружили ошибку!

Оказывается, что если вы дадите одно слово (без пробелов) функции getLines (), она вернет пустой массив, а не массив с одной строкой.

Пока мы исследовали это, мы обнаружили еще одну (гораздо более тонкую) ошибку, в результате которой строки могут заканчиваться немного длиннее, чем должны быть, поскольку в исходном коде не учитывались пробелы при измерении длины строки. 1008 *

Наша обновленная версия, которая работает для всего, что мы бросили в нее, выше. Дайте мне знать, если найдете какие-либо ошибки!

23 голосов
/ 18 октября 2010

Возможный метод (не полностью протестирован, но на данный момент он работал отлично)

    /**
     * Divide an entire phrase in an array of phrases, all with the max pixel length given.
     * The words are initially separated by the space char.
     * @param phrase
     * @param length
     * @return
     */
function getLines(ctx,phrase,maxPxLength,textStyle) {
    var wa=phrase.split(" "),
        phraseArray=[],
        lastPhrase=wa[0],
        measure=0,
        splitChar=" ";
    if (wa.length <= 1) {
        return wa
    }
    ctx.font = textStyle;
    for (var i=1;i<wa.length;i++) {
        var w=wa[i];
        measure=ctx.measureText(lastPhrase+splitChar+w).width;
        if (measure<maxPxLength) {
            lastPhrase+=(splitChar+w);
        } else {
            phraseArray.push(lastPhrase);
            lastPhrase=w;
        }
        if (i===wa.length-1) {
            phraseArray.push(lastPhrase);
            break;
        }
    }
    return phraseArray;
}
7 голосов
/ 20 июня 2012

Вот мое вращение ... Я прочитал ответ @ mizar и внес в него некоторые изменения ... и с небольшой помощью я смог получить это.

код удален, см. Скрипту.

Вот пример использования. http://jsfiddle.net/9PvMU/1/ - этот сценарий также можно увидеть здесь и в конечном итоге он стал тем, что я использовал в конце ... эта функция предполагает, что ctx доступна в родительской области ... если нет Вы всегда можете передать его.


редактировать

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


редактировать

Мне стало известно, что в этом коде была небольшая ошибка. Мне потребовалось некоторое время, чтобы обойти это, но здесь это обновляется. Я проверил это сам, и теперь, похоже, работает так, как ожидалось.

function fragmentText(text, maxWidth) {
    var words = text.split(' '),
        lines = [],
        line = "";
    if (ctx.measureText(text).width < maxWidth) {
        return [text];
    }
    while (words.length > 0) {
        var split = false;
        while (ctx.measureText(words[0]).width >= maxWidth) {
            var tmp = words[0];
            words[0] = tmp.slice(0, -1);
            if (!split) {
                split = true;
                words.splice(1, 0, tmp.slice(-1));
            } else {
                words[1] = tmp.slice(-1) + words[1];
            }
        }
        if (ctx.measureText(line + words[0]).width < maxWidth) {
            line += words.shift() + " ";
        } else {
            lines.push(line);
            line = "";
        }
        if (words.length === 0) {
            lines.push(line);
        }
    }
    return lines;
}
6 голосов
/ 31 мая 2010

context.measureText(text).width это то, что вы ищете ...

1 голос
/ 16 декабря 2014

Попробуйте этот скрипт, чтобы обернуть текст на холсте.

 <script>
  function wrapText(ctx, text, x, y, maxWidth, lineHeight) {
    var words = text.split(' ');
    var line = '';

    for(var n = 0; n < words.length; n++) {
      var testLine = line + words[n] + ' ';
      var metrics = ctx.measureText(testLine);
      var testWidth = metrics.width;
      if (testWidth > maxWidth && n > 0) {
        ctx.fillText(line, x, y);
        line = words[n] + ' ';
        y += lineHeight;
      }
      else {
        line = testLine;
      }
    }
    ctx.fillText(line, x, y);
  }

  var canvas = document.getElementById('Canvas01');
  var ctx = canvas.getContext('2d');
  var maxWidth = 400;
  var lineHeight = 24;
  var x = (canvas.width - maxWidth) / 2;
  var y = 70;
  var text = 'HTML is the language for describing the structure of Web pages. HTML stands for HyperText Markup Language. Web pages consist of markup tags and plain text. HTML is written in the form of HTML elements consisting of tags enclosed in angle brackets (like <html>). HTML tags most commonly come in pairs like <h1> and </h1>, although some tags represent empty elements and so are unpaired, for example <img>..';

  ctx.font = '15pt Calibri';
  ctx.fillStyle = '#555555';

  wrapText(ctx, text, x, y, maxWidth, lineHeight);
  </script>
</body>

См. Демо здесь http://codetutorial.com/examples-canvas/canvas-examples-text-wrap.

1 голос
/ 10 ноября 2013

Из сценария здесь: http://www.html5canvastutorials.com/tutorials/html5-canvas-wrap-text-tutorial/

Я добавил поддержку абзацев. Используйте \ n для новой строки.

function wrapText(context, text, x, y, line_width, line_height)
{
    var line = '';
    var paragraphs = text.split('\n');
    for (var i = 0; i < paragraphs.length; i++)
    {
        var words = paragraphs[i].split(' ');
        for (var n = 0; n < words.length; n++)
        {
            var testLine = line + words[n] + ' ';
            var metrics = context.measureText(testLine);
            var testWidth = metrics.width;
            if (testWidth > line_width && n > 0)
            {
                context.fillText(line, x, y);
                line = words[n] + ' ';
                y += line_height;
            }
            else
            {
                line = testLine;
            }
        }
        context.fillText(line, x, y);
        y += line_height;
        line = '';
    }
}

Текст можно отформатировать так:

var text = 
[
    "Paragraph 1.",
    "\n\n",
    "Paragraph 2."
].join("");

Использование:

wrapText(context, text, x, y, line_width, line_height);

вместо

context.fillText(text, x, y);
1 голос
/ 29 мая 2010

посмотрите на https://developer.mozilla.org/en/Drawing_text_using_a_canvas#measureText%28%29

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

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

Я не исследовал, но, возможно, это даже библиотеки javascript, которые делают это для вас

0 голосов
/ 05 октября 2017

Я публикую свою собственную версию, использованную здесь , поскольку ответы здесь были недостаточны для меня. Первое слово нужно было измерить в моем случае, чтобы иметь возможность отрицать слишком длинные слова из небольших областей холста. И мне нужна была поддержка комбо 'break + space,' space + break 'или double-break / абзац-разрыв.

wrapLines: function(ctx, text, maxWidth) {
    var lines = [],
        words = text.replace(/\n\n/g,' ` ').replace(/(\n\s|\s\n)/g,'\r')
        .replace(/\s\s/g,' ').replace('`',' ').replace(/(\r|\n)/g,' '+' ').split(' '),
        space = ctx.measureText(' ').width,
        width = 0,
        line = '',
        word = '',
        len = words.length,
        w = 0,
        i;
    for (i = 0; i < len; i++) {
        word = words[i];
        w = word ? ctx.measureText(word).width : 0;
        if (w) {
            width = width + space + w;
        }
        if (w > maxWidth) {
            return [];
        } else if (w && width < maxWidth) {
            line += (i ? ' ' : '') + word;
        } else {
            !i || lines.push(line !== '' ? line.trim() : '');
            line = word;
            width = w;
        }
    }
    if (len !== i || line !== '') {
        lines.push(line);
    }
    return lines;
}

Поддерживаются любые варианты разрывов строк или разрывов абзацев, удаляются двойные пробелы, а также начальные или конечные разрывы абзацев. Он возвращает либо пустой массив, если текст не помещается. Или массив линий, готовых к рисованию.

0 голосов
/ 02 марта 2017

Это должно привести строки правильно из текстового поля: -

 function fragmentText(text, maxWidth) {
    var lines = text.split("\n");
    var fittingLines = [];
    for (var i = 0; i < lines.length; i++) {
        if (canvasContext.measureText(lines[i]).width <= maxWidth) {
            fittingLines.push(lines[i]);
        }
        else {
            var tmp = lines[i];
            while (canvasContext.measureText(tmp).width > maxWidth) {
                tmp = tmp.slice(0, tmp.length - 1);
            }
            if (tmp.length >= 1) {
                var regex = new RegExp(".{1," + tmp.length + "}", "g");
                var thisLineSplitted = lines[i].match(regex);
                for (var j = 0; j < thisLineSplitted.length; j++) {
                    fittingLines.push(thisLineSplitted[j]);
                }
            }
        }
    }
    return fittingLines;

А затем нарисуйте извлеченные линии на холсте: -

 var lines = fragmentText(textBoxText, (rect.w - 10)); //rect.w = canvas width, rect.h = canvas height
                    for (var showLines = 0; showLines < lines.length; showLines++) { // do not show lines that go beyond the height
                        if ((showLines * resultFont.height) >= (rect.h - 10)) {      // of the canvas
                            break;
                        }
                    }
                    for (var i = 1; i <= showLines; i++) {
                        canvasContext.fillText(lines[i-1], rect.clientX +5 , rect.clientY + 10 + (i * (resultFont.height))); // resultfont = get the font height using some sort of calculation
                    }
0 голосов
/ 25 сентября 2012
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...