Я пытаюсь преобразовать сгенерированный SVG в PNG/JPG через HTML5 Canvas. Пока что этот метод работает со всем, что я ему предлагал, за исключением одного исключения. Когда элемент в исходном SVG имеет атрибут clip-path
, canvas.toDataURL()
возвращает пустой URL-адрес (т. е. data;
).
Это соответствующие функции, которые я использую для выполнения преобразования.
// expects svg dataurl
// returns a dataurl
async function _svgToRasterImage(url, options) {
const image = await _svgToImageElement(url)
const context = document.createElement('canvas').getContext('2d', {
antialias: options.rasterAA
})
context.canvas.width = options.rasterW || image.width
context.canvas.height = options.rasterH || image.height
context.drawImage(image,
0, 0, image.width, image.height,
0, 0, context.canvas.width, context.canvas.height
)
return context.canvas.toDataURL(
options.rasterTarget,
options.rasterQuality
)
}
// expects svg dataurl
// returns an SVGImageElement
function _svgToImageElement(url) {
return new Promise(async (resolve) => {
const image = new Image()
image.addEventListener(
'load',
resolve(image),
{ once: true }
)
image.src = url
})
}
Вот пример проблемного SVG
(Я сократил некоторые встроенные элементы, например, href="data:image/png;base64,..."
).
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.dev/svgjs" width="180" height="252">
<svg width="180" height="252" x="0" y="0">
<g>
<rect width="162" height="226.8" x="9" y="12.600000000000001" fill-opacity="0" rx="7.2" ry="7.2" stroke-width="7.2" stroke="#00ff00"/>
</g>
<g>
<ellipse rx="81" ry="113.4" cx="90" cy="126" fill-opacity="0" stroke-width="7.2" stroke="#0000ff"/>
</g>
<g clip-path="url("#SvgjsClipPath1009")">
<image width="256" height="256" xlink:href="data:image/png;base64,..." transform="matrix(0.6328125,0,0,0.6328125,9,45.00000000000001)"/>
</g>
</svg>
<defs>
<clipPath id="SvgjsClipPath1009">
<rect width="162" height="226.8" x="9" y="12.600000000000001"/>
</clipPath>
</defs>
</svg>
Хотя я не уверен, я уверен, что приведенный выше SVG действителен, потому что он правильно отображается в DOM как в Chrome, так и в Firefox; однако существует явная вероятность того, что я упускаю что-то очевидное, что браузер игнорирует (или исправляет для меня в этом случае)!
Да, я передаю SVG dataurl, закодированный в base64, в функцию (например, data:image/svg+xml;base64,...
. Я использую SVG.js для создания изображений SVG. "
автоматически экранируется этой библиотекой. Сначала я тоже подумал, что это странно ; однако другие встроенные ресурсы (например, шрифты) используют идентичную схему url("...")
и не вызывают проблем.
В вашем загрузчике изображений есть ошибка, он не ждет загрузки изображения перед вызовом resolve(img)
, он устанавливает результат этого вызова в качестве обработчика события и, таким образом, вызывает его напрямую. jsfiddle.net/tmqah64o
Я откатил ваше последнее редактирование, StackSnippets предназначены только для фактически исполняемых фрагментов кода. Этот фрагмент ничего не делает и поэтому не нуждается в StackSnippet.
Это замечательно, стрелочные функции снова превзошли меня. Мне любопытно, почему эта ошибка возникает только при наличии clip-path
, поскольку она не кажется связанной? Я тестирую ваше решение в своей реальной кодовой базе, пока мы говорим, и оно работает отлично. Что меня сбивает с толку, так это то, что я могу нормально отображать изображения без clip-path ??
Вероятно, ваш браузер смог отобразить весь svg почти синхронно, но для получения пути клипа они идут по асинхронному пути (обычно путь клипа может вызывать сетевой запрос, поскольку они могут указывать на внешний файл, хотя в <img > так нельзя).
Спасибо @Kaiido за помощь!
В вашем загрузчике изображений есть ошибка, он не ждет загрузки изображения перед вызовом resolve(img), он устанавливает результат этого вызова в качестве обработчика события и, таким образом, вызывает его напрямую.
Решение состоит в том, чтобы использовать функцию стрелки в обратном вызове прослушивателя событий, потому что прямой вызов resolve(image)
будет немедленно выполнен, разрешая обещание до того, как изображение фактически загрузится.
function _svgToImageElement(url) {
return new Promise(async (resolve) => {
const image = new Image()
image.addEventListener(
'load',
e => resolve(image), // <-- DO
// resolve(image), <-- DON'T
{ once: true }
)
image.src = url
})
}
Удивительно, сколько боли могут причинить 3 недостающих персонажа.
Итак, у вас есть SVG-разметка в файле
url
, который вы передаете функции? Является ли":
плохой копией-вставкой?