代码之家  ›  专栏  ›  技术社区  ›  Zach Saucier

用一条线把画布分成两张新画布

  •  0
  • Zach Saucier  · 技术社区  · 7 年前

    我希望允许用户将一个现有的画布分成两个画布,可以按照他们希望的方向进行。

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

    例如,在下面的演示中,我希望画布在白线处被“剪切”:

    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 回复  |  直到 7 年前
        1
  •  0
  •   Zach Saucier    7 年前

    TL;博士 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)分割原始画布的图像数据

    现在我们有了多边形,剩下的就是把这些数据放到新的画布上。最简单的方法是使用canvas' 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 啊!

    推荐文章