Как нарисовать объект SVG, модифицированный Javascript, на холсте HTML5? - PullRequest
0 голосов
/ 14 февраля 2019

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

Я последовал совету по другому вопросу StackOverflow о том, как загрузить и изменить файл SVG в Javascript, который выглядел следующим образом:

<object class="svgClass" type="image/svg+xml" data="image.svg"></object>

, затем в Javascript

document.querySelector("object.svgClass").
    getSVGDocument().getElementById("svgInternalID").setAttribute("fill", "red")

И это работает.Теперь у меня есть измененный SVG, отображаемый на моей веб-странице.

Но я не хочу просто отображать его - я хочу нарисовать его как часть обновления холста HTML5, например:

ctx.drawImage(myModifiedSVG, img_x, img_y);

Если я пытаюсь сохранить результат getSVGDocument () и передать его как myModifiedSVG, я просто получаю сообщение об ошибке.

Как мне сделать вызов холста HTML5 для рисования для моего измененного SVG?


Редактировать: я могу нарисовать изображение SVG на холсте HTML5, выполнив это:

var theSVGImage = new Image();
theSVGImage.src = "image.svg";
ctx.drawImage(theSVGImage, img_x, img_y);

, и это здорово, но я не знаю, как изменить текст / цвета вмой загруженный SVG образ таким образом!Если кто-то может сказать мне, как сделать эту модификацию, то это также будет решением.Я не привязан к прохождению тега HTML объекта.

1 Ответ

0 голосов
/ 15 февраля 2019

За один снимок вы можете перестроить новый SVG-файл, загрузить его в imageи нарисовать его снова на холсте:

async function doit() {
  const ctx = canvas.getContext('2d');
  const images = await prepareAssets();
  let i = 0;
  const size = canvas.width = canvas.height = 500;
  canvas.onclick = e => {
    i = +!i;
    ctx.clearRect(0, 0, size, size);
    ctx.drawImage(images[i], 0,0, size, size);
  };
  canvas.onclick();
  return images;
}

async function prepareAssets() {
  const svgDoc = await getSVGDOM();
  // There is no standard to draw relative sizes in canvas
  svgDoc.documentElement.setAttribute('width', '500');
  svgDoc.documentElement.setAttribute('height', '500');
  // generate the first <img> from current DOM state
  const originalImage = loadSVGImage(svgDoc);
  // here do your DOM manips
  svgDoc.querySelectorAll('[fill="#cc7226"]')
    .forEach(el => el.setAttribute('fill', 'lime'));
  // generate new <img>
  const coloredImage = loadSVGImage(svgDoc);
  
  return Promise.all([originalImage, coloredImage]);
}
  
function getSVGDOM() {
  return fetch('https://upload.wikimedia.org/wikipedia/commons/f/fd/Ghostscript_Tiger.svg')
    .then(resp => resp.text())
    .then(text => new DOMParser().parseFromString(text, 'image/svg+xml'));
}

function loadSVGImage(svgel) {
  // get the markup synchronously
  const markup = (new XMLSerializer()).serializeToString(svgel);
  const img = new Image();
  return new Promise((res, rej) => {
    img.onload = e => res(img);
    img.onerror = rej;
    // convert to a dataURI
    img.src=  'data:image/svg+xml,' + encodeURIComponent(markup);
  });
}

doit()
  .then(_ => console.log('ready: click to switch the image'))
  .catch(console.error);
<canvas id="canvas"></canvas>

Но если вы собираетесь делать это с большим количеством кадров и ожидаете, что это оживит ...

Вывам придется конвертировать svg в операции рисования Canvas.

Вышеприведенный метод является асинхронным, поэтому вы не можете надежно генерировать новые изображения на лету и готовить их для рисования в одном кадре.Вам нужно хранить некоторые из них заранее, но поскольку время, необходимое для загрузки изображения, совершенно случайно (по крайней мере, так должно быть), это может стать настоящим кошмаром программирования.
Добавьте к этому накладные расходыбраузер будет иметь при загрузке целый новый документ SVG каждый кадр (да, браузеры действительно загружают документ SVG, даже если он загружен в image) , затем рисуют его на холсте и, наконец, удаляют изпамяти, которая будет заполнена в кратчайшие сроки, у вас не будет много свободного ЦП, чтобы делать что-либо еще.

Так что лучше всего здесь, вероятно, проанализировать SVG и преобразовать его в операции рисования CanvasRenderingContext2D => Drawэто самостоятельно.

Это достижимо, более того, теперь мы можем передавать атрибуты d непосредственно в конструктор объекта Path2D, и что большинство объектов SVG имеют соответствие в API Canvas2D (мы даже можем использовать фильтры SVG),но это еще много работы.

Так что вы можете посмотреть на библиотеки, которые делают это.Я сам не специалист по библиотекам, и не могу рекомендовать никого, но я знаю, что canvg делает это очень давно, я просто не знаю, выставляют ли они свои объекты jsмногоразовым способом.Я знаю, что Fabric.js делает, но он также поставляется с множеством других функций, которые вам могут не понадобиться.

Выбор за вами.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...