【问题标题】:Clipping image to an svg将图像裁剪为 svg
【发布时间】:2016-09-10 14:00:24
【问题描述】:

有没有办法使用CanvasRenderingContext2D 将图像剪辑为 SVG 形状?
我正在尝试使用不同的 SVG 形状来按需显示图像的各个部分。

例如 - 带有 <ellipse> 元素(或等效的 <path>)的 SVG 将允许我显示图像的圆形部分。

CanvasRenderingContext2D.clip() 似乎与我所需要的很接近,但我找不到任何关于如何将它与 SVG 一起使用,或者如何将 SVG 绘制为路径的信息。

我正在考虑的另一个方向是将剪切区域保存为<path> 元素并手动将其转换为CanvasRenderingContext2D 等效方法,例如lineToarc

【问题讨论】:

  • svg 图像在画布上渲染时会变成光栅并丢失所有矢量信息。我能想到的两种方法来做你想做的事:首先解析你的 svg,然后使用 canvas API 来绘制这些形状(路径命令非常相似,所以它不是那么难,像 fabricjs 这样的库甚至可以很好地处理它你)或使用 globalCompositeOperation 而不是剪辑。但请注意,对于后者,Firefox 在将 gCO 直接应用于 svg 图像时存在错误,这会迫使您首先在第二个画布上绘制 svg;并且 Edge 之前的 IE 在绘制 svg 时会污染画布。

标签: javascript canvas svg clip


【解决方案1】:

@Kaiido 为您提供了一些很好的建议(请参阅他对问题的评论)。

  • 将 SVG 绘制到画布上并使用 globalCompositeOperation 而不是剪辑。但是 Firefox 在将 gCO 直接应用于 svg 图像时存在一个错误,这会迫使您首先在第二个画布上绘制 svg;并且 Edge 之前的 IE 在绘制 svg 时会污染画布。

  • 首先解析你的 svg,然后使用 canvas API 来绘制这些形状(路径命令非常相似,所以它不是那么难,像 fabricjs 这样的库甚至可以为你很好地处理它)

另一种选择是将您的 SVG 绘图转换为 .png 格式,并使用该图像 + globalCompositeOperation 将您的图像剪辑到 .png 形状内。这避免了 SVG 的跨浏览器问题。

但是,如果您的剪切形状只是简单的 SVG 路径(椭圆等),那么您可能会忘记 SVG 并使用画布路径命令来绘制路径。

我将重新发布以前的 SO Q&A 以说明在画布路径内进行剪辑:

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;

var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/kidwallpaper.jpg";
function start(){

  // resize the canvas to equal the image size
  var iw=img.width;
  var ih=img.height;
  cw=canvas.width=iw;
  ch=canvas.height=ih;

  // calculate the scaling needed to max the display of the image
  // inside the oval
  if(iw>ih){
    var scaleX=iw/ih
    var scaleY=1;
    var r=ih/2;
  }else{
    var scaleX=1;
    var scaleY=ih/iw;
    var r=iw/2;
  }

  // scale so the circle (arc) becomes an oval
  ctx.scale(scaleX,scaleY);
  ctx.arc(cw/scaleX/2,ch/scaleY/2,r,0,Math.PI*2);
  ctx.fill();

  // undo the scaling
  ctx.scale(1/scaleX,1/scaleY);

  // draw the image centered inside the oval using compositing
  ctx.globalCompositeOperation='source-atop';
  ctx.drawImage(img,cw/2-img.width/2,ch/2-img.height/2);
  ctx.globalCompositeOperation='source-atop';
}
body{ background-color: black; }
#canvas{border:1px solid red; margin:0 auto; }
<canvas id="canvas" width=300 height=300></canvas>

【讨论】:

    【解决方案2】:

    您不能直接执行此操作。你可以:

    • 将 SVG 绘制到画布中并使用它来合成剪辑。缺点是某些浏览器由于安全原因(外部链接、画布污染等)支持有限。
    • 手动解析 SVG

    这是一个示例,说明如何为 SVG 构建一个简单的解析器。它不能用于一般用途,但假设您知道有问题的 SVG。您可以在此基础上构建以支持不同的单位、转换列表等。

    提取的路径可以存储在 Path2D 对象上,而不是直接如下所示(Path2D 在某些浏览器中可能需要poly-fill),或者存储自定义对象/数组等。这完全取决于您。

    这个例子更像是一个概念验证。

    SVG 解析器到画布 Path2D 的示例

    var ctx = c.getContext("2d"),
        mask = document.getElementById("mask");  // get a SVG element
    
    // some random graphics
    for(var i=30,r=Math.random;i--;) {
      ctx.fillStyle = "hsl(" + (360*r()) + ",50%,50%)";ctx.fillRect(280*r(),120*r(),50,50)}
    
    // parse SVG element
    if (mask.localName) {
      switch(mask.localName) {
        case "rect":
          ctx.rect(v("x"), v("y"), v("width"), v("height"));
          break;
        case "ellipse":
          // need polyfill in some browsers
          ctx.ellipse(v("cx"), v("cy"), v("rx"), v("ry"), 0, 6.28);
          break;
        // more cases here
      }
    
      // use path from SVG to clip
      ctx.globalCompositeOperation = "destination-in";
      ctx.fill();
    }
    
    // helper - obtains a numeric value for SVG element property
    function v(name) {return mask[name].baseVal.value}
    #c, svg {border:1px solid #777}
    <h4>SVG (showing mask)</h4>
    <svg xmlns="http://www.w3.org/2000/svg" 
         width="300" height="150">
      <rect id="mask" x="50" y="30" width="200" height="100" />
    </svg>
    
    <h4>Canvas (mask applied from SVG)</h4>
    <canvas id=c></canvas>

    【讨论】:

      【解决方案3】:

      三个步骤:

      1. 将 SVG 绘制到画布上。
      2. 设置context.globalCompositeOperation = 'source-in';
      3. 绘制图像。

      图像只会在 SVG 已经用颜色填充像素的地方绘制,有效地将图像剪裁到已经绘制的任何位置。

      您可以将globalCompositeOperation 设置回'source-over'(默认值),或者在之前使用context.save() 并在之后使用context.restore() 将画布恢复为“正常”绘图模式。

      【讨论】:

      • 这是一次剪辑。
      猜你喜欢
      • 2011-08-23
      • 1970-01-01
      • 2018-03-06
      • 2019-01-11
      • 2020-10-30
      • 2019-02-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多