d3-inertia
Version:
Inertia Dragging
166 lines (148 loc) • 5.84 kB
JavaScript
// https://github.com/Fil/d3-inertia v0.4.0 Copyright 2021 Philippe Riviere
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('d3-selection'), require('d3-drag'), require('d3-timer'), require('versor')) :
typeof define === 'function' && define.amd ? define(['exports', 'd3-selection', 'd3-drag', 'd3-timer', 'versor'], factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.d3 = global.d3 || {}, global.d3, global.d3, global.d3, global.versor));
}(this, (function (exports, d3Selection, d3Drag, d3Timer, versor) { 'use strict';
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
var versor__default = /*#__PURE__*/_interopDefaultLegacy(versor);
function geoInertiaDragHelper(opt) {
var projection = opt.projection,
v0, // Mouse position in Cartesian coordinates at start of drag gesture.
r0, // Projection rotation as Euler angles at start.
q0, // Projection rotation as versor at start.
v10, // Mouse position in Cartesian coordinates just before end of drag gesture.
v11, // Mouse position in Cartesian coordinates at end.
q10; // Projection rotation as versor at end.
var inertia = inertiaHelper({
start: function() {
v0 = versor__default['default'].cartesian(projection.invert(inertia.position));
r0 = projection.rotate();
q0 = versor__default['default'](r0);
opt.start && opt.start();
},
move: function() {
var inv = projection.rotate(r0).invert(inertia.position);
if (isNaN(inv[0])) return;
var v1 = versor__default['default'].cartesian(inv),
q1 = versor__default['default'].multiply(q0, versor__default['default'].delta(v0, v1)),
r1 = versor__default['default'].rotation(q1);
opt.render(r1);
opt.move && opt.move();
},
end: function() {
// velocity
v10 = versor__default['default'].cartesian(
projection.invert(
inertia.position.map(function(d, i) {
return d - inertia.velocity[i] / 1000;
})
)
);
q10 = versor__default['default'](projection.rotate());
v11 = versor__default['default'].cartesian(projection.invert(inertia.position));
opt.end && opt.end();
},
stop: opt.stop,
finish: opt.finish,
render: function(t) {
var rotation = versor__default['default'].rotation(
versor__default['default'].multiply(q10, versor__default['default'].delta(v10, v11, t * 1000))
);
opt.render && opt.render(rotation);
},
time: opt.time
});
return inertia;
}
function geoInertiaDrag(target, render, proj, opt) {
if (!opt) opt = {};
// target can be an element, a selector, a function, or a selection
// but in case of a selection we make sure to reselect it with d3-selection@2
if (target.node) target = target.node();
target = d3Selection.select(target);
// complete params: (projection, render, startDrag, dragging, endDrag)
var inertia = geoInertiaDragHelper({
projection: proj,
render: function(rotation) {
proj.rotate(rotation);
render && render();
},
start: opt.start,
move: opt.move,
end: opt.end,
stop: opt.stop,
finish: opt.finish,
time: opt.time,
hold: opt.hold
});
target.call(
d3Drag.drag()
.on("start", inertia.start)
.on("drag", inertia.move)
.on("end", inertia.end)
);
return inertia;
}
function inertiaHelper(opt) {
var A = opt.time || 5000; // reference time in ms
var limit = 1.0001;
var B = -Math.log(1 - 1 / limit);
var inertia = {
start: function(e) {
var position = [e.x, e.y];
inertia.position = position;
inertia.velocity = [0, 0];
inertia.timer.stop(), this.classList.remove('inertia');
this.classList.add('dragging');
opt.start && opt.start.call(this, position);
},
move: function(e) {
var position = [e.x, e.y];
var time = performance.now();
var deltaTime = time - inertia.time;
var decay = 1 - Math.exp(-deltaTime / 1000);
inertia.velocity = inertia.velocity.map(function(d, i) {
var deltaPos = position[i] - inertia.position[i],
deltaTime = time - inertia.time;
return 1000 * (1 - decay) * deltaPos / deltaTime + d * decay;
});
inertia.time = time;
inertia.position = position;
opt.move && opt.move.call(this, position);
},
end: function() {
this.classList.remove('dragging', 'inertia');
var v = inertia.velocity;
if (v[0] * v[0] + v[1] * v[1] < 100) return inertia.timer.stop(), opt.stop && opt.stop();
var time = performance.now();
var deltaTime = time - inertia.time;
if (opt.hold == undefined) opt.hold = 100; // default flick->drag threshold time (0 disables inertia)
if (deltaTime >= opt.hold) return inertia.timer.stop(), opt.stop && opt.stop();
this.classList.add('inertia');
opt.end && opt.end();
var me = this;
inertia.timer.restart(function(e) {
inertia.t = limit * (1 - Math.exp(-B * e / A));
opt.render && opt.render(inertia.t);
if (inertia.t > 1) {
inertia.timer.stop(), me.classList.remove('inertia');
inertia.velocity = [0, 0];
inertia.t = 1;
opt.finish && opt.finish();
}
});
},
position: [0, 0],
velocity: [0, 0], // in pixels/s
timer: d3Timer.timer(function(){}),
time: 0
};
inertia.timer.stop();
return inertia;
}
exports.geoInertiaDrag = geoInertiaDrag;
exports.geoInertiaDragHelper = geoInertiaDragHelper;
exports.inertiaHelper = inertiaHelper;
Object.defineProperty(exports, '__esModule', { value: true });
})));