Skip to content

Commit

Permalink
Refactor startHammer (#455)
Browse files Browse the repository at this point in the history
* Refactor startHammer

* Remove duplicate onZoom call

* More refactoring

* Reduce lines

* Reduce more lines
  • Loading branch information
kurkle authored Apr 17, 2021
1 parent b49c8e9 commit b7bd3e2
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 95 deletions.
173 changes: 78 additions & 95 deletions src/hammer.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import {callback as call} from 'chart.js/helpers';
import Hammer from 'hammerjs';
import {doPan, doZoom} from './core';
import {getEnabledScalesByPoint} from './utils';
Expand All @@ -10,137 +11,118 @@ function createEnabler(chart, panOptions) {
if (!event || !event.srcEvent) { // Sometimes Hammer queries this with a null event.
return true;
}
const requireModifier = panOptions.modifierKey
&& (event.pointerType === 'mouse');
const requireModifier = panOptions.modifierKey && (event.pointerType === 'mouse');
if (requireModifier && !event.srcEvent[panOptions.modifierKey + 'Key']) {
if (typeof panOptions.onPanRejected === 'function') {
panOptions.onPanRejected({
chart: chart,
event: event
});
}
call(panOptions.onPanRejected, [{chart, event}]);
return false;
}
return true;
};
}

export function startHammer(chart, options) {
const node = chart.canvas;
const {pan: panOptions, zoom: zoomOptions} = options;
function pinchAxes(p0, p1) {
// fingers position difference
const x = Math.abs(p0.clientX - p1.clientX);
const y = Math.abs(p0.clientY - p1.clientY);

const mc = new Hammer.Manager(node);
if (zoomOptions && zoomOptions.enabled) {
mc.add(new Hammer.Pinch());
}
if (panOptions && panOptions.enabled) {
mc.add(new Hammer.Pan({
threshold: panOptions.threshold,
enable: createEnabler(chart, panOptions)
}));
}
// diagonal fingers will change both (xy) axes
const p = x / y;
return p > 0.3 && p < 1.7 ? 'xy' : x > y ? 'x' : 'y';
}

function createPinchHandlers(chart, zoomOptions) {
// Hammer reports the total scaling. We need the incremental amount
let currentPinchScaling;
const handlePinch = function(e) {
const diff = 1 / (currentPinchScaling) * e.scale;
const {center, pointers} = e;
const zoom = 1 / (currentPinchScaling) * e.scale;
const rect = e.target.getBoundingClientRect();
const offsetX = e.center.x - rect.left;
const offsetY = e.center.y - rect.top;
const center = {
x: offsetX,
y: offsetY
const focalPoint = {
x: center.x - rect.left,
y: center.y - rect.top
};

// fingers position difference
const x = Math.abs(e.pointers[0].clientX - e.pointers[1].clientX);
const y = Math.abs(e.pointers[0].clientY - e.pointers[1].clientY);

// diagonal fingers will change both (xy) axes
const p = x / y;
let xy;
if (p > 0.3 && p < 1.7) {
xy = 'xy';
} else if (x > y) {
xy = 'x'; // x axis
} else {
xy = 'y'; // y axis
}
const xy = pinchAxes(pointers[0], pointers[1]);

doZoom(chart, diff, diff, center, zoomOptions, xy);

if (typeof zoomOptions.onZoom === 'function') {
zoomOptions.onZoom({chart: chart});
}
doZoom(chart, zoom, zoom, focalPoint, zoomOptions, xy);

// Keep track of overall scale
currentPinchScaling = e.scale;
};

mc.on('pinchstart', function() {
currentPinchScaling = 1; // reset tracker
});
mc.on('pinch', handlePinch);
mc.on('pinchend', function(e) {
handlePinch(e);
currentPinchScaling = null; // reset
if (typeof zoomOptions.onZoomComplete === 'function') {
zoomOptions.onZoomComplete({chart: chart});
return {
start() {
currentPinchScaling = 1;
},
pinch: handlePinch,
end(e) {
handlePinch(e);
currentPinchScaling = null; // reset
call(zoomOptions.onZoomComplete, [chart]);
}
});
};
}

let currentDeltaX = null;
let currentDeltaY = null;
let panning = false;
let panningScales = null;
function createPanHandlers(chart, panOptions) {
let delta = null;
let enabledScales = null;
const handlePan = function(e) {
if (currentDeltaX !== null && currentDeltaY !== null) {
panning = true;
const deltaX = e.deltaX - currentDeltaX;
const deltaY = e.deltaY - currentDeltaY;
currentDeltaX = e.deltaX;
currentDeltaY = e.deltaY;
doPan(chart, deltaX, deltaY, panOptions, panningScales);
if (delta !== null) {
chart.panning = true;
doPan(chart, e.deltaX - delta.x, e.deltaY - delta.y, panOptions, enabledScales);
delta = {x: e.deltaX, y: e.deltaY};
}
};

mc.on('panstart', function(e) {
if (panOptions.enabled) {
return {
start(e) {
const rect = e.target.getBoundingClientRect();
const x = e.center.x - rect.left;
const y = e.center.y - rect.top;
panningScales = getEnabledScalesByPoint(panOptions, x, y, chart);
enabledScales = getEnabledScalesByPoint(panOptions, x, y, chart);

delta = {x: 0, y: 0};
handlePan(e);
},
move: handlePan,
end() {
delta = null;
setTimeout(() => (chart.panning = false), 500);
call(panOptions.onPanComplete, [chart]);
}
};
}

currentDeltaX = 0;
currentDeltaY = 0;
handlePan(e);
});
mc.on('panmove', handlePan);
mc.on('panend', function() {
currentDeltaX = null;
currentDeltaY = null;
setTimeout(function() {
panning = false;
}, 500);
if (typeof panOptions.onPanComplete === 'function') {
panOptions.onPanComplete({chart: chart});
}
});
const hammers = new WeakMap();
export function startHammer(chart, options) {
const canvas = chart.canvas;
const {pan: panOptions, zoom: zoomOptions} = options;

chart.$zoom._ghostClickHandler = function(e) {
if (panning && e.cancelable) {
e.stopImmediatePropagation();
e.preventDefault();
}
};
node.addEventListener('click', chart.$zoom._ghostClickHandler);
const mc = new Hammer.Manager(canvas);
if (zoomOptions && zoomOptions.enabled) {
const {start, pinch, end} = createPinchHandlers(chart, zoomOptions);

mc.add(new Hammer.Pinch());
mc.on('pinchstart', start);
mc.on('pinch', pinch);
mc.on('pinchend', end);
}

if (panOptions && panOptions.enabled) {
const {start, move, end} = createPanHandlers(chart, panOptions);
mc.add(new Hammer.Pan({
threshold: panOptions.threshold,
enable: createEnabler(chart, panOptions)
}));
mc.on('panstart', start);
mc.on('panmove', move);
mc.on('panend', end);
}

chart._mc = mc;
hammers.set(chart, mc);
}

export function stopHammer(chart) {
const mc = chart._mc;
const mc = hammers.get(chart);
if (mc) {
mc.remove('pinchstart');
mc.remove('pinch');
Expand All @@ -149,5 +131,6 @@ export function stopHammer(chart) {
mc.remove('pan');
mc.remove('panend');
mc.destroy();
hammers.delete(chart);
}
}
7 changes: 7 additions & 0 deletions src/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ export default {
chart.resetZoom = () => resetZoom(chart);
},

beforeEvent(chart, args) {
if (args.event.type === 'click' && chart.panning) {
// cancel the click event at pan end
return false;
}
},

beforeUpdate: function(chart, args, options) {
addListeners(chart, options);
},
Expand Down

0 comments on commit b7bd3e2

Please sign in to comment.