6

我有一个 SVG 文件加载到<object>标签中。我正在使用 javascript 来操作这个 svg 中的一些元素。例如:

var theSvgXml = document.getElementById('theObject').contentDocument;
theSvgXml.getElementById('theElementId').style.display = 'inline';
theSvgXml.getElementById('theElementId').style.fill = 'red';
theSvgXml.getElementById('anotherElement').style.display = 'none';

这完美地工作,一切都很好。但我想知道是否可以用画布做同样的事情。我也阅读了有关 kinetic js、fabric js、canvg js 的信息,并且我看到了通过文件目录、xml 或通过图像将 svg 文件加载到画布中的各种方法。

但是在将这个 svg 文件绘制到画布中之后,我可以通过它们的 ID 来操作元素吗?

SVG 是使用 Adove Illustrator 创建的,每个图层或组都被赋予一个 ID,可以使用 css 选择器访问。同样,这可以在画布上绘制 SVG 后在画布上完成吗?(请注意,SVG 是巨大的,我考虑画布解决方案的唯一原因是因为 svg 没有显示插画家的乘法效果来创建阴影)

任何帮助将不胜感激。谢谢你。

代码片段:

<g id="Pockets">
<g id="Pen__x26__Radio_Arm_Pocket" class="st791"> … </g>
<g id="Pen_Pocket_Arm" class="st791"> … </g>
<g id="Card_Zipper_Arm_Pocket" class="st791"> … </g>
<g id="Radio_Pocket_Arm_Pocket" class="st791"> … </g>
<g id="Angled_Chest_Pocket_Right" class="st791"> … </g>
<g id="Angled_Chest_Pocket_Left" class="st791"> … </g>
<g id="Angled_Chest_Pocket_left_and_Right" class="st791"> … </g>
<g id="Chest_Pocket_Right" class="st791"> … </g>
<g id="Chest_Pocket_Left" class="st791"> … </g>
<g id="Chest_Pocket_left_and_Right" class="st791"> … </g>
<g id="Tool_Pocket" class="st791"> … </g>
<g id="Cargo_x2F_Mobile_Pocket_Velcro" class="st791"> … </g>
<g id="Cargo_x2F_Mobile_Pocket_Zip" class="st791"> … </g>
<g id="Cargo_x2F_Mobile_Pocket_Button" class="st791"> … </g>
<g id="Cargo_Pocket_Velcro" class="st791"> … </g>
<g id="Cargo_Pocket_Button" class="st791"> … </g>
<g id="Cargo_Pocket_Zip" class="st791"> … </g>
<g id="Back_Pocket_Right_Velcro" class="st791"> … </g>
<g id="Back_Pocket_left_Velcro" class="st791"> … </g>
<g id="Back_Pocket_left_and_Right_Velcro" class="st791"> … </g>
<g id="Back_Pocket_Right_Velcro_Button" class="st791"> … </g>
<g id="Back_Pocket_left_Velcro_Button" class="st791"> … </g>
<g id="Back_Pocket_left_and_Right_Button" class="st791"> … </g>
<g id="Back_Pocket_Right_Zip" class="st791"> … </g>
<g id="Back_Pocket_left_Zip" class="st791"> … </g>
<g id="Back_Pocket_left_and_Right_Zip" class="st791"> … </g>
</g>

从 adobe illustrator 导出文件后,您可以从 xml 片段中看到,每个组都设置了一个 ID。如何将这些作为对象保存在画布中(如 Fabrics.js 建议使用 getObjects() 方法)?有没有办法做到这一点?如果是,我如何引用这些组?此外,阴影是一个关键问题,我不想使用闪光灯。谢谢

4

4 回答 4

10

以下是使用 Fabric 执行此操作的方法:

fabric.loadSVGFromURL('/assets/72.svg', function(objects, options){

  var group = fabric.util.groupSVGElements(objects, options);

  group
    .set({ left: 300, top: 200 })
    .scaleToWidth(500)
    .setCoords();

  canvas.add(group);
}, reviver);

function reviver(element, object) {
  object.id = element.getAttribute('id');
}

代码应该是不言自明的。我们加载 SVG;Fabric 在内部解析它,吐出代表每个元素的一组对象。然后,我们将这些元素分组,并将它们以一块的形式添加到画布上。Reviver 负责读取每个 SVG 元素的 id 并将其分配给相应的结构实例。

在http://fabricjs.com/kitchensink/中运行这个片段,你会得到:

已加载 svg

让我们检查这个分组对象:

canvas.item(0) + ''; "#<fabric.PathGroup (29303): { top: 200, left: 300 }>"

及其孩子:

canvas.item(0).getObjects(); // Array[2287]

让我们通过 id 检索一个:

var greenland = canvas.item(0).getObjects().filter(function(obj) {
  return obj.id === 'path4206';
})[0];

如您所见,这都是普通的旧 Javascript。现在让我们更改该特定对象/形状的颜色:

greenland.fill = 'red';

红色的格陵兰

于 2013-09-15T11:34:55.613 回答
1

只是关于你的情况的一些随机想法

虽然 html canvas 确实有一些合成模式,但 canvas 没有图像乘法混合。

但是,Firefox 可以!这是合成的多重混合扩展:

ctx.globalCompositeOperation = 'multiply';

具有 SVG-->Canvas 转换器的主要画布库是 FabricJS 和 KineticJS,但它们都没有实现这个乘法过滤器。在这两者中,FabricJS 在这一点上更支持 SVG,我看到 Kangax(FabricJS 的创建者)对您的帖子发表了评论。你可能会很好地问他是否会添加一个乘法过滤器;)

由于您似乎可以访问 Adob​​e Illustrator,因此您可以尝试 Mike Swanson 令人印象深刻的 SVG-->Canvas 翻译器。我不知道它是否处理图像过滤器,但它在采用标准 Adob​​e Illustrator SVG 和创建 Canvas 绘图方面做得非常出色(如果你问我,这是非常令人印象深刻的应用程序!): http ://blog.mikeswanson.com/post /29634279264/ai2canvas

在 SVG 内部思考——你应该查看 SVGJS 库:

SVGJS 是一个 SVG 库,可让您通过 ID 操作 SVG 元素。

https://svgdotjs.github.io/

还有一个从 Illustrator 导入的扩展:

https://svgdotjs.github.io/importing-exporting/

最后 SVGJS 也有一个扩展来做一个变暗的图像过滤器(不是相乘,而是关闭)

https://svgdotjs.github.io/plugins/svg-filter-js/

如果你变得绝望......(你不太可能会变得如此绝望!)

您可以通过使用 context.getImageData 抓取画布像素,然后在要相乘的像素的每个 r、g、b 元素上运行此函数来“滚动您自己的”乘法过滤器。

function multiply(top, bottom){
    return(top*bottom/255;
}

祝你好运!

于 2013-09-14T04:56:06.387 回答
1

<canvas>对像素进行操作,它没有像 SVG 这样的对象/元素的概念。因此,要操作图像,您需要操作底层 SVG 并在每次更改后将其重新渲染到画布上。

您可以将 SVG 图像放在<canvas>using.drawImage方法上。

var cvs = document.createElement('canvas'), ctx = cvs.getContext('2d');
cvs.width = 600;
cvs.height = 300;
document.body.appendChild(cvs);

var img = new Image();
img.width = '600';
img.height = '300';

img.onload = function(){
    ctx.drawImage(img,0,0);
}
img.src = 'file.svg';

但是您不能操作以这种方式创建的 SVG。为此,您应该尝试将<svg>元素 DOM 转换为 base46 编码字符串,将其设置为图像的源,然后渲染到画布。

所以在上面的例子中,而不是img.src = 'file.svg'你需要

img.src = 'data:image/svg+xml;base64,' + btoa( document.getElementById('mySvgElement').outerHTML );
于 2013-09-11T11:37:53.940 回答
0

如果您使用像动力学这样的框架,那么您可以通过它们的 id 或名称访问和操作图层和对象。

于 2013-09-11T11:10:47.180 回答