Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions docs/samples/drag.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Drag To Zoom

Zooming is performed by clicking and selecting an area over the chart with the mouse.
Zooming is performed by clicking and selecting an area over the chart with the mouse. Pan is activated by keeping `ctrl` pressed.

```js chart-editor
// <block:data:1>
Expand Down Expand Up @@ -56,7 +56,8 @@ Object.keys(scales).forEach(scale => Object.assign(scales[scale], scaleOpts));
const dragColor = Utils.randomColor(0.4);
const zoomOptions = {
pan: {
enabled: false,
enabled: true,
modifierKey: 'ctrl',
},
zoom: {
enabled: true,
Expand Down Expand Up @@ -86,6 +87,9 @@ const config = {
text: (ctx) => 'Zoom: ' + zoomStatus()
}
},
onClick(e) {
console.log(e.type);
}
}
};
// </block:config>
Expand All @@ -108,5 +112,6 @@ const actions = [
module.exports = {
actions,
config,
output: 'Clicks are logged here'
};
```
10 changes: 8 additions & 2 deletions src/handlers.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,19 @@ function addHandler(chart, target, type, handler) {
export function mouseMove(chart, event) {
const state = getState(chart);
if (state.dragStart) {
state.dragging = true;
state.dragEnd = event;
chart.update('none');
}
}

export function mouseDown(chart, event) {
const state = getState(chart);
const {pan: panOptions, zoom: zoomOptions} = state.options;
const panKey = panOptions && panOptions.modifierKey;
if (panKey && event[panKey + 'Key']) {
return call(zoomOptions.onZoomRejected, [{chart, event}]);
}
state.dragStart = event;

addHandler(chart, chart.canvas, 'mousemove', mouseMove);
Expand Down Expand Up @@ -76,8 +82,7 @@ export function mouseUp(chart, event) {
const {width: dragDistanceX, height: dragDistanceY} = rect;

// Remove drag start and end before chart update to stop drawing selected area
state.dragStart = null;
state.dragEnd = null;
state.dragStart = state.dragEnd = null;

const zoomThreshold = zoomOptions.threshold || 0;
if (dragDistanceX <= zoomThreshold && dragDistanceY <= zoomThreshold) {
Expand All @@ -95,6 +100,7 @@ export function mouseUp(chart, event) {
};
zoom(chart, amount, 'zoom');

setTimeout(() => (state.dragging = false), 500);
call(zoomOptions.onZoomComplete, [chart]);
}

Expand Down
4 changes: 2 additions & 2 deletions src/plugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,8 @@ export default {

beforeEvent(chart, args) {
const state = getState(chart);
if (args.event.type === 'click' && state.panning) {
// cancel the click event at pan end
if (args.event.type === 'click' && (state.panning || state.dragging)) {
// cancel the click event at pan/zoom end
return false;
}
},
Expand Down
14 changes: 14 additions & 0 deletions test/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,20 @@ jasmine.triggerWheelEvent = function(chart, init = {}) {
node.dispatchEvent(event);
};

jasmine.dispatchEvent = function(chart, type, pt, init = {}) {
const node = chart.canvas;
const rect = node.getBoundingClientRect();
const event = new MouseEvent(type, Object.assign({}, init, {
clientX: rect.left + pt.x,
clientY: rect.top + pt.y,
cancelable: true,
bubbles: true,
view: window
}));

node.dispatchEvent(event);
};

beforeEach(function() {
addMatchers();
});
Expand Down
75 changes: 74 additions & 1 deletion test/specs/zoom.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -299,7 +299,7 @@ describe('zoom', function() {
wheelEv[key + 'Key'] = true;
}

await jasmine.triggerWheelEvent(chart, wheelEv);
jasmine.triggerWheelEvent(chart, wheelEv);

if (pressed) {
expect(scaleX.options.min).not.toEqual(oldMinX);
Expand All @@ -315,6 +315,79 @@ describe('zoom', function() {
}
});

describe('drag with pan.modifierKey', function() {
for (const key of ['ctrl', 'alt', 'shift', 'meta']) {
for (const pressed of [true, false]) {
let chart, scaleX, scaleY;
it(`should ${pressed ? 'not ' : ''}change ${pressed ? 'without' : 'with'} key ${key}`, async function() {
const rejectedSpy = jasmine.createSpy('zoomRejected');
const clickSpy = jasmine.createSpy('clicked');
chart = window.acquireChart({
type: 'line',
data,
options: {
scales: {
x: {
type: 'linear',
min: 0,
max: 10
},
y: {
type: 'linear'
}
},
plugins: {
zoom: {
pan: {
modifierKey: key,
},
zoom: {
enabled: true,
drag: true,
mode: 'x',
onZoomRejected: rejectedSpy
}
}
},
onClick: clickSpy
}
});

scaleX = chart.scales.x;
scaleY = chart.scales.y;

const oldMinX = scaleX.options.min;
const oldMaxX = scaleX.options.max;

const pt = {
x: scaleX.getPixelForValue(1.5),
y: scaleY.getPixelForValue(1.1),
};
const pt2 = {x: pt.x + 20, y: pt.y + 20};
const init = {};
if (pressed) {
init[key + 'Key'] = true;
}

jasmine.dispatchEvent(chart, 'mousedown', pt, init);
jasmine.dispatchEvent(chart, 'mousemove', pt2, init);
jasmine.dispatchEvent(chart, 'mouseup', pt2, init);

if (pressed) {
expect(scaleX.options.min).toEqual(oldMinX);
expect(scaleX.options.max).toEqual(oldMaxX);
expect(rejectedSpy).toHaveBeenCalled();
} else {
expect(scaleX.options.min).not.toEqual(oldMinX);
expect(scaleX.options.max).not.toEqual(oldMaxX);
expect(rejectedSpy).not.toHaveBeenCalled();
}
expect(clickSpy).not.toHaveBeenCalled();
});
}
}
});

describe('with overScaleMode = y and mode = xy', function() {
const config = {
type: 'line',
Expand Down