Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Export zoomRect and make it more configurable #659

Merged
merged 6 commits into from Sep 1, 2022
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.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 8 additions & 3 deletions docs/guide/developers.md
Expand Up @@ -44,7 +44,7 @@ Returns whether the chart has been zoomed or panned - i.e. whether the initial s

## Custom Scales

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.
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.

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

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

The zoom and pan functions take the following arguments:
The zoom, zoomRect, and pan functions take the following arguments:

| Name | Type | For | Description
| ---- | ---- | --- | ----------
| `scale` | `Scale` | Zoom, Pan | The custom scale instance (usually derived from `Chart.Scale`)
| `zoom` | `number` | Zoom | The zoom fraction; 1.0 is unzoomed, 0.5 means zoomed in to 50% of the original area, etc.
| `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.
| `from` | `number` | ZoomRect | Pixel coordinate of the start of the zoomRect operation.
| `to` | `number` | ZoomRect | Pixel coordinate of the end of the zoomRect operation.
| `delta` | `number` | Pan | Pixel amount to pan by
| `limits` | [Limits](./options#limits) | Zoom, Pan | Zoom and pan limits (from chart options)

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).
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).
20 changes: 8 additions & 12 deletions src/core.js
@@ -1,5 +1,5 @@
import {each, callback as call, sign, valueOrDefault} from 'chart.js/helpers';
import {panFunctions, updateRange, zoomFunctions} from './scale.types';
import {panFunctions, updateRange, zoomFunctions, zoomRectFunctions} from './scale.types';
import {getState} from './state';
import {directionEnabled, getEnabledScalesByPoint} from './utils';

Expand Down Expand Up @@ -43,6 +43,11 @@ function doZoom(scale, amount, center, limits) {
call(fn, [scale, amount, center, limits]);
}

function doZoomRect(scale, amount, from, to, limits) {
const fn = zoomRectFunctions[scale.type] || zoomRectFunctions.default;
call(fn, [scale, amount, from, to, limits]);
}

function getCenter(chart) {
const ca = chart.chartArea;
return {
Expand Down Expand Up @@ -81,15 +86,6 @@ export function zoom(chart, amount, transition = 'none') {
call(zoomOptions.onZoom, [{chart}]);
}

function getRange(scale, pixel0, pixel1) {
const v0 = scale.getValueForPixel(pixel0);
const v1 = scale.getValueForPixel(pixel1);
return {
min: Math.min(v0, v1),
max: Math.max(v0, v1)
};
}

export function zoomRect(chart, p0, p1, transition = 'none') {
const state = getState(chart);
const {options: {limits, zoom: zoomOptions}} = state;
Expand All @@ -101,9 +97,9 @@ export function zoomRect(chart, p0, p1, transition = 'none') {

each(chart.scales, function(scale) {
if (scale.isHorizontal() && xEnabled) {
updateRange(scale, getRange(scale, p0.x, p1.x), limits, true);
doZoomRect(scale, p0.x, p1.x, limits);
} else if (!scale.isHorizontal() && yEnabled) {
updateRange(scale, getRange(scale, p0.y, p1.y), limits, true);
doZoomRect(scale, p0.y, p1.y, limits);
}
});

Expand Down
2 changes: 1 addition & 1 deletion src/index.esm.js
@@ -1,4 +1,4 @@
import plugin from './plugin';

export default plugin;
export {pan, zoom, zoomScale, resetZoom} from './core';
export {pan, zoom, zoomRect, zoomScale, resetZoom} from './core';
9 changes: 5 additions & 4 deletions src/plugin.js
@@ -1,8 +1,8 @@
import Hammer from 'hammerjs';
import {addListeners, computeDragRect, removeListeners} from './handlers';
import {startHammer, stopHammer} from './hammer';
import {pan, zoom, resetZoom, zoomScale, getZoomLevel, getInitialScaleBounds, isZoomedOrPanned} from './core';
import {panFunctions, zoomFunctions} from './scale.types';
import {pan, zoom, resetZoom, zoomScale, getZoomLevel, getInitialScaleBounds, isZoomedOrPanned, zoomRect} from './core';
import {panFunctions, zoomFunctions, zoomRectFunctions} from './scale.types';
import {getState, removeState} from './state';
import {version} from '../package.json';

Expand Down Expand Up @@ -49,6 +49,7 @@ export default {

chart.pan = (delta, panScales, transition) => pan(chart, delta, panScales, transition);
chart.zoom = (args, transition) => zoom(chart, args, transition);
chart.zoomRect = (p0, p1, transition) => zoomRect(chart, p0, p1, transition);
chart.zoomScale = (id, range, transition) => zoomScale(chart, id, range, transition);
chart.resetZoom = (transition) => resetZoom(chart, transition);
chart.getZoomLevel = () => getZoomLevel(chart);
Expand Down Expand Up @@ -103,6 +104,6 @@ export default {
},

panFunctions,

zoomFunctions
zoomFunctions,
zoomRectFunctions,
};
17 changes: 17 additions & 0 deletions src/scale.types.js
Expand Up @@ -29,6 +29,15 @@ function getLimit(state, scale, scaleLimits, prop, fallback) {
return valueOrDefault(limit, fallback);
}

function getRange(scale, pixel0, pixel1) {
const v0 = scale.getValueForPixel(pixel0);
const v1 = scale.getValueForPixel(pixel1);
return {
min: Math.min(v0, v1),
max: Math.max(v0, v1)
};
}

export function updateRange(scale, {min, max}, limits, zoom = false) {
const state = getState(scale.chart);
const {id, axis, options: scaleOpts} = scale;
Expand Down Expand Up @@ -72,6 +81,10 @@ function zoomNumericalScale(scale, zoom, center, limits) {
return updateRange(scale, newRange, limits, true);
}

function zoomRectNumericalScale(scale, from, to, limits) {
updateRange(scale, getRange(scale, from, to), limits, true);
}

const integerChange = (v) => v === 0 || isNaN(v) ? 0 : v < 0 ? Math.min(Math.round(v), -1) : Math.max(Math.round(v), 1);

function existCategoryFromMaxZoom(scale) {
Expand Down Expand Up @@ -158,6 +171,10 @@ export const zoomFunctions = {
default: zoomNumericalScale,
};

export const zoomRectFunctions = {
default: zoomRectNumericalScale,
};

export const panFunctions = {
category: panCategoryScale,
default: panNumericalScale,
Expand Down
1 change: 1 addition & 0 deletions test/specs/api.spec.js
Expand Up @@ -5,6 +5,7 @@ describe('api', function() {
expect(typeof chart.pan).toBe('function');
expect(typeof chart.zoom).toBe('function');
expect(typeof chart.zoomScale).toBe('function');
expect(typeof chart.zoomRect).toBe('function');
expect(typeof chart.resetZoom).toBe('function');
expect(typeof chart.getZoomLevel).toBe('function');
expect(typeof chart.getInitialScaleBounds).toBe('function');
Expand Down
3 changes: 2 additions & 1 deletion test/specs/module.spec.js
Expand Up @@ -7,8 +7,9 @@ describe('module', function() {
expect(window.ChartZoom.id).toBe('zoom');
});

it ('should expose zoomFunctions and panFunctions', function() {
it ('should expose zoomFunctions, zoomRectFunctions, and panFunctions', function() {
expect(window.ChartZoom.zoomFunctions instanceof Object).toBe(true);
expect(window.ChartZoom.zoomRectFunctions instanceof Object).toBe(true);
expect(window.ChartZoom.panFunctions instanceof Object).toBe(true);
});

Expand Down
4 changes: 4 additions & 0 deletions types/index.d.ts
Expand Up @@ -21,6 +21,7 @@ declare module 'chart.js' {
interface Chart<TType extends keyof ChartTypeRegistry = keyof ChartTypeRegistry, TData = DistributiveArray<ChartTypeRegistry[TType]['defaultDataPoint']>, TLabel = unknown> {
pan(pan: PanAmount, scales?: Scale[], mode?: UpdateMode): void;
zoom(zoom: ZoomAmount, mode?: UpdateMode): void;
zoomRect(p0: Point, p1: Point, mode?: UpdateMode): void;
zoomScale(id: string, range: ScaleRange, mode?: UpdateMode): void;
resetZoom(mode?: UpdateMode): void;
getZoomLevel(): number;
Expand All @@ -30,6 +31,7 @@ declare module 'chart.js' {
}

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

type ScaleFunctions<T> = {
Expand All @@ -40,13 +42,15 @@ type ScaleFunctions<T> = {

declare const Zoom: Plugin & {
zoomFunctions: ScaleFunctions<ZoomFunction>;
zoomRectFunctions: ScaleFunctions<ZoomRectFunction>;
panFunctions: ScaleFunctions<PanFunction>;
};

export default Zoom;

export function pan(chart: Chart, amount: PanAmount, scales?: Scale[], mode?: UpdateMode): void;
export function zoom(chart: Chart, amount: ZoomAmount, mode?: UpdateMode): void;
export function zoomRect(chart: Chart, p0: Point, p1: Point, mode?: UpdateMode): void;
export function zoomScale(chart: Chart, scaleId: string, range: ScaleRange, mode?: UpdateMode): void;
export function resetZoom(chart: Chart, mode?: UpdateMode): void;
export function getZoomLevel(chart: Chart): number;
Expand Down