如何使 fabric.js 输出包含 alpha 颜色的 svg?

How do I make fabric.js output svg including alpha color?

我需要将织物 canvas 绘图的结果输出到 SVG 文件。

我使用的是 fabric.js 版本 1.7.6,当我将一条路径绘制到 canvas 并使用 rgba(255,0,0,.15) 之类的 rgba 填充时,生成的 SVG 具有 rgb(0,0,0)。是否需要启用某些设置才能使其输出 alpha 通道?

在我的示例代码中,紫色圆圈正确转换为 SVG,但矩形只显示为黑色。

样本HTML:

<html>
  <head>
    <script src="fabric.js"></script>
  </head>
  <body>

    <div id="canvasHolder" style="border: 3px solid black;">
      <canvas id="canvasElement" width="400" height="400" />
    </div>

    <div id="svgHolder" style="border: 3px solid blue;">
    </div>
  </body>

  <script>
    var canvas = new fabric.Canvas('canvasElement');

    var rect = new fabric.Path('M,0,0,h,100,v,100,h,-100,z',{
        top:100,
        left:100,
        stroke: 'green',
        fill: 'rgba(255,0,0,.15)'
    });
    canvas.add(rect);

    var circ = new fabric.Circle({
        radius: 30,
        top:30,
        left:30,
        stroke: 'blue',
        fill: 'purple'
    });
    canvas.add(circ);

    canvas.renderAll();

    // Make an SVG object out of the fabric canvas
    var SVG = canvas.toSVG();
   document.getElementById('svgHolder').innerHTML = SVG;
  </script>

</html>

输出 SVG:

<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="400" height="400" viewBox="0 0 400 400" xml:space="preserve">
<desc>Created with Fabric.js 1.7.6</desc>
<defs>
</defs>
<path d="M 0 0 h 100 v 100 h -100 z" style="stroke: rgb(0,128,0); stroke-width: 1; stroke-dasharray: none; stroke-linecap: butt; stroke-linejoin: miter; stroke-miterlimit: 10; fill: rgb(0,0,0); fill-rule: nonzero; opacity: 1;" transform="translate(150.5 150.5) translate(-50, -50) " stroke-linecap="round"></path>
</svg>

正如我在评论中所说,这看起来像是一个错误,您应该报告 project's issue tracker

颜色全部转换为 rgb()(rgba、hsl、hsla、hex、关键字),因此不支持 alpha 通道...

目前,这是一个繁重的解决方法:

toSVG 接受一个 reviver 函数,它将接收所有 svg 节点标记。从那里,您可以重新应用正确的样式,但并不那么容易。

我能找到的唯一允许我们识别哪个对象对应于我们获得的 svg 标记的参数是 id 参数。

  • 所以首先,我们将构建一个字典,它将按 id 存储我们的颜色。
  • 然后,我们将为织物的对象分配 ID 和颜色。
  • 最后,在 reviver 中,我们将解析标记以将其转换为 svg 节点,检查其 id 属性,然后更改其 style.fillstyle.stroke 属性,在返回此修改节点的序列化之前。

var canvas = new fabric.Canvas('canvasElement');

var colors_dict = {};
// an helper function to generate and store our colors objects
function getColorId(fill, stroke) {
  var id = 'c_' + Math.random() * 10e16;
  return colors_dict[id] = {
    id: id,
    fill: fill || 'black',
    stroke: stroke || 'black' // weirdly fabric doesn't support 'none'
  }
}

// first ask for the color object of the rectangle
var rect_color = getColorId('hsla(120, 50%, 50%, .5)', 'rgba(0,0,255, .25)');
var rect = new fabric.Path('M,0,0,h,100,v,100,h,-100,z', {
  top: 60,
  left: 60,
  stroke: rect_color.stroke, // set the required stroke
  fill: rect_color.fill, // fill
  id: rect_color.id  // and most importantly, the id
});
canvas.add(rect);

var circ_color = getColorId('rgba(200, 0,200, .7)');
var circ = new fabric.Circle({
  radius: 30,
  top: 30,
  left: 30,
  stroke: circ_color.stroke,
  fill: circ_color.fill,
  id: circ_color.id
});
canvas.add(circ);

canvas.renderAll();

var parser = new DOMParser();
var serializer = new XMLSerializer();
function reviveColors(svg){
  // first we parse the markup we get, and extract the node we're interested in
  var svg_doc = parser.parseFromString('<svg xmlns="http://www.w3.org/2000/svg">' + svg + '</svg>', 'image/svg+xml');
  var svg_node = svg_doc.documentElement.firstElementChild;
  var id = svg_node.getAttribute('id');
  if (id && id in colors_dict) { // is this one of the colored nodes
    var col = colors_dict[id]; // get back our color object
    svg_node.style.fill = col.fill; // reapply the correct styles
    svg_node.style.stroke = col.stroke;
    // return the new markup
    return serializer.serializeToString(svg_node).replace('xmlns="http://www.w3.org/2000/svg', '');
  }
  return svg;
}

// Make an SVG object out of the fabric canvas
var SVG = canvas.toSVG(null, reviveColors);
document.getElementById('svgHolder').innerHTML = SVG;
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.7.12/fabric.js"></script>
<div id="canvasHolder" style="border: 3px solid black;">
<!-- beware canvas tag can't be self-closing -->
<canvas id="canvasElement" width="400" height="200"></canvas>
</div>
<div id="svgHolder" style="border: 3px solid blue;">

颜色被转换为 RGB,因为 svg 规范需要 CSS2 格式的颜色,因此不支持 rgba。

引用:https://www.w3.org/TR/2008/REC-CSS2-20080411/syndata.html#value-def-color

织物通过填充不透明度规则实现透明度。关键是 fabric.Color 颜色解析器看起来像是被 alpha 通道的 .15 表示法所窒息。

请使用 0.15 即可。

我同意 fabric.Color 可以解决这个问题。