Fabric.js: 如何将从 URL 加载的 SVG 转换为灰度?

Fabric.js: How to turn SVG loaded from URL into grayscale?

我正在开发一个产品定制工具,用户可以在其中上传他们的 SVG 文件,以便将它们打印在产品上。

现在,我需要能够将这个导入的徽标变成灰度,以向他们展示如果他们选择激光打印类型会是什么样子。

我知道有一个灰度过滤器可以做到这一点,但它似乎不适用于我加载了这段代码的元素(不要介意来自 smarty 的 {$var}):

fabric.loadSVGFromURL(r.filepath, function(objects, options){

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

        //loadedObjects.setCoords();

        //loadedObjects.setCoords();
        var scaleX = canvas_{$image.id_image}.width / loadedObjects.width;
        var scaleY =  canvas_{$image.id_image}.height / loadedObjects.height;
        var scale = (scaleX > scaleY) ? scaleY : scaleX;

        canvas_{$image.id_image}.add(loadedObjects).renderAll();
        loadedObjects.setCoords();
        loadedObjects.set({
            top: 0,
            left: 0,
            scaleX: scale,
            scaleY: scale,
            selectable: true
        });

        loadedSVG_{$image.id_image} = loadedObjects;

        canvas_{$image.id_image}.renderAll();

    });

我想知道实现此结果的最佳方法是什么。我应该把我的元素变成 fabric.Image 吗?这不会通过破坏向量来影响调整大小的能力吗?

还有其他方法吗?

如果您直接使用 Fabric.Image.fromURL() 将 svg 作为图像加载,您的 svg 图像不会降低质量,因为 <img> 标签用于 store svg 图像仍然在向量中。

但是,在应用图像滤镜时,滤镜会在 <canvas> 元素上以 img 标签的默认大小应用一次,这会导致图像光栅化。

一种骇人听闻的方法是首先将 svg 加载到对象或 iframe 或嵌入标记中,将所需的过滤器附加为 SVGFilter,然后将 svg 从 DOM 字符串加载到 Fabric 中.

但这有一些限制:

  • 跨源请求被阻止,并且这些标签上没有 crossOrigin 属性。
  • 您必须自己进行解析。
  • 放大时 FF 中的图像框似乎存在错误...

无论如何,这里是代码和 a plunker example 现场展示。

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

   var obj = document.createElement('object');
    //make it invisible
   obj.style.height = obj.style.width = 0;
    // we have to append it so it does load
   document.body.appendChild(obj);

   obj.onload = function() {
     // get the def node of our svgFilter
     var defs = document.getElementById('def').cloneNode('true');
     // get the doc of our object
     var doc = obj.contentDocument || obj.contentWindow.documentElement;
     // we can now remove the <object>
     document.body.removeChild(obj);
     // the target svg
     var svg = doc.querySelector('svg');
     svg.insertBefore(defs, svg.firstElementChild);
     // set the filter on the whole svg
     svg.setAttribute('filter', 'url(#desaturate)');
     // encode it to a dataURI
     var svgStr = new XMLSerializer().serializeToString(svg);
     var svgURL = 'data:image/svg+xml; charset=utf8, ' + encodeURIComponent(svgStr);
     // load it as an image
     fabric.Image.fromURL(svgURL, function(img) {
       canvas.add(img).renderAll();
     });

     // We could have tried with loadSVGFrom... but the filter isn't applied then
     /*
     fabric.loadSVGFromString(svgStr, function(objects, options) {
       var loadedObject = fabric.util.groupSVGElements(objects, options);
       canvas.add(loadedObject);
       loadedObject.center().setCoords();
       canvas.renderAll();
     });
     fabric.loadSVGFromURL(svgURL, function(objects, options) {
       var loadedObject = fabric.util.groupSVGElements(objects, options);
       canvas.add(loadedObject);
       loadedObject.center().setCoords();
       canvas.renderAll();
     });
     */
   };
   obj.data = "yourFile.svg";
<!-- append your svg filter in the main doc -->
<svg id="filters" width="0" height="0">
  <defs id="def">
    <filter id="desaturate">
      <feColorMatrix type="matrix" values="0.3333 0.3333 0.3333 0 0
                                             0.3333 0.3333 0.3333 0 0
                                             0.3333 0.3333 0.3333 0 0
                                             0      0      0      1 0" />
    </filter>
  </defs>
</svg>
<canvas id="canvas" width="500" height="500"></canvas>

另一种解决方案是先在 up-scaled canvas 上绘制 svg,在其 dataURL 上应用过滤器,然后缩小图像,但这也会产生一些压缩伪影。 ..

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

var scaleSVG = function(url, scale, callback) {
  var c = document.createElement('canvas');
  var img = new Image();
  img.crossOrigin = 'Anonymous';
  img.onload = function() {
    c.width = this.width * scale;
    c.height = this.height * scale;
    c.getContext('2d').drawImage(this, 0, 0, c.width, c.height);
    callback(c.toDataURL());
  };
  img.src = url;
};

scaleSVG('https://dl.dropboxusercontent.com/s/b7qcju9ubmdtigj/ball.svg',
  10, function(dataURL) {
    fabric.Image.fromURL(dataURL, function(img) {
      // scale in the other side
      var oImg = img.scale(.1);
      var filter = new fabric.Image.filters.Grayscale();
      oImg.filters.push(filter);
      canvas.add(oImg);
      oImg.applyFilters(canvas.renderAll.bind(canvas));
    }, {
      crossOrigin: 'Anonymous'
    });
  });
<canvas id="canvas" width="500" height="500"></canvas>
<script src="https://cdnjs.cloudflare.com/ajax/libs/fabric.js/1.5.0/fabric.js"></script>