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

时间:2018-07-02 04:33:45

标签: javascript canvas line getimagedata

我希望允许用户以他们想要的任何方向将现有的画布分割成两个画布。

我知道如何允许用户画一条线,我也知道如何将一个画布的图像数据复制到两个新的画布上,但是我如何只复制用户绘制的两面的相关颜色数据线到其各自的画布上?

例如,在以下演示中,我希望“剪切”画布,其中白线是:

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>

1 个答案:

答案 0 :(得分:0)

TL; DR the demo


我发现最好的方法是:1)计算画布边界(或外部)边缘的线的“终点”,2)使用的终点创建两个*多边形步骤1中生成的直线和画布的四个角,以及3)基于我们创建的多边形将原始画布的图像数据划分为两个新的画布。

*我们实际上创建了一个,但是“第二个”是原始画布的剩余部分。


1)计算终点

您可以使用一种非常便宜的算法来计算某些端点,这些端点需要指定起始坐标,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;

2)创建两个多边形

要创建多边形,我要使用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);
}

3)划分原始画布的图像数据

现在我们有了多边形,剩下的就是将这些数据放入新的画布中。最简单的方法是使用画布的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();

所有这些加在一起就给了我们this demo