SVG与HTML5 Canvas实现的过滤器

时间:2016-07-28 17:19:38

标签: javascript canvas svg html5-canvas

我正在尝试导出已使用SVG过滤器修改过的图像以包含投影。由于CSS目前不支持的阴影传播能力,我正在使用SVG过滤器代替CSS。不幸的是,canvas也不支持类似阴影传播功能的Photoshop。 Canvas将编写图像,但不包括SVG滤镜,只包含画布特定的阴影滤镜。

最终目标是导出包含过滤器的jpeg图像。所以这将是SVG>画布>图像。

所以我想我的问题是,在绘制图像时如何欺骗画布以包含SVG滤镜?

这是我到目前为止所拥有的。

图片HTML:

<img ng-src="logo.png" style="-webkit-filter: url("#logo-filter"); filter: url("#logo-filter");" />

SVG过滤器设置:

    <svg>
      <defs>
        <filter id="logo-filter" x="0" y="0" width="200%" height="200%">
            <feFlood result="flood" flood-color="#00FFFF" flood-opacity="1"></feFlood>
            <feComposite in="flood" result="mask" in2="SourceGraphic" operator="in" />
            <feOffset in="mask" result="offset"  dx="10" dy="10" />
            <feGaussianBlur in="offset" result="blurred" stdDeviation="3" />
            <feComponentTransfer>
                    <feFuncA type="gamma" exponent="0.5" amplitude="3" in="blurred" result="blurred2" />
            </feComponentTransfer>
            <feMerge>
            <feMergeNode in="blurred2"></feMergeNode>
            <feMergeNode in="SourceGraphic"></feMergeNode>
        </feMerge>
        </filter>
    </defs>
</svg>

Canvas实施:

function plotLogo(img, logo, container) {
      var canvas = document.getElementById("logo-canvas");
      var position = getOffset($('#'+container.id+'-logo'));

      var logo_image = new Image();

      logo_image.src = img;

      logo_image.onload = function(){
        canvas.width = container.width;
        canvas.height = container.height;
        var context = canvas.getContext('2d');

        /* StackOverflow Comment: Works but with no shadow spread */
        context.shadowOffsetX = logo.shadow.offsetX;
        context.shadowOffsetY = logo.shadow.offsetY;
        context.shadowColor = logo.shadow.color;
        context.shadowBlur = logo.shadow.blur*2; //Multiplied by 2 to get closest look to webkit shadow

        context.drawImage(logo_image, position.posX, position.posY, logo.width, logo.height);
      };
    }

1 个答案:

答案 0 :(得分:1)

我找到了自己问题的答案,这是我提出的解决方案。

我必须让SVG基本上反映用户使用svg图像标记操作的内容。两者都链接到filter:url(#logo-filter)。

<svg id="svg-logo" height="175" width="600">
    <defs>
        <filter id="logo-filter" x="-50%" y="-50%" width="200%" height="200%">
            <feFlood result="flood" flood-color="[[params.logo.shadow.color]]" flood-opacity="1"></feFlood>
            <feComposite in="flood" result="mask" in2="SourceGraphic" operator="in" />
            <feOffset in="mask" result="offset"  dx="[[params.logo.shadow.offsetX]]" dy="[[params.logo.shadow.offsetY]]" />
            <feGaussianBlur in="offset" result="blurred" ng-stddeviation="[[params.logo.shadow.blur]]" />
            <feComponentTransfer>
                    <feFuncA type="gamma" exponent="0.5" amplitude="[[params.logo.shadow.spread]]" in="blurred" result="blurred2" />
            </feComponentTransfer>
            <feMerge>
            <feMergeNode in="blurred2"></feMergeNode>
            <feMergeNode in="SourceGraphic"></feMergeNode>
        </feMerge>
        </filter>
    </defs>
    <image id="svg-logo-image" xlink:href="logo.png" filter="url(#[[svg.logo.filter]])" x="0" y="0" />
</svg>

然后收集SVG信息并使用window.btoa转换为base64编码。请注意,除非xlink:href也在base64图像代码中,否则它将无效!

 var svg = document.getElementById('svg-'+type);
 var svgData = new XMLSerializer().serializeToString(svg);

 var encodedData = window.btoa(unescape(encodeURIComponent(svgData)));
 var newSrc = 'data:image/svg+xml;base64,'+encodedData;

这是我目前拥有的全部功能。

function plotImage(element, container, index, img) {

  var type = img ? 'logo' : 'address';
  var canvas = document.getElementById(type+'-canvas');
  var svg = document.getElementById('svg-'+type);
  var svgElem = document.getElementById('svg-'+type+'-image');
  var position = getOffset($('#'+container.name+'-'+type), $('#'+container.name));


  svg.setAttribute('width', container.width);
  svg.setAttribute('height', container.height);

  if(typeof img !== 'undefined') {

    svgElem.setAttribute('x', position.x);
    svgElem.setAttribute('y', position.y);
    svgElem.setAttribute('width', element.width);
    svgElem.setAttribute('height', element.height);
    svgElem.setAttribute('xlink:href', img);

  } else {

    svgElem.setAttribute('x', position.x);
    svgElem.setAttribute('y', position.y+(element.size/3)); // Adjust Y via 1/3 of font-size

  }

  var svgData = new XMLSerializer().serializeToString(svg);

  var encodedData = window.btoa(unescape(encodeURIComponent(svgData)));
  var newSrc = 'data:image/svg+xml;base64,'+encodedData;

  // Alternate Method, (slower)
  // var svgBlob = new Blob([svgData], {type: "image/svg+xml;charset=utf-8"});
  // var newSrc = window.URL.createObjectURL(svgBlob);

  var image = new Image();
  image.src = newSrc;
  image.onload = drawCanvas(image, canvas, container);

}