如何拆分 canvas 对角线或非方形块

how to split a canvas diagonally or non-square pieces

假设我有一个 canvas,它是一个矩形。我想把那个 canvas 沿对角线拆分,然后能够操纵这些部分来做任何我想做的事情。

我的最终目标是让一个矩形沿对角线分割,并且这 2 个部分在屏幕外以相反的方向进行动画处理。我正在考虑完全在动画循环中的 canvas 内执行此操作,或者将每个片段转换为图像元素并使用 CSS3 动画来移动这些片段。我只是想找出最好的方法。

下面的代码、codepen link 和图片只是为了说明我希望 canvas 被拆分的位置。你会发现这不是等边的精确分割。

http://codepen.io/FranciscoG/pen/YPjzbQ

<div id="container">
  <img id="dog" src="http://i.imgur.com/1GUzYh9.jpg" width="375"
  height="667">
</div>

<script>
var container = document.getElementById('container');
var dogImg = document.getElementById('dog');

function convertImageToCanvas(image) {
    var canvas = document.createElement("canvas");
    canvas.width = image.width;
    canvas.height = image.height;
    canvas.getContext("2d").drawImage(image, 0, 0);
    return canvas;
}

function drawLine(canvas) {
  var context = canvas.getContext('2d');
  context.beginPath();
  context.moveTo(0,0);
  context.lineTo(345, 0);
  context.lineTo(0, 567);
  context.lineTo(0,0);
  context.stroke();
  context.closePath();
  return canvas;
};

var newDog = convertImageToCanvas(dogImg);
var divided = drawLine(newDog);
container.innerHTML = "";
container.appendChild(divided)
</script>

您可以使用context.clip来实现您的图像分割效果

context.clip 将图像限制为使用指定路径绘制。

您可以定义几个这样的裁剪区域,将您的图像分成几个部分(路径)。

然后在动画循环中,您可以清除 canvas 并使用变化的偏移重新绘制每个裁剪区域。左裁剪区域将在每个循环中向左移动(偏移)。右裁剪区域会在每次循环中向右移动(偏移)。

这是示例代码和演示:

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


var nextTime=0;
var duration=1000/60*3;

var offset=0;
var paths=[];

var img=new Image();
img.onload=start;
img.src="https://dl.dropboxusercontent.com/u/139992952/multple/Dog-With-Cute-Cat.jpg";
function start(){
  cw=canvas.width=img.width;
  ch=canvas.height=img.height;

  paths.push({path:[{x:0,y:0},{x:150,y:0},{x:0,y:ch}],direction:-1});
  paths.push({path:[{x:150,y:0},{x:0,y:ch},{x:cw,y:ch},{x:cw,y:0}],direction:1});

  requestAnimationFrame(animate);
}

function draw(){
  ctx.clearRect(0,0,cw,ch);
  for(var i=0;i<paths.length;i++){
    var path=paths[i].path;
    var offX=offset*paths[i].direction;
    ctx.save();
    ctx.beginPath();
    var pt=path[0];
    ctx.moveTo(pt.x+offX,pt.y);
    for(var j=1;j<path.length;j++){
      var pt=path[j];
      ctx.lineTo(pt.x+offX,pt.y);
    }
    ctx.closePath();
    ctx.stroke();
    ctx.clip();
    ctx.drawImage(img,offX,0);
    ctx.restore();
  }
}

function animate(time){
  if(offset<cw){requestAnimationFrame(animate);}else{log('done');}
  if(time<nextTime){return;}
  nextTime=time+duration;
  draw();
  offset++;
}
body{ background-color: ivory; padding:10px; }
#canvas{border:1px solid red;}
<canvas id="canvas" width=300 height=300></canvas>

您始终可以使用剪辑,但请注意,这将涉及 save/restore 调用,这是一项相对较慢的业务。有人建议将 resetClip() 包含在规范中,但似乎很难将消息传达给小组 为什么 需要它。

无论如何,我会推荐以下方法:

  • 创建一个对象或函数,可以重现图像的一半边之一(即包含框的对角线)的单个蒙版(路径)。
  • 对于左半部分:绘制图像,将合成模式设置为destination-in,绘制遮罩,将canvas提取为图像
  • 对于右半部分:绘制图像,将合成模式设置为destination-out,绘制遮罩,将canvas提取为图像

现在将图像(直接使用 canvas 元素)放入容器中,使它们相互堆叠。

使用过渡或动画制作动画class。

var img = new Image(375, 667);
img.onload = setup;
img.src = "http://i.imgur.com/1GUzYh9.jpg";

function setup() {

  var path = [0,0, 345,0, 0, 567];   // last point not needed..
  var left = createCanvas(this, path, "destination-in");
  var right = createCanvas(this, path, "destination-out");
  var cont = document.getElementById("cont");
  
  cont.appendChild(left);
  cont.appendChild(right);
  
  // animate here by setting animation/transition class or using JS:
  var x = 0;
  (function loop() {
    left.style.left = x + "px";   // translate is smoother, but need
    right.style.left = -x + "px"; // prefix in some browser. Update as needed..
    x-=5; if (x < -400) x = 0;
    requestAnimationFrame(loop);
  })();
  
  function createCanvas(img, path, mode) {
    var canvas = document.createElement("canvas"),
        ctx = canvas.getContext("2d");
    canvas.width = img.width;
    canvas.height = img.height;
    
    // draw image
    ctx.drawImage(img, 0, 0);
    
    // create mask
    ctx.moveTo(path[0], path[1]);
    for(var i = 2; i < path.length; i += 2) ctx.lineTo(path[i], path[i+1]);
    ctx.closePath();
    
    // composite mode and create half
    ctx.globalCompositeOperation = mode;
    ctx.fill();
    
    return canvas
  }
}
#cont {
  position:relative;width:375px;height:667px;overflow:hidden;
  }
#cont>canvas {position:absolute;left:0;right:0;}
<div id="cont"></div>