使用一条线将画布分为两个新画布 [英] Using a line to divide a canvas into two new canvases

查看:122
本文介绍了使用一条线将画布分为两个新画布的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

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 演示.


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

TL;DR the demo.


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.

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

您可以使用一种非常便宜的算法来计算给定起始坐标,x和y差(即斜率)以及画布边界的一些端点.我使用了以下内容:

You can use a very cheap algorithm to calculate some end points given a start coordinate, x and y difference (i.e. slope), and the bounds for the canvas. 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;
}

其中

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


2)创建两个多边形

要创建多边形,我要使用 Paul Bourke的


2) Create two polygons

To create the polygons, I make use of 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];
}

遵循我自己的一些逻辑:

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)划分原始画布的图像数据

现在我们有了多边形,剩下的就是将这些数据放到新的画布中.最简单的方法是使用canvas' ctx.drawImage ctx.globalCompositeOperation .


3) Divide up the original canvas' image data

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.

// 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!

这篇关于使用一条线将画布分为两个新画布的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆