【问题标题】:Draw multiple rectangle boxes on canvas without overlapping在画布上绘制多个矩形框而不重叠
【发布时间】:2015-10-25 18:54:43
【问题描述】:

我试图在画布图像上绘制多个(最多 6 个)矩形,而实际上不让它们重叠。这怎么能在javascript中完成? 我想不出可以用于此的逻辑..

编辑:

如何检测是否在任何一个绘制的矩形内发生了鼠标单击,以便我可以再次在画布周围移动矩形而不重叠?

【问题讨论】:

  • 只需指定以xys 开头的不同矩形,对吗?
  • 存储每个矩形的宽度、高度、x 位置和 y 位置,当您创建一个新矩形时,请检查创建的坐标与您已有的坐标

标签: javascript angularjs html canvas


【解决方案1】:

要确定一个新的矩形是否会与您现有的任何矩形重叠,您必须进行 3 次测试:

  1. 新矩形是否与任何现有矩形相交?
  2. 新的矩形是否完全包含任何现有的矩形?
  3. 任何现有的矩形是否完全包含新的矩形?

方法如下...

如果您有使用 javascript 对象定义的现有矩形,如下所示:

var rects=[];
rects.push({left:100,right:200,top:100,bottom:200});

然后你可以像这样测试一个新的矩形是否会覆盖任何现有的矩形:

var newRectangle={left:50,right:25,top:50,bottom:25};

function willOverlap(newRect){

    // shortcut to the new potential rect
    var r2=newRect;

    // test if one rect is completely inside another rect
    var isInside=function(rect1,rect2){
        return(rect2.left>=rect1.left && 
        rect2.right<=rect1.right && 
        rect2.top>=rect1.top &&
        rect2.bottom<=rect1.bottom);
    }

    // test if the new rect is overlapping any existing rect
    var isOverlapping=false;
    for(var i=0;i<rects.length;i++){
        var r1=rects[i];
        //
        var isIntersecting = !(r2.left>r1.right ||
        r2.right<r1.left ||
        r2.top>r1.bottom ||
        r2.bottom<r1.top);
        //
        var isContained= isInside(r1,r2) || isInside(r2,r1);
        //
        if(isIntersecting || isContained){
            isOverlapping=true;
        }
    }
    return(isOverlapping);
}

这是示例代码和演示:

var canvas=document.getElementById("canvas");
var ctx=canvas.getContext("2d");
var cw=canvas.width;
var ch=canvas.height;
function reOffset(){
  var BB=canvas.getBoundingClientRect();
  offsetX=BB.left;
  offsetY=BB.top;        
}
var offsetX,offsetY;
reOffset();
window.onscroll=function(e){ reOffset(); }
window.onresize=function(e){ reOffset(); }

var isDown=false;
var startX,startY;

var rects=[];
var newRect;

$("#canvas").mousedown(function(e){handleMouseDown(e);});
$("#canvas").mousemove(function(e){handleMouseMove(e);});
$("#canvas").mouseup(function(e){handleMouseUp(e);});
$("#canvas").mouseout(function(e){handleMouseOut(e);});


function handleMouseDown(e){
  // tell the browser we're handling this event
  e.preventDefault();
  e.stopPropagation();

  startX=parseInt(e.clientX-offsetX);
  startY=parseInt(e.clientY-offsetY);

  // Put your mousedown stuff here
  isDown=true;
}

function handleMouseUp(e){
  // tell the browser we're handling this event
  e.preventDefault();
  e.stopPropagation();

  mouseX=parseInt(e.clientX-offsetX);
  mouseY=parseInt(e.clientY-offsetY);

  // Put your mouseup stuff here
  isDown=false;

  if(!willOverlap(newRect)){
    rects.push(newRect);
  }
  drawAll();
}

function drawAll(){
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  ctx.lineWidth=1;
  ctx.strokeStyle='green';
  for(var i=0;i<rects.length;i++){
    var r=rects[i];
    ctx.strokeRect(r.left,r.top,r.right-r.left,r.bottom-r.top);
  }
}

function handleMouseOut(e){
  // tell the browser we're handling this event
  e.preventDefault();
  e.stopPropagation();

  mouseX=parseInt(e.clientX-offsetX);
  mouseY=parseInt(e.clientY-offsetY);

  // Put your mouseOut stuff here
  isDown=false;
}

function handleMouseMove(e){
  if(!isDown){return;}
  // tell the browser we're handling this event
  e.preventDefault();
  e.stopPropagation();

  mouseX=parseInt(e.clientX-offsetX);
  mouseY=parseInt(e.clientY-offsetY);

  newRect={
    left:Math.min(startX,mouseX),
    right:Math.max(startX,mouseX),
    top:Math.min(startY,mouseY),
    bottom:Math.max(startY,mouseY),
  }

  drawAll();
  ctx.strokeStyle = "lightgray";
  ctx.lineWidth = 3;
  ctx.strokeRect(startX,startY,mouseX-startX,mouseY-startY);

}


function willOverlap(newRect){

  // shortcut to the new potential rect
  var r2=newRect;

  // test if one rect is completely inside another rect
  var isInside=function(rect1,rect2){
    return(rect2.left>=rect1.left && 
           rect2.right<=rect1.right && 
           rect2.top>=rect1.top &&
           rect2.bottom<=rect1.bottom);
  }

  // test if the new rect is overlapping any existing rect
  var isOverlapping=false;
  for(var i=0;i<rects.length;i++){
    var r1=rects[i];
    //
    var isIntersecting = !(r2.left>r1.right ||
                           r2.right<r1.left ||
                           r2.top>r1.bottom ||
                           r2.bottom<r1.top);
    //
    var isContained= isInside(r1,r2) || isInside(r2,r1);
    //
    if(isIntersecting || isContained){
      isOverlapping=true;
    }
  }
  return(isOverlapping);
}
body{ background-color: ivory; }
#canvas{border:1px solid red; margin:0 auto; }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script>
<h4>Drag to create a new rect.<br>New rect will be added if not overlapping.</h4>
<canvas id="canvas" width=300 height=300></canvas>

【讨论】:

  • 嗨@Austi01101110。感谢您的回复。这是我的小提琴:jsfiddle.net/5koLdpt2。我无法绘制多个矩形,也无法调整大小和移动框。我在绘制的框外单击的那一刻消失了。我是前端开发和画布的新手,所以不确定如何继续。你能帮我提琴吗?
  • @steffi.m,在看到您在小提琴中尝试做的事情后,我已经通过示例代码进行了更改。干杯!
  • 谢谢。这行得通。但我无法调整矩形的大小或移动它们。我怎样才能让它工作?
  • @steffi.m 移动和调整大小——这不是您问题的一部分吗?幸运的是,Stackoverflow 有很多关于“移动”和“调整”画布上的绘图的问答。快速回答:画布绘图是永久性的(不可移动或调整大小),因此您必须清除画布并将它们重新绘制到新位置和新尺寸。
  • 嗨,马克。我已经尝试实现矩形的移动和调整大小,但我并非无法做到。也尝试过查看一些博客,但无法将它们与我的代码集成。这是我调查过的其中之一:simonsarris.com/blog/225-canvas-selecting-resizing-shape
【解决方案2】:

查看此演示:JSFiddle

只需在 JavaScript 中指定矩形的不同起点,然后绘制即可:

function draw() {
    var canvas = document.getElementById("canvas");
    if (canvas.getContext) {
        var ctx = canvas.getContext("2d");

        ctx.fillStyle = "rgb(200,0,0)";
        ctx.fillRect(10, 10, 20, 20);

        ctx.fillStyle = "rgba(0, 0, 200, 0.5)";
        ctx.fillRect(30, 30, 20, 20);

        ......
    }
}

【讨论】:

    【解决方案3】:

    将每个正方形想象成一个盒子,然后插入逻辑。

    要检查您是否将框 1 视为点云,然后检查框 2 的四个点中的任何一个是否在该点云中。

    请注意,我是一名 java 开发人员,但原理是一样的。 Math.abs 是 x 的绝对值。

    if((Math.abs(b1X - b2X) < b1Width && Math.abs(b1Y - b2Y) < b1Height) ||
       (Math.abs(b1X - (b2X + b2Width)) < b1Width && Math.abs(b1Y - b2Y) < b1Height) ||
       (Math.abs(b1X - b2X) < b1Width && Math.abs(b1Y - (b2Y + b2Height)) < b1Height) ||
      (Math.abs(b1X - (b2X + b2Width)) < b1Width && Math.abs(b1Y - (b2Y + b2Height)) < b1Height))
                return false;
    

    第一行检查b2上的点1是否在点云中,第二行如果点2在点云上,第三行如果点3在云上,第4行如果点4在云上

    点定义为

    1-----2

    |。 . . . |

    3-----4

    当你创建你的方格时,你必须检查新创建的方格和其余方格。如果它对任何正方形返回 false,则它是重叠的。

    【讨论】:

    • 另外,这假设形状是正方形(或矩形)。要检查它们是否是不是完全直角的其他形状会变得相当困难。
    猜你喜欢
    • 2017-01-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-22
    相关资源
    最近更新 更多