Перевод не завершен. Пожалуйста, помогите перевести эту статью с английского.

Прежде чем начать

Для понимания этой статьи, рекомендуется понимать JavaScript, Canvas API и DOM API

И также будет хорошо, если Вы также знакомы с SVG

Несмотря на то, что это не очень тривиально (по причинам безопасности), существует возможность вставить DOM-объекты, такие как HTML в canvas. В этой статье, взятой из  этого поста автора Роберта О'Каллахана, описано как можно это сделать надежно, безопасно и в соответствии со спицификацией.

Обзор

Нельзя просто так взять и вставить HTML в canvas. Вместо этого, вы должны использовать  SVG картинку, содержащую то, что вы хотите срендерить. Чтобы нарисовать HTML-содержимое, вам нужно использовать элемент <foreignObject>, содержащий HTML, а уже затем рисовать SVG-изображение внутри canvas.

Шаг за шагом

Единственный подвох здесь — и это возможно преувеличение, — создание SVG для вашего изображения. Всё, что вам нужно сделать — это создать строку, содержащую XML для SVG и построить Blob, состоящий из следующих частей:

  1. MIME-тип Blob-а  должен быть "image/svg+xml".
  2. <svg> элемент.
  3. Внутри должен быть <foreignObject> элемент.
  4. Сам (хорошо отформатированный) HTML, вложенный в <foreignObject>.

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

Пример

HTML

<canvas id="canvas" style="border:2px solid black;" width="200" height="200">
</canvas>

JavaScript

var canvas = document.getElementById('canvas');
var ctx = canvas.getContext('2d');

var data = '<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200">' +
           '<foreignObject width="100%" height="100%">' +
           '<div xmlns="http://www.w3.org/1999/xhtml" style="font-size:40px">' +
             '<em>I</em> like ' + 
             '<span style="color:white; text-shadow:0 0 2px blue;">' +
             'cheese</span>' +
           '</div>' +
           '</foreignObject>' +
           '</svg>';

var DOMURL = window.URL || window.webkitURL || window;

var img = new Image();
var svg = new Blob([data], {type: 'image/svg+xml'});
var url = DOMURL.createObjectURL(svg);

img.onload = function() {
  ctx.drawImage(img, 0, 0);
  DOMURL.revokeObjectURL(url);
}

img.src = url;

Этот пример изображен ниже.

ScreenshotLive sample

Переменной data присвоено содержание SVG-картинки, которую, в свою очередь, мы хотим нарисовать внутри canvas.

Затем мы создаем новый HTML элемент <img>, вызывая new Image(), добавляем в него data, выделяем объект URL, и рисуем картинку в контексте canvas, вызывая  drawImage() на загрузку картинки.

Безопасность

Вы можете задаться вопросом, будет ли это безопасно в свете опасений относительно возможности чтения конфиденциальных данных из элемента canvas. Ответ таков: это решение полагается на тот факт, что реализация SVG-изображений очень строгая. SVG-картинки не позволяют загружать никакие внешние ресурсы, например даже те что пришли с того же домена. Такие реусрсы как растровые изобращения (например, JPEG) или <iframe>-ы должны быть встроены как data: URIs.

В добавок к этому, вы не можете включать скрипт в SVG-изображение, поэтому нет риска доступа к DOM из других скриптов, и DOM elements в SVG-картинках не могут принимать события ввода, не оставляя таким образом способа загрузить привилегированную информацию в элемент управления формой (такую как полный путь до файла <input> элемент) и срендерить его, а затем вытащить эту информацию, читая пиксели.

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

Полученный canvas должен быть чистым, что означает что вы не можете вызвать toBlob(function(blob){…}) чтобы вернуть blob для canvas, или toDataURL() чтобы вернуть Base64-encoded data: URI.

Рисование в HTML

Поскольку SVG должен быть валидным XML, вам потребуется парсить HTML, чтобы получить хорошо-отформаттированный результат от HTML-парсера. Ниже представлен код, демонстрирующий самый простой путь чтобы распарсить html.

var doc = document.implementation.createHTMLDocument('');
doc.write(html);

// You must manually set the xmlns if you intend to immediately serialize 
// the HTML document to a string as opposed to appending it to a 
// <foreignObject> in the DOM
doc.documentElement.setAttribute('xmlns', doc.documentElement.namespaceURI);

// Get well-formed markup
html = (new XMLSerializer).serializeToString(doc);

Смотрите также

Метки документа и участники

 Внесли вклад в эту страницу: bad4iz, komuroe
 Обновлялась последний раз: bad4iz,