调整 HTML5 画布中矩形的大小

13

我有一些函数可以在画布元素上绘制矩形。当元素被绘制后,我想通过拖动其角来调整其大小。

var canvas = document.getElementById('canvas'),
  ctx = canvas.getContext('2d'),
  rect = {},
  drag = false;

function init() {
  canvas.addEventListener('mousedown', mouseDown, false);
  canvas.addEventListener('mouseup', mouseUp, false);
  canvas.addEventListener('mousemove', mouseMove, false);
}

function mouseDown(e) {
  rect.startX = e.pageX - this.offsetLeft;
  rect.startY = e.pageY - this.offsetTop;
  drag = true;
}

function mouseUp() {
  drag = false;
}

function mouseMove(e) {
  if (drag) {
    rect.w = (e.pageX - this.offsetLeft) - rect.startX;
    rect.h = (e.pageY - this.offsetTop) - rect.startY;
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    draw();
  }
}

function draw() {
  ctx.fillRect(rect.startX, rect.startY, rect.w, rect.h);
}

init();
<canvas id="canvas" width="500" height="500"></canvas>


我编辑了我的答案,使其更加完整。 - Paul Kaplan
3个回答

26

确保使用某种阈值检查拖动角上的内容,使用closeEnough变量来保存这个阈值,然后进行角点和鼠标点之间的绝对值差的比较,看是否小于阈值。除此之外,还有许多情况需要处理。 这里是一个jsFiddle的示例

var canvas = document.getElementById('canvas'),
    ctx = canvas.getContext('2d'),
    rect = {},
    drag = false,
    mouseX, 
    mouseY,
    closeEnough = 10,
    dragTL=dragBL=dragTR=dragBR=false;

function init() {
  canvas.addEventListener('mousedown', mouseDown, false);
  canvas.addEventListener('mouseup', mouseUp, false);
  canvas.addEventListener('mousemove', mouseMove, false);
}

function mouseDown(e) {
  mouseX = e.pageX - this.offsetLeft;
  mouseY = e.pageY - this.offsetTop;

  // if there isn't a rect yet
  if(rect.w === undefined){
    rect.startX = mouseY;
    rect.startY = mouseX;
    dragBR = true;
  }

  // if there is, check which corner
  //   (if any) was clicked
  //
  // 4 cases:
  // 1. top left
  else if( checkCloseEnough(mouseX, rect.startX) && checkCloseEnough(mouseY, rect.startY) ){
    dragTL = true;
  }
  // 2. top right
  else if( checkCloseEnough(mouseX, rect.startX+rect.w) && checkCloseEnough(mouseY, rect.startY) ){
    dragTR = true;

  }
  // 3. bottom left
  else if( checkCloseEnough(mouseX, rect.startX) && checkCloseEnough(mouseY, rect.startY+rect.h) ){
    dragBL = true;

  }
  // 4. bottom right
  else if( checkCloseEnough(mouseX, rect.startX+rect.w) && checkCloseEnough(mouseY, rect.startY+rect.h) ){
    dragBR = true;

  }
  // (5.) none of them
  else {
    // handle not resizing
  }

  ctx.clearRect(0,0,canvas.width,canvas.height);
  draw();

}

function checkCloseEnough(p1, p2){
  return Math.abs(p1-p2)<closeEnough;
}
function mouseUp() {
  dragTL = dragTR = dragBL = dragBR = false;
}

function mouseMove(e) {
  mouseX = e.pageX - this.offsetLeft;
  mouseY = e.pageY - this.offsetTop;
  if(dragTL){
    rect.w += rect.startX-mouseX;
    rect.h += rect.startY-mouseY;
    rect.startX = mouseX;
    rect.startY = mouseY;
  } else if(dragTR) {
    rect.w = Math.abs(rect.startX-mouseX);
    rect.h += rect.startY-mouseY;
    rect.startY = mouseY;
  } else if(dragBL) {
    rect.w += rect.startX-mouseX;
    rect.h = Math.abs(rect.startY-mouseY);
    rect.startX = mouseX;  
  } else if(dragBR) {
    rect.w = Math.abs(rect.startX-mouseX);
    rect.h = Math.abs(rect.startY-mouseY);
  }
    ctx.clearRect(0,0,canvas.width,canvas.height);
    draw();
}

function draw() {
  ctx.fillRect(rect.startX, rect.startY, rect.w, rect.h);
}

init();

你能解释一下你的意思吗?对我来说它运行得很好。我更新了fiddle,使它更明显地展示了正在发生的事情。 - Paul Kaplan
做得很好,我喜欢手柄,这样用户就知道如何使用它。 - CarbonMan

15

进行一个手柄系统:当鼠标移动时,获取到每个角落的距离,找到最靠近光标的第一个角落,然后保存它并根据它调整矩形大小。

这里有一个 JSfiddle 示例:http://jsfiddle.net/BaliBalo/9HXMG/

function getHandle(mouse) {
    if (dist(mouse, point(rect.x, rect.y)) <= handlesSize) return 'topleft';
    if (dist(mouse, point(rect.x + rect.w, rect.y)) <= handlesSize) return 'topright';
    if (dist(mouse, point(rect.x, rect.y + rect.h)) <= handlesSize) return 'bottomleft';
    if (dist(mouse, point(rect.x + rect.w, rect.y + rect.h)) <= handlesSize) return 'bottomright';
    if (dist(mouse, point(rect.x + rect.w / 2, rect.y)) <= handlesSize) return 'top';
    if (dist(mouse, point(rect.x, rect.y + rect.h / 2)) <= handlesSize) return 'left';
    if (dist(mouse, point(rect.x + rect.w / 2, rect.y + rect.h)) <= handlesSize) return 'bottom';
    if (dist(mouse, point(rect.x + rect.w, rect.y + rect.h / 2)) <= handlesSize) return 'right';
    return false;
}

2
问题有点老了,但对于像我这样遇到这个问题的人,我整理了各种想法(感谢@Paul Kaplan)并打包了一个可以在响应式画布上运行的解决方案

代码可在链接的HTML页面源代码中查看,并在this文章(“响应式图像上的交互式选择框”)中进行了说明。

我相信它会有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接