Search code examples
javascriptraphael

Dragging a rectangle in Raphael JS bounces back and forth


In this jsFiddle I have a Raphael JS grid with a rectangle that I need to move snapped to a grid. The rectangle has four red handles in each corner, and when it moves the handles move as well.

So far I can move the rectangle around (that works fine) but when I try to move it to the left the rectangle flickers and bounces back and forth. Could the problem be that I'm setting the x rectangle coordinate inside the drag move function? Any ideas will be greatly appreciated.

   var move = function(dx, dy) {
    rect.attr("x", ox + dx);
    rect.attr("y", oy + dy);
    leftTop.attr("x", ox1 + dx);
    leftTop.attr("y", oy1 + dy);
    rightTop.attr("x", ox2 + dx);
    rightTop.attr("y", oy2 + dy);
    leftBottom.attr("x", ox3 + dx);
    leftBottom.attr("y", oy3 + dy);
    rightBottom.attr("x", ox4 + dx);
    rightBottom.attr("y", oy4 + dy);

    if ((dx - lastdx) < 0)
        seeMoveLeft(rect, leftTop, rightTop,
                 leftBottom, rightBottom);

    lastdx = dx; 

};

var up = function() {};

rect.drag(move, start, up);


var seeMoveLeft = function (rect, leftTop, rightTop,
                 leftBottom, rightBottom){
    var left = rect.attr('x');
    // find next left grid
    var found = false;
    var min = left - 40;
    for (var i=left; i>=min; i--){
       if (i % 40 == 0) {
          found = true;
          break;
       }
    }

    if (found) {
        var diff = left - i;
        rect.attr('x', i);
        var lt = leftTop.attr('x');
        leftTop.attr('x', lt - diff);
        var rt = rightTop.attr('x');
        rightTop.attr('x', rt - diff);
        var lb = leftBottom.attr('x'); 
        leftBottom.attr('x', lb - diff);
        var rb = rightBottom.attr('x');
        rightBottom.attr('x', rb - diff);

    }

}

Solution

  • The problem is that you set the new x of rect here:

    rect.attr("x", ox + dx);
    

    Then you modify it here:

    if ((dx - lastdx) < 0)
            seeMoveLeft(rect, leftTop, rightTop,
                     leftBottom, rightBottom);
    

    But since move function is based on the mousemove event, sometimes, you'll get the same dx value 2 times in a row. This is normal with mousemove event, especially if you move slow. You can try it below, each time the clientX stays the same, the window will turn green.

    var lastClientX;
    window.addEventListener('mousemove', (e) => {
    
      if (lastClientX && e.clientX === lastClientX) {
        document.body.style.backgroundColor = 'green';
      } else {
        document.body.style.backgroundColor = 'yellow';
      }
    
      lastClientX = e.clientX;
    
    })
    body {
      width: 100%;
      height 100%;
    }

    What happens in your case when you have 2 consecutive identical dx is that x - lastdx won't validate and so the position won't be adjusted and will stay at rect.attr("x", ox + dx);. So first move event, the position is adjusted to the grid with seeMoveLeft, next, the position changes but isn't adjusted because dx is not smaller than lastdx, it's equal. Hence the flicker you see.

    Simplest way to correct, would be to skip the positionning if dx is the same as lastdx. Like this:

    if(lastdx !== dx){
        rect.attr("x", ox + dx);
        leftTop.attr("x", ox1 + dx);
        rightTop.attr("x", ox2 + dx);
        leftBottom.attr("x", ox3 + dx);
        rightBottom.attr("x", ox4 + dx);
      }
    

    https://jsfiddle.net/ce7sh9ov/1/