我希望允许用户以他们想要的任何方向将现有的画布分割成两个画布。
我知道如何允许用户画一条线,我也知道如何将一个画布的图像数据复制到两个新的画布上,但是我如何只复制用户绘制的两面的相关颜色数据线到其各自的画布上?
例如,在以下演示中,我希望“剪切”画布,其中白线是:
const canvas = document.querySelector("canvas"),
ctx = canvas.getContext("2d");
const red = "rgb(104, 0, 0)",
lb = "rgb(126, 139, 185)",
db = "rgb(20, 64, 87)";
var width,
height,
centerX,
centerY,
smallerDimen;
var canvasData,
inCoords;
function sizeCanvas() {
width = canvas.width = window.innerWidth;
height = canvas.height = window.innerHeight;
centerX = width / 2;
centerY = height / 2;
smallerDimen = Math.min(width, height);
}
function drawNormalState() {
// Color the bg
ctx.fillStyle = db;
ctx.fillRect(0, 0, width, height);
// Color the circle
ctx.arc(centerX, centerY, smallerDimen / 4, 0, Math.PI * 2, true);
ctx.fillStyle = red;
ctx.fill();
ctx.lineWidth = 3;
ctx.strokeStyle = lb;
ctx.stroke();
// Color the triangle
ctx.beginPath();
ctx.moveTo(centerX + smallerDimen / 17, centerY - smallerDimen / 10);
ctx.lineTo(centerX + smallerDimen / 17, centerY + smallerDimen / 10);
ctx.lineTo(centerX - smallerDimen / 9, centerY);
ctx.fillStyle = lb;
ctx.fill();
ctx.closePath();
screenshot();
ctx.beginPath();
ctx.strokeStyle = "rgb(255, 255, 255)";
ctx.moveTo(width - 20, 0);
ctx.lineTo(20, height);
ctx.stroke();
ctx.closePath();
}
function screenshot() {
canvasData = ctx.getImageData(0, 0, width, height).data;
}
function init() {
sizeCanvas();
drawNormalState();
}
init();
body {
margin: 0;
}
<canvas></canvas>
答案 0 :(得分:0)
我发现最好的方法是:1)计算画布边界(或外部)边缘的线的“终点”,2)使用的终点创建两个*多边形步骤1中生成的直线和画布的四个角,以及3)基于我们创建的多边形将原始画布的图像数据划分为两个新的画布。
*我们实际上创建了一个,但是“第二个”是原始画布的剩余部分。
您可以使用一种非常便宜的算法来计算某些端点,这些端点需要指定起始坐标,x和y差(即斜率)以及画布的边界。我使用了以下内容:
function getEndPoints(startX, startY, xDiff, yDiff, maxX, maxY) {
let currX = startX,
currY = startY;
while(currX > 0 && currY > 0 && currX < maxX && currY < maxY) {
currX += xDiff;
currY += yDiff;
}
let points = {
firstPoint: [currX, currY]
};
currX = startX;
currY = startY;
while(currX > 0 && currY > 0 && currX < maxX && currY < maxY) {
currX -= xDiff;
currY -= yDiff;
}
points.secondPoint = [currX, currY];
return points;
}
其中
let xDiff = firstPoint.x - secondPoint.x,
yDiff = firstPoint.y - secondPoint.y;
要创建多边形,我要使用Paul Bourke's Javascript line intersection:
function intersect(point1, point2, point3, point4) {
let x1 = point1[0],
y1 = point1[1],
x2 = point2[0],
y2 = point2[1],
x3 = point3[0],
y3 = point3[1],
x4 = point4[0],
y4 = point4[1];
// Check if none of the lines are of length 0
if((x1 === x2 && y1 === y2) || (x3 === x4 && y3 === y4)) {
return false;
}
let denominator = ((y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1));
// Lines are parallel
if(denominator === 0) {
return false;;
}
let ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denominator;
let ub = ((x2 - x1) * (y1 - y3) - (y2 - y1) * (x1 - x3)) / denominator;
// is the intersection along the segments
if(ua < 0 || ua > 1 || ub < 0 || ub > 1) {
return false;
}
// Return a object with the x and y coordinates of the intersection
let x = x1 + ua * (x2 - x1);
let y = y1 + ua * (y2 - y1);
return [x, y];
}
遵循我自己的一些逻辑:
let origin = [0, 0],
xBound = [width, 0],
xyBound = [width, height],
yBound = [0, height];
let polygon = [origin];
// Work clockwise from 0,0, adding points to our polygon as appropriate
// Check intersect with top bound
let topIntersect = intersect(origin, xBound, points.firstPoint, points.secondPoint);
if(topIntersect) {
polygon.push(topIntersect);
}
if(!topIntersect) {
polygon.push(xBound);
}
// Check intersect with right
let rightIntersect = intersect(xBound, xyBound, points.firstPoint, points.secondPoint);
if(rightIntersect) {
polygon.push(rightIntersect);
}
if((!topIntersect && !rightIntersect)
|| (topIntersect && rightIntersect)) {
polygon.push(xyBound);
}
// Check intersect with bottom
let bottomIntersect = intersect(xyBound, yBound, points.firstPoint, points.secondPoint);
if(bottomIntersect) {
polygon.push(bottomIntersect);
}
if((topIntersect && bottomIntersect)
|| (topIntersect && rightIntersect)) {
polygon.push(yBound);
}
// Check intersect with left
let leftIntersect = intersect(yBound, origin, points.firstPoint, points.secondPoint);
if(leftIntersect) {
polygon.push(leftIntersect);
}
现在我们有了多边形,剩下的就是将这些数据放入新的画布中。最简单的方法是使用画布的ctx.drawImage
和ctx.globalCompositeOperation
。
// Use or create 2 new canvases with the split original canvas
let newCanvas1 = document.querySelector("#newCanvas1");
if(newCanvas1 == null) {
newCanvas1 = document.createElement("canvas");
newCanvas1.id = "newCanvas1";
newCanvas1.width = width;
newCanvas1.height = height;
document.body.appendChild(newCanvas1);
}
let newCtx1 = newCanvas1.getContext("2d");
newCtx1.globalCompositeOperation = 'source-over';
newCtx1.drawImage(canvas, 0, 0);
newCtx1.globalCompositeOperation = 'destination-in';
newCtx1.beginPath();
newCtx1.moveTo(polygon[0][0], polygon[0][1]);
for(let item = 1; item < polygon.length; item++) {
newCtx1.lineTo(polygon[item][0], polygon[item][1]);
}
newCtx1.closePath();
newCtx1.fill();
let newCanvas2 = document.querySelector("#newCanvas2");
if(newCanvas2 == null) {
newCanvas2 = document.createElement("canvas");
newCanvas2.id = "newCanvas2";
newCanvas2.width = width;
newCanvas2.height = height;
document.body.appendChild(newCanvas2);
}
let newCtx2 = newCanvas2.getContext("2d");
newCtx2.globalCompositeOperation = 'source-over';
newCtx2.drawImage(canvas, 0, 0);
newCtx2.globalCompositeOperation = 'destination-out';
newCtx2.beginPath();
newCtx2.moveTo(polygon[0][0], polygon[0][1]);
for(let item = 1; item < polygon.length; item++) {
newCtx2.lineTo(polygon[item][0], polygon[item][1]);
}
newCtx2.closePath();
newCtx2.fill();
所有这些加在一起就给了我们this demo!