如何在HTML5画布上绘制经过Javascript修改的SVG对象? [英] How do I draw a Javascript-modified SVG object on a HTML5 canvas?

查看:124
本文介绍了如何在HTML5画布上绘制经过Javascript修改的SVG对象?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我要实现的总体任务是加载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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆