【问题标题】:Fabric.js: How to turn SVG loaded from URL into grayscale?Fabric.js:如何将从 URL 加载的 SVG 转换为灰度?
【发布时间】:2016-04-20 13:24:58
【问题描述】:

我正在开发一种产品定制工具,用户可以在其中上传他们的 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 吗?这不会通过破坏向量来影响调整大小的能力吗?

还有其他方法吗?

【问题讨论】:

    标签: canvas svg fabricjs


    【解决方案1】:

    如果您确实使用Fabric.Image.fromURL() 直接将您的svg 加载为图像,您的svg 图像不会降低质量,因为用于存储 svg 图像的<img> 标记仍在矢量中.

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

    一种不成熟的方法是先将 svg 加载到对象或 iframe 或 embed 标记中,将所需的过滤器附加为 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>
    

    另一种解决方案是首先在放大的画布上绘制 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>

    【讨论】:

    • 第一个解决方案运行良好,第二个解决方案与.cloneAsImage 存在相同的压缩问题。无论如何,非常感谢你的回答,我从来没有想过这样做!
    猜你喜欢
    • 2021-11-08
    • 2020-12-02
    • 1970-01-01
    • 2013-06-11
    • 2018-05-22
    • 2020-04-29
    • 2020-04-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多