如何拆分 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>
假设我有一个 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>