Skip to content

Commit 0259d37

Browse files
authored
Export zoomRect and make it more configurable (#659)
* Implement zoomRect functions Normal zooming supports customizing zoom behavior using zoomFunctions, but the drag-to-zoom functionality that's implemented by zoomRect bypasses zoomFunctions and calls updateRange correctly. I have a custom scale implementation that relies on zoomFunctions, so trying to enable drag-to-zoom causes problems. (Specifically, I'm breaking up the axis into viewports so that I can stack multiple series within this chart - somewhat like this example. The scales for the individual portions of the chart shouldn't be directly zoomed, because their layout is managed by a separate, controlling scale, so their zoomFunctions are no-ops.) Zooming by rectangle appears to be a distinctly different interface than zooming by percentage and focal point, so it seems reasonable to add a zoomRectFunctions to let scales customize that behavior and cover this use case. * Expose zoomRect; remove incorrect TypeScript type definitions To go with adding stronger zoomRect functionality, it makes sense to expose the `zoomRect` function directly. The TypeScript type definitions said that standalone functions like `zoom` were exported, but these don't actually exist. Fix that. * Restore incorrectly deleted type definitions And properly export zoomRect. * Further update comments * Restore type tests * Fix type
1 parent 7ec113a commit 0259d37

8 files changed

Lines changed: 46 additions & 21 deletions

File tree

docs/guide/developers.md

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ Returns whether the chart has been zoomed or panned - i.e. whether the initial s
4444

4545
## Custom Scales
4646

47-
You can extend chartjs-plugin-zoom with support for [custom scales](https://www.chartjs.org/docs/latest/developers/axes.html) by using the zoom plugin's `zoomFunctions` and `panFunctions` members. These objects are indexed by scale types (scales' `id` members) and give optional handlers for zoom and pan functionality.
47+
You can extend chartjs-plugin-zoom with support for [custom scales](https://www.chartjs.org/docs/latest/developers/axes.html) by using the zoom plugin's `zoomFunctions`, `zoomRectFunctions`, and `panFunctions` members. These objects are indexed by scale types (scales' `id` members) and give optional handlers for zoom and pan functionality.
4848

4949
```js
5050
import {Scale} from 'chart.js';
@@ -57,17 +57,22 @@ MyScale.id = 'myScale';
5757
MyScale.defaults = defaultConfigObject;
5858

5959
zoomPlugin.zoomFunctions.myScale = (scale, zoom, center, limits) => false;
60+
zoomPlugin.zoomRectFunctions.myScale = (scale, from, to, limits) => false;
6061
zoomPlugin.panFunctions.myScale = (scale, delta, limits) => false;
62+
// zoomRectFunctions can normally be omitted, since zooming by specific pixel
63+
// coordinates rarely needs special handling.
6164
```
6265

63-
The zoom and pan functions take the following arguments:
66+
The zoom, zoomRect, and pan functions take the following arguments:
6467

6568
| Name | Type | For | Description
6669
| ---- | ---- | --- | ----------
6770
| `scale` | `Scale` | Zoom, Pan | The custom scale instance (usually derived from `Chart.Scale`)
6871
| `zoom` | `number` | Zoom | The zoom fraction; 1.0 is unzoomed, 0.5 means zoomed in to 50% of the original area, etc.
6972
| `center` | `{x, y}` | Zoom | Pixel coordinates of the center of the zoom operation. `{x: 0, y: 0}` is the upper left corner of the chart's canvas.
73+
| `from` | `number` | ZoomRect | Pixel coordinate of the start of the zoomRect operation.
74+
| `to` | `number` | ZoomRect | Pixel coordinate of the end of the zoomRect operation.
7075
| `delta` | `number` | Pan | Pixel amount to pan by
7176
| `limits` | [Limits](./options#limits) | Zoom, Pan | Zoom and pan limits (from chart options)
7277

73-
For examples, see chartjs-plugin-zoom's [default zoomFunctions and panFunctions handling for standard Chart.js axes](https://github.com/chartjs/chartjs-plugin-zoom/blob/v1.0.1/src/scale.types.js#L128).
78+
For examples, see chartjs-plugin-zoom's [default zoomFunctions, zoomRectFunctions, and panFunctions handling for standard Chart.js axes](https://github.com/chartjs/chartjs-plugin-zoom/blob/v1.0.1/src/scale.types.js#L128).

src/core.js

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {each, callback as call, sign, valueOrDefault} from 'chart.js/helpers';
2-
import {panFunctions, updateRange, zoomFunctions} from './scale.types';
2+
import {panFunctions, updateRange, zoomFunctions, zoomRectFunctions} from './scale.types';
33
import {getState} from './state';
44
import {directionEnabled, getEnabledScalesByPoint} from './utils';
55

@@ -43,6 +43,11 @@ function doZoom(scale, amount, center, limits) {
4343
call(fn, [scale, amount, center, limits]);
4444
}
4545

46+
function doZoomRect(scale, amount, from, to, limits) {
47+
const fn = zoomRectFunctions[scale.type] || zoomRectFunctions.default;
48+
call(fn, [scale, amount, from, to, limits]);
49+
}
50+
4651
function getCenter(chart) {
4752
const ca = chart.chartArea;
4853
return {
@@ -80,15 +85,6 @@ export function zoom(chart, amount, transition = 'none') {
8085
call(zoomOptions.onZoom, [{chart}]);
8186
}
8287

83-
function getRange(scale, pixel0, pixel1) {
84-
const v0 = scale.getValueForPixel(pixel0);
85-
const v1 = scale.getValueForPixel(pixel1);
86-
return {
87-
min: Math.min(v0, v1),
88-
max: Math.max(v0, v1)
89-
};
90-
}
91-
9288
export function zoomRect(chart, p0, p1, transition = 'none') {
9389
const state = getState(chart);
9490
const {options: {limits, zoom: zoomOptions}} = state;
@@ -100,9 +96,9 @@ export function zoomRect(chart, p0, p1, transition = 'none') {
10096

10197
each(chart.scales, function(scale) {
10298
if (scale.isHorizontal() && xEnabled) {
103-
updateRange(scale, getRange(scale, p0.x, p1.x), limits, true);
99+
doZoomRect(scale, p0.x, p1.x, limits);
104100
} else if (!scale.isHorizontal() && yEnabled) {
105-
updateRange(scale, getRange(scale, p0.y, p1.y), limits, true);
101+
doZoomRect(scale, p0.y, p1.y, limits);
106102
}
107103
});
108104

src/index.esm.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
import plugin from './plugin';
22

33
export default plugin;
4-
export {pan, zoom, zoomScale, resetZoom} from './core';
4+
export {pan, zoom, zoomRect, zoomScale, resetZoom} from './core';

src/plugin.js

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import Hammer from 'hammerjs';
22
import {addListeners, computeDragRect, removeListeners} from './handlers';
33
import {startHammer, stopHammer} from './hammer';
4-
import {pan, zoom, resetZoom, zoomScale, getZoomLevel, getInitialScaleBounds, isZoomedOrPanned} from './core';
5-
import {panFunctions, zoomFunctions} from './scale.types';
4+
import {pan, zoom, resetZoom, zoomScale, getZoomLevel, getInitialScaleBounds, isZoomedOrPanned, zoomRect} from './core';
5+
import {panFunctions, zoomFunctions, zoomRectFunctions} from './scale.types';
66
import {getState, removeState} from './state';
77
import {version} from '../package.json';
88

@@ -53,6 +53,7 @@ export default {
5353

5454
chart.pan = (delta, panScales, transition) => pan(chart, delta, panScales, transition);
5555
chart.zoom = (args, transition) => zoom(chart, args, transition);
56+
chart.zoomRect = (p0, p1, transition) => zoomRect(chart, p0, p1, transition);
5657
chart.zoomScale = (id, range, transition) => zoomScale(chart, id, range, transition);
5758
chart.resetZoom = (transition) => resetZoom(chart, transition);
5859
chart.getZoomLevel = () => getZoomLevel(chart);
@@ -107,6 +108,6 @@ export default {
107108
},
108109

109110
panFunctions,
110-
111-
zoomFunctions
111+
zoomFunctions,
112+
zoomRectFunctions,
112113
};

src/scale.types.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,15 @@ function getLimit(state, scale, scaleLimits, prop, fallback) {
2929
return valueOrDefault(limit, fallback);
3030
}
3131

32+
function getRange(scale, pixel0, pixel1) {
33+
const v0 = scale.getValueForPixel(pixel0);
34+
const v1 = scale.getValueForPixel(pixel1);
35+
return {
36+
min: Math.min(v0, v1),
37+
max: Math.max(v0, v1)
38+
};
39+
}
40+
3241
export function updateRange(scale, {min, max}, limits, zoom = false) {
3342
const state = getState(scale.chart);
3443
const {id, axis, options: scaleOpts} = scale;
@@ -72,6 +81,10 @@ function zoomNumericalScale(scale, zoom, center, limits) {
7281
return updateRange(scale, newRange, limits, true);
7382
}
7483

84+
function zoomRectNumericalScale(scale, from, to, limits) {
85+
updateRange(scale, getRange(scale, from, to), limits, true);
86+
}
87+
7588
const integerChange = (v) => v === 0 || isNaN(v) ? 0 : v < 0 ? Math.min(Math.round(v), -1) : Math.max(Math.round(v), 1);
7689

7790
function existCategoryFromMaxZoom(scale) {
@@ -158,6 +171,10 @@ export const zoomFunctions = {
158171
default: zoomNumericalScale,
159172
};
160173

174+
export const zoomRectFunctions = {
175+
default: zoomRectNumericalScale,
176+
};
177+
161178
export const panFunctions = {
162179
category: panCategoryScale,
163180
default: panNumericalScale,

test/specs/api.spec.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ describe('api', function() {
55
expect(typeof chart.pan).toBe('function');
66
expect(typeof chart.zoom).toBe('function');
77
expect(typeof chart.zoomScale).toBe('function');
8+
expect(typeof chart.zoomRect).toBe('function');
89
expect(typeof chart.resetZoom).toBe('function');
910
expect(typeof chart.getZoomLevel).toBe('function');
1011
expect(typeof chart.getInitialScaleBounds).toBe('function');

test/specs/module.spec.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,9 @@ describe('module', function() {
77
expect(window.ChartZoom.id).toBe('zoom');
88
});
99

10-
it ('should expose zoomFunctions and panFunctions', function() {
10+
it ('should expose zoomFunctions, zoomRectFunctions, and panFunctions', function() {
1111
expect(window.ChartZoom.zoomFunctions instanceof Object).toBe(true);
12+
expect(window.ChartZoom.zoomRectFunctions instanceof Object).toBe(true);
1213
expect(window.ChartZoom.panFunctions instanceof Object).toBe(true);
1314
});
1415

types/index.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ declare module 'chart.js' {
2121
interface Chart<TType extends keyof ChartTypeRegistry = keyof ChartTypeRegistry, TData = DistributiveArray<ChartTypeRegistry[TType]['defaultDataPoint']>, TLabel = unknown> {
2222
pan(pan: PanAmount, scales?: Scale[], mode?: UpdateMode): void;
2323
zoom(zoom: ZoomAmount, mode?: UpdateMode): void;
24+
zoomRect(p0: Point, p1: Point, mode?: UpdateMode): void;
2425
zoomScale(id: string, range: ScaleRange, mode?: UpdateMode): void;
2526
resetZoom(mode?: UpdateMode): void;
2627
getZoomLevel(): number;
@@ -30,6 +31,7 @@ declare module 'chart.js' {
3031
}
3132

3233
export type ZoomFunction = (scale: Scale, zoom: number, center: Point, limits: LimitOptions) => boolean;
34+
export type ZoomRectFunction = (scale: Scale, from: number, to: number, limits: LimitOptions) => boolean;
3335
export type PanFunction = (scale: Scale, delta: number, limits: LimitOptions) => boolean;
3436

3537
type ScaleFunctions<T> = {
@@ -40,13 +42,15 @@ type ScaleFunctions<T> = {
4042

4143
declare const Zoom: Plugin & {
4244
zoomFunctions: ScaleFunctions<ZoomFunction>;
45+
zoomRectFunctions: ScaleFunctions<ZoomRectFunction>;
4346
panFunctions: ScaleFunctions<PanFunction>;
4447
};
4548

4649
export default Zoom;
4750

4851
export function pan(chart: Chart, amount: PanAmount, scales?: Scale[], mode?: UpdateMode): void;
4952
export function zoom(chart: Chart, amount: ZoomAmount, mode?: UpdateMode): void;
53+
export function zoomRect(chart: Chart, p0: Point, p1: Point, mode?: UpdateMode): void;
5054
export function zoomScale(chart: Chart, scaleId: string, range: ScaleRange, mode?: UpdateMode): void;
5155
export function resetZoom(chart: Chart, mode?: UpdateMode): void;
5256
export function getZoomLevel(chart: Chart): number;

0 commit comments

Comments
 (0)