2
\$\begingroup\$

I'm looking for a mathematical formulae to calculate map scrolling inertia. Basically I have an HTML5 Canvas displaying part of a map and I capture the mousedown / mousemove / mouseup to calculate my new offset.

And when I scroll the map I want the map to continue scrolling for a few moments after the user stopped dragging the cursor.

I do have a sample implementation but I'm not really satisfied with it. I'd be looking for a nice clean math formulae to achieve this. I'd like my map to behave for example like in Civ-5 when you scroll the map around.

Here is part of my implementation: (working jsfiddle: https://jsfiddle.net/CowWarrior/dkxnj5d8/1/)

//mouseup
stopMapDrag: function (e) {
    var movementX = (e.pageX - this.startX);
    var movementY = (e.pageY - this.startY);
    this.mapOffsetX = parseInt(this.mapOffsetX) + movementX;
    this.mapOffsetY = parseInt(this.mapOffsetY) + movementY;
    this.isDragging = false;
    this.displayMap();      

    //calculate last vector
    ui.vectorX = parseInt(e.pageX - ui.absStartX);
    ui.vectorY = parseInt(e.pageY - ui.absStartY);

    //show inertia
    setTimeout(function(){ ui.dragInertia(2); }, 30);
    console.log("(vectorX:" + ui.vectorX + ", vectorY:" + ui.vectorY + ")");

    $('#canViewport').css('cursor', 'default');
},

//need to find a good formulae for inertia
dragInertia: function (factor) {
    var movementX = parseInt(ui.vectorX / factor);
    var movementY = parseInt(ui.vectorY / factor);
    ui.mapOffsetX = parseInt(ui.mapOffsetX + movementX);
    ui.mapOffsetY = parseInt(ui.mapOffsetY + movementY);
    ui.displayMap();

    if (factor < 8)
        setTimeout(function(){ ui.dragInertia(factor * 1.2); }, 30);
},
\$\endgroup\$
1
\$\begingroup\$

Math.acos() solution

I have something very close to what I am looking for but I beleive there is an even better way of doing this.

I started by plotting a graph using Desmos. I was actually looking for a less steep curve at the beginning but I think this is close enough to what I'm looking for. In this representation the Y axis is the velocity and the X axis is the time. Map inertia graph

The full fiddle of my solution is here: https://jsfiddle.net/CowWarrior/kkodtxm9/23/

I ended up changing almost nothing of the mouseup event:

stopMapDrag: function (e) {
    [...]

    //show inertia
    setTimeout(function(){ ui.dragInertia(0); }, 50);
    console.log("(vectorX:" + ui.vectorX + ", vectorY:" + ui.vectorY + ")");

    $('#canViewport').css('cursor', 'default');
},

However my dragInertia function now look like this:

dragInertia: function (iteration) {
    var multiplier = Math.acos(iteration) * 0.33;
    var movementX = parseInt(ui.vectorX * multiplier);
    var movementY = parseInt(ui.vectorY * multiplier);
    ui.mapOffsetX = parseInt(ui.mapOffsetX + movementX);
    ui.mapOffsetY = parseInt(ui.mapOffsetY + movementY);       
    ui.displayMap();

    if (iteration < 0.9)
        setTimeout(function(){ ui.dragInertia(iteration + 0.1); }, 50);
},

Please submit your answers if you have something even better to propose. I'm somehow not entirely satisfied with what I want.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.