简体   繁体   English

在画布上绘制可调整大小的矩形

[英]Drawing resizable rectangles on Canvas

I am trying to draw resizable rectangles on a canvas element. 我试图在画布元素上绘制可调整大小的矩形。 I check the position of the mouse in relation to the rectangles on the screen then update width/ height of the triangle within which the mouse falls. 我检查鼠标相对于屏幕上矩形的位置,然后更新鼠标落入的三角形的宽度/高度。 So far I have been successful save for reducing element width/ height. 到目前为止,我已经成功地节省了元素宽度/高度。 This is because the mouse position is within range of rectangle co-ordinates. 这是因为鼠标位置在矩形坐标的范围内。

How would I handle expansion? 我将如何处理扩展? Here's the code 这是代码

var canvas = document.getElementById('canvas'),
ctx = canvas.getContext('2d'),
rect = [],
handlesSize = 4,
currentHandle = false,
drag = false,
selected = false;

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

function point(x, y) {
return {
    x: x,
    y: y
};
}

function collides(rects, x, y) {
var isCollision = false;
for (var i = 0, len = rects.length; i < len; i++) {
    var left = rects[i].x, right = rects[i].x+rects[i].w;
    var top = rects[i].y, bottom = rects[i].y+rects[i].h;
    if (right >= x
        && left <= x
        && bottom >= y
        && top <= y) {
        isCollision = i;
    }
}
return isCollision;
}

function dist(p1, p2) {
return Math.sqrt((p2.x - p1.x) * (p2.x - p1.x) + (p2.y - p1.y) * (p2.y - p1.y));
}

function getHandle(e,mouse) {
var returned = false;
selected = collides(rect, e.offsetX, e.offsetY);
if(selected || selected === 0)
{
    if (dist(mouse, point(rect[selected].x, rect[selected].y)) <= handlesSize) returned = 'topleft';
    if (dist(mouse, point(rect[selected].x + rect[selected].w, rect[selected].y)) <= handlesSize) returned = 'topright';
    if (dist(mouse, point(rect[selected].x, rect[selected].y + rect[selected].h)) <= handlesSize) returned = 'bottomleft';
    if (dist(mouse, point(rect[selected].x + rect[selected].w, rect[selected].y + rect[selected].h)) <= handlesSize) returned = 'bottomright';
    if (dist(mouse, point(rect[selected].x + rect[selected].w / 2, rect[selected].y)) <= handlesSize) returned = 'top';
    if (dist(mouse, point(rect[selected].x, rect[selected].y + rect[selected].h / 2)) <= handlesSize) returned = 'left';
    if (dist(mouse, point(rect[selected].x + rect[selected].w / 2, rect[selected].y + rect.h)) <= handlesSize) returned = 'bottom';
    if (dist(mouse, point(rect[selected].x + rect[selected].w, rect[selected].y + rect[selected].h / 2)) <= handlesSize) returned = 'right';
}


return returned;
}

function mouseDown(e) {

if (currentHandle) 
{  
    draw();
    drag = true;
}
else
{
    var mousePos = point(e.pageX - this.offsetLeft, e.pageY - this.offsetTop);
    rect.push({x: mousePos.x, y: mousePos.y, w: 80, h: 20});
    draw();
    drag = true;
    selected = collides(rect, e.offsetX, e.offsetY);
}
}

function mouseUp() {
drag = false;
currentHandle = false;
draw();
}  

function mouseMove(e) {
var previousHandle = currentHandle;
if (!drag) currentHandle = getHandle(e,point(e.pageX - this.offsetLeft, e.pageY - this.offsetTop));
selected = collides(rect, e.offsetX, e.offsetY);
var select = rect[selected];
if (currentHandle && drag && selected) {
    var mousePos = point(e.pageX - this.offsetLeft, e.pageY - this.offsetTop);
    var select = rect[selected];
    switch (currentHandle) {
        case 'topleft':
            rect[selected].w += select.x - mousePos.x;
            rect[selected].h += select.y - mousePos.y;
            rect[selected].x = mousePos.x;
            rect[selected].y = mousePos.y;
            break;
        case 'topright':
            rect[selected].w = mousePos.x - rect[selected].x;
            rect[selected].h += rect[selected].y - mousePos.y;
            rect[selected].y = mousePos.y;
            break;
        case 'bottomleft':
            rect[selected].w += rect[selected].x - mousePos.x;
            rect[selected].x = mousePos.x;
            rect[selected].h = mousePos.y - rect[selected].y;
            break;
        case 'bottomright':
            rect[selected].w = mousePos.x - rect[selected].x;
            rect[selected].h = mousePos.y - rect[selected].y;
            break;

        case 'top':
            rect[selected].h += rect[selected].y - mousePos.y;
            rect[selected].y = mousePos.y;
            break;

        case 'left':
            rect[selected].w += rect[selected].x - mousePos.x;
            rect[selected].x = mousePos.x;
            break;

        case 'bottom':
            rect[selected].h = mousePos.y - rect[selected].y;
            break;

        case 'right':
            rect[selected].w = mousePos.x - rect[selected].x;
            break;
    }
}
if (drag || currentHandle != previousHandle) draw();
}

function draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.fillStyle = 'black';
$.each(rect,function(i,item)
{
    ctx.fillRect(item.x, item.y, item.w, item.h);
});

    if (currentHandle) {
    var posHandle = [];
    posHandle = point(0, 0);
    switch (currentHandle) {
        case 'topleft':
            posHandle.x = rect[selected].x;
            posHandle.y = rect[selected].y;
            break;
        case 'topright':
            posHandle.x = rect[selected].x + rect[selected].w;
            posHandle.y = rect[selected].y;
            break;
        case 'bottomleft':
            posHandle.x = rect[selected].x;
            posHandle.y = rect[selected].y + rect[selected].h;
            break;
        case 'bottomright':
            posHandle.x = rect[selected].x + rect[selected].w;
            posHandle.y = rect[selected].y + rect[selected].h;
            break;
        case 'top':
            posHandle.x = rect[selected].x + rect[selected].w / 2;
            posHandle.y = rect[selected].y;
            break;
        case 'left':
            posHandle.x = rect[selected].x;
            posHandle.y = rect[selected].y + rect[selected].h / 2;
            break;
        case 'bottom':
            posHandle.x = rect[selected].x + rect[selected].w / 2;
            posHandle.y = rect[selected].y + rect[selected].h;
            break;
        case 'right':
            posHandle.x = rect[selected].x + rect[selected].w;
            posHandle.y = rect[selected].y + rect[selected].h / 2;
            break;
    }
    ctx.globalCompositeOperation = 'xor';
    ctx.beginPath();
    ctx.arc(posHandle.x, posHandle.y, handlesSize, 0, 2 * Math.PI);
    ctx.fill();
    ctx.globalCompositeOperation = 'source-over';
}
}

init();

You can see a live fiddle here js fiddle 你可以在这里看到一个现场小提琴

The logic in mouseMove is a little ambiguous, so I try to reform it to a more clear statements: mouseMove的逻辑有点模棱两可,因此我尝试将其mouseMove为更清晰的语句:

  1. If not dragging, keep checking if the mouse collides with any rectangle, and if if collides to any, get the rectHandle. 如果不拖动,请继续检查鼠标是否与任何矩形发生碰撞,如果与任何矩形发生碰撞,请获取rectHandle。

  2. And if you mousedown when rectHandle exist, set drag to true . 并且如果在rectHandle存在时drag鼠标向下drag ,请将drag设置为true

  3. So we know if drag is true, you don't have to check collision again, just use selected which is the selected rect's index you get from collides to start compute the new rectangle. 因此,我们知道如果drag为true,则不必再次检查碰撞,只需使用selected ,它是从collides获取的选定矩形的索引,从而开始计算新矩形。

Some changes made to work as you expect, with the altered jsfiddle to demo: 经过更改后的jsfiddle进行了演示,您可以进行一些更改以按预期工作:

collides 碰撞

function collides(rects, x, y) {
    // Set search index to -1 rather than false if nothing collides.
    // This will make the return value stick to Number, so we don't need to
    // care check if it is 0 and not false in your origin code.
    var isCollision = -1;
    for (var i = 0, len = rects.length; i < len; i++) {
        var left = rects[i].x, right = rects[i].x+rects[i].w;
        var top = rects[i].y, bottom = rects[i].y+rects[i].h;
        if (right >= x
            && left <= x
            && bottom >= y
            && top <= y) {
            isCollision = i;
        }
    }
    return isCollision;
}

gethandle gethandle

function getHandle(e,mouse) {
    var returned = false;
    // Remove this, do it outside.
    // selected = collides(rect, e.offsetX, e.offsetY);
    if(selected || selected === 0)
    {
        // You can use the else if logic here, so when a handle is found,
        // it don't keep check if other handles are valid.
        // But note that when rect is small, if else way would return topleft 
        // while current code would return right.
        if (dist(mouse, point(rect[selected].x, rect[selected].y)) <= handlesSize) returned = 'topleft';
        if (dist(mouse, point(rect[selected].x + rect[selected].w, rect[selected].y)) <= handlesSize) returned = 'topright';
        if (dist(mouse, point(rect[selected].x, rect[selected].y + rect[selected].h)) <= handlesSize) returned = 'bottomleft';
        if (dist(mouse, point(rect[selected].x + rect[selected].w, rect[selected].y + rect[selected].h)) <= handlesSize) returned = 'bottomright';
        if (dist(mouse, point(rect[selected].x + rect[selected].w / 2, rect[selected].y)) <= handlesSize) returned = 'top';
        if (dist(mouse, point(rect[selected].x, rect[selected].y + rect[selected].h / 2)) <= handlesSize) returned = 'left';
        if (dist(mouse, point(rect[selected].x + rect[selected].w / 2, rect[selected].y + rect.h)) <= handlesSize) returned = 'bottom';
        if (dist(mouse, point(rect[selected].x + rect[selected].w, rect[selected].y + rect[selected].h / 2)) <= handlesSize) returned = 'right';
    }


    return returned;
}

mousemove 鼠标移动

function mouseMove(e) {
    var previousHandle = currentHandle;
    // If not dragging, check collision
    if (!drag) {
        selected = collides(rect, e.offsetX, e.offsetY);
        // If collides with something, get handle
        // You can move this part into collides
        if (selected >= 0) {
            currentHandle = getHandle(e,point(e.pageX - this.offsetLeft, e.pageY - this.offsetTop));
        }
    } else {
        // If drag is true, selected and currentHandle is guranteed to have value in your logic.
        var mousePos = point(e.pageX - this.offsetLeft, e.pageY - this.offsetTop);
        var select = rect[selected];
        switch (currentHandle) {
            case 'topleft':
                rect[selected].w += select.x - mousePos.x;
                rect[selected].h += select.y - mousePos.y;
                rect[selected].x = mousePos.x;
                rect[selected].y = mousePos.y;
                break;
            case 'topright':
                rect[selected].w = mousePos.x - rect[selected].x;
                rect[selected].h += rect[selected].y - mousePos.y;
                rect[selected].y = mousePos.y;
                break;
            case 'bottomleft':
                rect[selected].w += rect[selected].x - mousePos.x;
                rect[selected].x = mousePos.x;
                rect[selected].h = mousePos.y - rect[selected].y;
                break;
            case 'bottomright':
                rect[selected].w = mousePos.x - rect[selected].x;
                rect[selected].h = mousePos.y - rect[selected].y;
                break;

            case 'top':
                rect[selected].h += rect[selected].y - mousePos.y;
                rect[selected].y = mousePos.y;
                break;

            case 'left':
                rect[selected].w += rect[selected].x - mousePos.x;
                rect[selected].x = mousePos.x;
                break;

            case 'bottom':
                rect[selected].h = mousePos.y - rect[selected].y;
                break;

            case 'right':
                rect[selected].w = mousePos.x - rect[selected].x;
                break;
        }
    }

    // If dragging or currentHandle changed, draw it
    if (drag || currentHandle != previousHandle) draw();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM