MDN wants to learn about developers like you: https://qsurvey.mozilla.com/s3/MDN-dev-survey

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

Before you start

To understand this article, it is recommended to be comfortable with JavaScript, the Canvas API and the DOM API

It's even better if you are also familiar with 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);

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

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

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