8

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.

apaul
  • 16,092
  • 8
  • 47
  • 82

2 Answers2

2

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.

Ast Derek
  • 2,739
  • 1
  • 20
  • 28
  • This seems to swing around a little wild. Any way to reduce the sensitivity? – apaul Jun 21 '13 at 17:39
  • Multiply the angle for some value < 1 – Ast Derek Jun 21 '13 at 17:48
  • 1
    In [this example](http://jsfiddle.net/klickagent/Arm59/1/) I managed to reduce the swing around a litte bit – klickagent.ch Jun 21 '13 at 20:22
  • @AstDerek I've run into another issue with this solution. Try dragging the boat in a circle in one continuous motion. It will flip at a certain point – apaul Jun 21 '13 at 21:55
  • I've been thinking a lot. The script needs to remember the past angle as well and define the direction of the rotation from there, made a test to clarify the idea but I'm still a bit confused :P – Ast Derek Jun 22 '13 at 21:12
  • 1
    One suggestion : instead of storing the last position, store the last x positions (try it with 2, 3, 4...), and make an average of these + current – LeGEC Jun 27 '13 at 15:10
  • 1
    Hmmm... somewhat dampens the glitches, but not perfect http://jsfiddle.net/799Tp/2/ – LeGEC Jun 27 '13 at 15:30
1

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.

adeneo
  • 312,895
  • 29
  • 395
  • 388
  • This seems closer to what I'm after, but it still seems to have issues executing a circular motion. – apaul Jun 26 '13 at 16:21
  • @apaul34208 - Are you using Firefox ? – adeneo Jun 26 '13 at 17:41
  • Try Chrome or IE, seems Firefox has issues! Tried figuring out what the problem in FF is, but it just won't do what I tell it. – adeneo Jun 26 '13 at 17:55
  • I worked out the problem, I had `transition: transform .2s;` set on `#canoe` which was gumming up the works in FF. Unfortunately without it seems a bit twitchy. Do you know of any other way to smooth things out? – apaul Jun 26 '13 at 18:37
  • Increasing the timeouts a little would probably make it smoother but at the same time a little "slower", see if this helps any -> http://jsfiddle.net/unuCD/26/ – adeneo Jun 26 '13 at 19:14
  • @adeneo if you're now using JQuery versions prior to ver 1.7.2, it will add the css prefixes for you, so you may just use: transform' : 'rotate('+degree+'deg)' and other browser specific lines will be handled automatically. – Wessam El Mahdy Mar 27 '16 at 21:10