如何在HTML5画布上绘制经过Javascript修改的SVG对象? [英] How do I draw a Javascript-modified SVG object on a HTML5 canvas?
问题描述
我要实现的总体任务是加载SVG图像文件,在某处修改颜色或文本,然后将其绘制到HTML5画布上(大概使用drawImage(),但任何合理的选择都可以)
The overall task I'm trying to achieve is to load an SVG image file, modify a color or text somewhere, and then draw it onto an HTML5 canvas (presumably with drawImage(), but any reasonable alternative would be fine).
我遵循了有关如何在Javascript中加载和修改SVG文件的另一个StackOverflow问题的建议,就像这样:
I followed advice on another StackOverflow question on how to load and modify a SVG file in Javascript, which went like this:
<object class="svgClass" type="image/svg+xml" data="image.svg"></object>
Javascript之后
followed in Javascript by
document.querySelector("object.svgClass").
getSVGDocument().getElementById("svgInternalID").setAttribute("fill", "red")
那行得通.现在,我的网页上显示了修改后的SVG.
And that works. I now have the modified SVG displaying in my web page.
但是我不想只显示它-我想将其绘制为HTML5画布更新的一部分,如下所示:
But I don't want to just display it - I want to draw it as part of an HTML5 canvas update, like this:
ctx.drawImage(myModifiedSVG, img_x, img_y);
如果我尝试存储getSVGDocument()的结果并将其作为myModifiedSVG传入,那么我只会收到一条错误消息.
If I try storing the result of getSVGDocument() and passing that in as myModifiedSVG, I just get an error message.
如何为修改后的SVG进行HTML5画布绘制调用?
How do I make the HTML5 canvas draw call for my modified SVG?
通过执行以下操作,我已经可以在HTML5画布上绘制SVG图像了:
I can draw an SVG image on an HTML5 canvas already through doing this:
var theSVGImage = new Image();
theSVGImage.src = "image.svg";
ctx.drawImage(theSVGImage, img_x, img_y);
太好了,但是我不知道如何用这种方式修改已加载的SVG图像中的文本/颜色!如果有人可以告诉我如何进行修改,那也将是一个解决方案.我不依赖于通过对象HTML标记.
and that's great, but I don't know how to modify text/colors in my loaded SVG image that way! If someone could tell me how to do that modification, then that would also be a solution. I'm not tied to going through the object HTML tag.
推荐答案
对于一个镜头,您可以重建一个新的svg文件,将其加载到< img>中,然后在画布上再次绘制:
For a one shot, you could rebuild a new svg file, load it in an <img> and draw it again on the canvas:
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>
但是如果您要使用很多帧,并希望它能动画……
But if you are going to do it with a lot of frames, and expect it to animate...
您将必须将svg转换为Canvas绘图操作.
上面的方法是异步的,因此您不能即时可靠地生成新图像并使它准备好在单个帧中绘制.您需要提前存储其中一些,但是由于加载图像所需的时间是完全随机的(至少应该是随机的),这可能是真正的编程梦night.
此外,浏览器每帧要加载整个新的SVG文档的开销(是的,即使加载到< img>中,浏览器也确实加载了SVG文档),然后将其绘制在画布,最后将其从内存中删除,这将很快被占用,您将没有太多的空闲CPU来做其他事情.
The method above is asynchronous, so you cannot reliably generate new images on the fly and get it ready to be drawn in a single frame. You need to store a few of these ahead of time, but since how long it will take to load the image is completely random (at least it should be) this might be a real programming nightmare.
Add to that the overhead the browser will have in loading a whole new SVG document every frame (yes, browsers do load the SVG document even when loaded inside an <img>), then paint it on the canvas, and finally remove it from the memory which will get filled in no time, you won't have a much free CPU to do anything else.
所以这里最好的方法是解析您的SVG并将其转换为CanvasRenderingContext2D绘图操作=>自己绘制.
So the best here is probably to parse your SVG and to convert it to CanvasRenderingContext2D drawing operations => Draw it yourself.
这是可以实现的,此外,现在我们可以将d
属性直接传递到Path2D对象构造函数中,并且大多数SVG对象在Canvas2D API中都有对应关系(我们甚至可以使用SVG过滤器),但是仍然很多工作.
This is achievable, moreover now that we can pass d
attributes directly into Path2D object constructor, and that most of SVG objects have correspondence in the Canvas2D API (we even can use SVG filters), but that's still a lot of work.
因此,您可能需要查看执行此操作的库.我自己不是图书馆专家,我也不推荐任何人,但是我知道 canvg 这样做已经很长时间了,我只是不知道他们是否以可重用的方式公开了他们的js对象.我知道 Fabric.js 确实可以,但是它还带有许多您可能不需要的其他功能.
So you may want to look at libraries that do that. I'm not an expert in libraries myself, and I can't recommend any, but I know that canvg does that since a very long time, I just don't know if they do expose their js objects in a reusable way. I know that Fabric.js does, but it also comes with a lot of other features that you may not need.
由您选择.
这篇关于如何在HTML5画布上绘制经过Javascript修改的SVG对象?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!