简体   繁体   中英

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:

  1. If not dragging, keep checking if the mouse collides with any rectangle, and if if collides to any, get the rectHandle.

  2. And if you mousedown when rectHandle exist, set drag to 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.

Some changes made to work as you expect, with the altered jsfiddle to demo:

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

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();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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