Skip to content

Commit b2aeae6

Browse files
authored
fix: incorrect zoom out factor when using using wheel (#888)
* fix: incorrect zoom out factor when using using wheel * fix: lint * fix: increase jasmine timeout
1 parent 437b2ca commit b2aeae6

File tree

6 files changed

+77
-28
lines changed

6 files changed

+77
-28
lines changed

docs/samples/api.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ const actions = [
7171
}, {
7272
name: 'Zoom -10%',
7373
handler(chart) {
74-
chart.zoom(0.9);
74+
chart.zoom(2 - 1 / 0.9);
7575
},
7676
}, {
7777
name: 'Zoom x +10%',
@@ -81,7 +81,7 @@ const actions = [
8181
}, {
8282
name: 'Zoom x -10%',
8383
handler(chart) {
84-
chart.zoom({x: 0.9});
84+
chart.zoom({x: 2 - 1 / 0.9});
8585
},
8686
}, {
8787
name: 'Pan x 100px (anim)',

docs/samples/wheel/log.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,12 @@ const scales = {
126126
display: true,
127127
text: 'Frequency',
128128
},
129+
},
130+
y: {
131+
// constant width for the scale
132+
afterFit: (scale) => {
133+
scale.width = 50;
134+
},
129135
}
130136
};
131137
// </block:scales>
@@ -157,7 +163,7 @@ const config = {
157163
},
158164
}
159165
},
160-
}
166+
},
161167
};
162168
// </block:config>
163169

karma.conf.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,8 @@ module.exports = function(karma) {
3333

3434
client: {
3535
jasmine: {
36-
stopOnSpecFailure: !!karma.autoWatch
36+
stopOnSpecFailure: !!karma.autoWatch,
37+
timeoutInterval: 10000,
3738
}
3839
},
3940

src/handlers.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -216,10 +216,11 @@ export function wheel(chart, event) {
216216
}
217217

218218
const rect = event.target.getBoundingClientRect();
219-
const speed = 1 + (event.deltaY >= 0 ? -zoomOptions.wheel.speed : zoomOptions.wheel.speed);
219+
const speed = zoomOptions.wheel.speed;
220+
const percentage = event.deltaY >= 0 ? 2 - 1 / (1 - speed) : 1 + speed;
220221
const amount = {
221-
x: speed,
222-
y: speed,
222+
x: percentage,
223+
y: percentage,
223224
focalPoint: {
224225
x: event.clientX - rect.left,
225226
y: event.clientY - rect.top

src/scale.types.js

Lines changed: 40 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {valueOrDefault} from 'chart.js/helpers';
1+
import {almostEquals, valueOrDefault} from 'chart.js/helpers';
22
import {getState} from './state';
33

44
/**
@@ -112,6 +112,40 @@ function linearRange(scale, pixel0, pixel1) {
112112
};
113113
}
114114

115+
/**
116+
* @param {number} range
117+
* @param {{ min: number; max: number; minLimit: number; maxLimit: number; }} options
118+
* @param {{ min: { scale?: number; options?: number; }; max: { scale?: number; options?: number; }}} [originalLimits]
119+
*/
120+
function fixRange(range, {min, max, minLimit, maxLimit}, originalLimits) {
121+
const offset = (range - max + min) / 2;
122+
min -= offset;
123+
max += offset;
124+
125+
// In case the values are really close to the original values, use the original values.
126+
const origMin = originalLimits.min.options ?? originalLimits.min.scale;
127+
const origMax = originalLimits.max.options ?? originalLimits.max.scale;
128+
129+
const epsilon = range / 1e6;
130+
if (almostEquals(min, origMin, epsilon)) {
131+
min = origMin;
132+
}
133+
if (almostEquals(max, origMax, epsilon)) {
134+
max = origMax;
135+
}
136+
137+
// Apply limits
138+
if (min < minLimit) {
139+
min = minLimit;
140+
max = Math.min(minLimit + range, maxLimit);
141+
} else if (max > maxLimit) {
142+
max = maxLimit;
143+
min = Math.max(maxLimit - range, minLimit);
144+
}
145+
146+
return {min, max};
147+
}
148+
115149
/**
116150
* @param {Scale} scale
117151
* @param {ScaleRange} minMax
@@ -141,24 +175,15 @@ export function updateRange(scale, {min, max}, limits, zoom = false) {
141175
return true;
142176
}
143177

144-
const offset = (range - max + min) / 2;
145-
min -= offset;
146-
max += offset;
178+
const newRange = fixRange(range, {min, max, minLimit, maxLimit}, state.originalScaleLimits[scale.id]);
147179

148-
if (min < minLimit) {
149-
min = minLimit;
150-
max = Math.min(minLimit + range, maxLimit);
151-
} else if (max > maxLimit) {
152-
max = maxLimit;
153-
min = Math.max(maxLimit - range, minLimit);
154-
}
155-
scaleOpts.min = min;
156-
scaleOpts.max = max;
180+
scaleOpts.min = newRange.min;
181+
scaleOpts.max = newRange.max;
157182

158-
state.updatedScaleLimits[scale.id] = {min, max};
183+
state.updatedScaleLimits[scale.id] = newRange;
159184

160185
// return true if the scale range is changed
161-
return scale.parse(min) !== scale.min || scale.parse(max) !== scale.max;
186+
return scale.parse(newRange.min) !== scale.min || scale.parse(newRange.max) !== scale.max;
162187
}
163188

164189
function zoomNumericalScale(scale, zoom, center, limits) {

test/specs/zoom.wheel.spec.js

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -278,34 +278,50 @@ describe('zoom with wheel', function() {
278278
const chart = window.acquireChart(config);
279279
const scaleY = chart.scales.y;
280280

281-
const wheelEv = {
281+
const zoomIn = {
282282
x: Math.round(scaleY.left + (scaleY.right - scaleY.left) / 2),
283283
y: Math.round(scaleY.top + (scaleY.bottom - scaleY.top) / 2),
284284
deltaY: -1
285285
};
286286

287+
const zoomOut = {
288+
...zoomIn,
289+
deltaY: 1
290+
};
291+
287292
expect(scaleY.min).toBe(1);
288293
expect(scaleY.max).toBe(10000);
289294

290-
jasmine.triggerWheelEvent(chart, wheelEv);
295+
jasmine.triggerWheelEvent(chart, zoomIn);
291296

292297
expect(scaleY.min).toBeCloseTo(1.6, 1);
293298
expect(scaleY.max).toBeCloseTo(6310, -1);
294299

295-
jasmine.triggerWheelEvent(chart, wheelEv);
300+
jasmine.triggerWheelEvent(chart, zoomIn);
296301

297302
expect(scaleY.min).toBeCloseTo(2.4, 1);
298303
expect(scaleY.max).toBeCloseTo(4170, -1);
299304

305+
jasmine.triggerWheelEvent(chart, zoomOut);
306+
jasmine.triggerWheelEvent(chart, zoomOut);
307+
308+
expect(scaleY.min).toBe(1);
309+
expect(scaleY.max).toBe(10000);
310+
300311
chart.resetZoom();
301312

302313
expect(scaleY.min).toBe(1);
303314
expect(scaleY.max).toBe(10000);
304315

305-
jasmine.triggerWheelEvent(chart, {...wheelEv, deltaY: 1});
316+
jasmine.triggerWheelEvent(chart, zoomOut);
317+
318+
expect(scaleY.min).toBeCloseTo(0.6, 1);
319+
expect(scaleY.max).toBeCloseTo(16700, -2);
320+
321+
jasmine.triggerWheelEvent(chart, zoomIn);
306322

307-
expect(scaleY.min).toBe(0.6);
308-
expect(scaleY.max).toBeCloseTo(15800, -2);
323+
expect(scaleY.min).toBeCloseTo(1);
324+
expect(scaleY.max).toBeCloseTo(10000);
309325
});
310326
});
311327

0 commit comments

Comments
 (0)