Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Drag div by leading point (like a boat in water)

I'm try to get a div to drag much like a boat through water, but I'm having some trouble getting the rotation right.

Here is what I have so far:
jsFiddle

JS

var start, stop;
$('#canoe').draggable({
    containment: '#board',
    cursor: 'none',
    cursorAt: {
        top: 5
    },
    drag: function (event, ui) {
        start = ui.position.left;
        setTimeout(function () {
            stop = ui.position.left;
        }, 150);
        $('#canoe').css({
            'transform': 'rotate(' + (start - stop) + 'deg)'
        });
    }
});

CSS

#board {
    height:100%;
    width:100%;
    background:blue;
}
#canoe {
    background: #fff;
    border-radius:100% 100% 100% 100%;
    height:60px;
    width:10px;
    position:absolute;
    left:50%;
    bottom:0;
    transform-origin:top center;
    transition: transform .2s;
}

HTML

<div id="board">
    <div id="canoe">A</div>
</div>

Is there a better way to get the rotation set up so that the front of the boat is always leading, even with a 360deg rotation?

Additional Context: I'm working on a Basic Game

Bounty update: I need the "boat" to be able to be dragged in a circle in one continuous motion without flipping/switching the rotation direction.

like image 210
apaul Avatar asked Jun 21 '13 16:06

apaul


2 Answers

You need to:

  • Store position each time it changes
  • At change, calculate the angle of the line between said positions
  • Save last position

http://jsfiddle.net/AstDerek/799Tp/

Movement doesn't look soft but is closer to what you want.

If you want to simulate water drag, you'd need to reduce the angle change by some factor, then use some time interval or similar to continue movement after dragging has ended, until the angle of the ship matches the angle it should have, or a new drag event starts.

like image 110
Ast Derek Avatar answered Oct 08 '22 12:10

Ast Derek


It's a bit more complicated, but here's how I'd do it :

var save = false, timer;

$('#canoe').draggable({
    containment: '#board',
    cursor: 'none',
    cursorAt: {
        top: 5
    },
    drag: function (event, ui) {
        if ( !save ) save = ui.offset;
        var canoe    = $('#canoe'),
            center_x = save.left + 5,
            center_y = save.top + 30,
            radians  = Math.atan2(event.pageX - center_x, event.pageY - center_y),
            degree   = (radians * (180 / Math.PI) * -1) + 180,
            time     = Math.abs(ui.offset.top-save.top) + Math.abs(ui.offset.left-save.left);

        canoe.css({
            '-moz-transform'    : 'rotate('+degree+'deg)',
            '-webkit-transform' : 'rotate('+degree+'deg)',
            '-o-transform'      : 'rotate('+degree+'deg)',
            '-ms-transform'     : 'rotate('+degree+'deg)'
        });

        timer = setTimeout(function() {
            clearTimeout(timer);
            save = ui.offset;
        }, Math.abs( time-300 ) + 400 );
    }
});

FIDDLE

It compares the current mouse position to where the center of the canoe was some given time ago.
The time is set based on how fast the mouse moves, as slower movements will need a longer timeout etc.

It's also a good idea to clear the timeouts so they don't build up, even if it wasn't really issue when I tested this, and the use of Math.abs ensures it's always a positive integer.

I added a few more browser prefixes to the CSS.

like image 1
adeneo Avatar answered Oct 08 '22 13:10

adeneo