简体   繁体   English

使用一条线将画布分为两个新画布

[英]Using a line to divide a canvas into two new canvases

I'm looking to allow users to slice an existing canvas into two canvases in whatever direction they would like. 我希望允许用户以他们想要的任何方向将现有的画布分割成两个画布。

I know how to allow the user to draw a line and I also know how to copy the image data of one canvas onto two new ones, but how can I copy only the relevant color data on either side of the user-drawn line to its respective canvas? 我知道如何允许用户画一条线,我也知道如何将一个画布的图像数据复制到两个新画布上,但是如何将用户画线的任何一侧的相关颜色数据仅复制到画布上各自的帆布?

For example, in the following demo I'd like the canvas to be "cut" where the white line is: 例如,在下面的演示中,我希望“剪切”画布,其中白线是:

 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> 

TL;DR the demo . TL; DR 演示


The best way I've found to do this is to 1) calculate "end points" for the line at the edge of (or outside) the canvas' bounds, 2) create two* polygons using the end points of the line generated in step 1 and the canvas' four corners, and 3) divide up the original canvas' image data into two new canvases based on the polygons we create. 我发现最好的方法是:1)计算画布边界(或外部)边缘的线的“端点”,2)使用在生成的线的端点创建两个*多边形步骤1和画布的四个角,以及3)根据我们创建的多边形将原始画布的图像数据划分为两个新的画布。

* We actually create one, but the "second" is the remaining part of the original canvas. *我们实际上创建了一个,但是“第二个”是原始画布的剩余部分。


1) Calculate the end points 1)计算终点

You can use a very cheap algorithm to calculate some end points given a start coordinate, x and y difference (ie slope), and the bounds for the canvas. 给定起始坐标,x和y的差(即斜率)以及画布的边界,您可以使用一种非常便宜的算法来计算一些端点。 I used the following: 我使用了以下内容:

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;
}

where 哪里

let xDiff = firstPoint.x - secondPoint.x,
    yDiff = firstPoint.y - secondPoint.y;

2) Create two polygons 2)创建两个多边形

To create the polygons, I make use of Paul Bourke's Javascript line intersection : 为了创建多边形,我使用了Paul Bourke的 Javascript线交叉点

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];
}

Along with some of my own logic: 加上我自己的一些逻辑:

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);
}

3) Divide up the original canvas' image data 3)划分原始画布的图像数据

Now that we have our polygon, all that's left is putting this data into new canvases. 现在我们有了多边形,剩下的就是将这些数据放到新的画布中。 The easiest way to do this is to use canvas' ctx.drawImage and ctx.globalCompositeOperation . 最简单的方法是使用canvas的ctx.drawImagectx.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();

All of that put together gives us this demo ! 所有这些都给了我们这个演示

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM