]>
Commit | Line | Data |
---|---|---|
947f0963 TL |
1 | /** |
2 | * The PanZoom interaction allows the user to navigate the data for one or more chart | |
3 | * axes by panning and/or zooming. Navigation can be limited to particular axes. Zooming is | |
4 | * performed by pinching on the chart or axis area; panning is performed by single-touch dragging. | |
5 | * The interaction only works with cartesian charts/series. | |
6 | * | |
7 | * For devices which do not support multiple-touch events, zooming can not be done via pinch | |
8 | * gestures; in this case the interaction will allow the user to perform both zooming and panning | |
9 | * using the same single-touch drag gesture. | |
10 | * {@link #modeToggleButton} provides a button to indicate and toggle between two modes. | |
11 | * | |
12 | * @example | |
13 | * Ext.create({ | |
14 | * renderTo: document.body, | |
15 | * xtype: 'cartesian', | |
16 | * width: 600, | |
17 | * height: 400, | |
18 | * insetPadding: 40, | |
19 | * interactions: [{ | |
20 | * type: 'panzoom', | |
21 | * zoomOnPan: true | |
22 | * }], | |
23 | * store: { | |
24 | * fields: ['name', 'data1', 'data2', 'data3', 'data4', 'data5'], | |
25 | * data: [{ | |
26 | * 'name': 'metric one', | |
27 | * 'data1': 10, | |
28 | * 'data2': 12, | |
29 | * 'data3': 14, | |
30 | * 'data4': 8, | |
31 | * 'data5': 13 | |
32 | * }, { | |
33 | * 'name': 'metric two', | |
34 | * 'data1': 7, | |
35 | * 'data2': 8, | |
36 | * 'data3': 16, | |
37 | * 'data4': 10, | |
38 | * 'data5': 3 | |
39 | * }, { | |
40 | * 'name': 'metric three', | |
41 | * 'data1': 5, | |
42 | * 'data2': 2, | |
43 | * 'data3': 14, | |
44 | * 'data4': 12, | |
45 | * 'data5': 7 | |
46 | * }, { | |
47 | * 'name': 'metric four', | |
48 | * 'data1': 2, | |
49 | * 'data2': 14, | |
50 | * 'data3': 6, | |
51 | * 'data4': 1, | |
52 | * 'data5': 23 | |
53 | * }, { | |
54 | * 'name': 'metric five', | |
55 | * 'data1': 27, | |
56 | * 'data2': 38, | |
57 | * 'data3': 36, | |
58 | * 'data4': 13, | |
59 | * 'data5': 33 | |
60 | * }] | |
61 | * }, | |
62 | * axes: [{ | |
63 | * type: 'numeric', | |
64 | * position: 'left', | |
65 | * fields: ['data1'], | |
66 | * title: { | |
67 | * text: 'Sample Values', | |
68 | * fontSize: 15 | |
69 | * }, | |
70 | * grid: true, | |
71 | * minimum: 0 | |
72 | * }, { | |
73 | * type: 'category', | |
74 | * position: 'bottom', | |
75 | * fields: ['name'], | |
76 | * title: { | |
77 | * text: 'Sample Values', | |
78 | * fontSize: 15 | |
79 | * } | |
80 | * }], | |
81 | * series: [{ | |
82 | * type: 'line', | |
83 | * highlight: { | |
84 | * size: 7, | |
85 | * radius: 7 | |
86 | * }, | |
87 | * style: { | |
88 | * stroke: 'rgb(143,203,203)' | |
89 | * }, | |
90 | * xField: 'name', | |
91 | * yField: 'data1', | |
92 | * marker: { | |
93 | * type: 'path', | |
94 | * path: ['M', - 2, 0, 0, 2, 2, 0, 0, - 2, 'Z'], | |
95 | * stroke: 'blue', | |
96 | * lineWidth: 0 | |
97 | * } | |
98 | * }, { | |
99 | * type: 'line', | |
100 | * highlight: { | |
101 | * size: 7, | |
102 | * radius: 7 | |
103 | * }, | |
104 | * fill: true, | |
105 | * xField: 'name', | |
106 | * yField: 'data3', | |
107 | * marker: { | |
108 | * type: 'circle', | |
109 | * radius: 4, | |
110 | * lineWidth: 0 | |
111 | * } | |
112 | * }] | |
113 | * }); | |
114 | * | |
115 | * The configuration object for the `panzoom` interaction type should specify which axes | |
116 | * will be made navigable via the `axes` config. See the {@link #axes} config documentation | |
117 | * for details on the allowed formats. If the `axes` config is not specified, it will default | |
118 | * to making all axes navigable with the default axis options. | |
119 | * | |
120 | */ | |
121 | Ext.define('Ext.chart.interactions.PanZoom', { | |
122 | ||
123 | extend: 'Ext.chart.interactions.Abstract', | |
124 | ||
125 | type: 'panzoom', | |
126 | alias: 'interaction.panzoom', | |
127 | requires: [ | |
128 | 'Ext.draw.Animator' | |
129 | ], | |
130 | ||
131 | config: { | |
132 | ||
133 | /** | |
134 | * @cfg {Object/Array} axes | |
135 | * Specifies which axes should be made navigable. The config value can take the following | |
136 | * formats: | |
137 | * | |
138 | * - An Object with keys corresponding to the {@link Ext.chart.axis.Axis#position position} | |
139 | * of each axis that should be made navigable. Each key's value can either be an Object | |
140 | * with further configuration options for each axis or simply `true` for a default set | |
141 | * of options. | |
142 | * | |
143 | * { | |
144 | * type: 'panzoom', | |
145 | * axes: { | |
146 | * left: { | |
147 | * maxZoom: 5, | |
148 | * allowPan: false | |
149 | * }, | |
150 | * bottom: true | |
151 | * } | |
152 | * } | |
153 | * | |
154 | * If using the full Object form, the following options can be specified for each axis: | |
155 | * | |
156 | * - minZoom (Number) A minimum zoom level for the axis. Defaults to `1` which is its | |
157 | * natural size. | |
158 | * - maxZoom (Number) A maximum zoom level for the axis. Defaults to `10`. | |
159 | * - startZoom (Number) A starting zoom level for the axis. Defaults to `1`. | |
160 | * - allowZoom (Boolean) Whether zooming is allowed for the axis. Defaults to `true`. | |
161 | * - allowPan (Boolean) Whether panning is allowed for the axis. Defaults to `true`. | |
162 | * - startPan (Boolean) A starting panning offset for the axis. Defaults to `0`. | |
163 | * | |
164 | * - An Array of strings, each one corresponding to the {@link Ext.chart.axis.Axis#position | |
165 | * position} of an axis that should be made navigable. The default options will be used | |
166 | * for each named axis. | |
167 | * | |
168 | * { | |
169 | * type: 'panzoom', | |
170 | * axes: ['left', 'bottom'] | |
171 | * } | |
172 | * | |
173 | * If the `axes` config is not specified, it will default to making all axes navigable | |
174 | * with the default axis options. | |
175 | */ | |
176 | axes: { | |
177 | top: {}, | |
178 | right: {}, | |
179 | bottom: {}, | |
180 | left: {} | |
181 | }, | |
182 | ||
183 | minZoom: null, | |
184 | ||
185 | maxZoom: null, | |
186 | ||
187 | /** | |
188 | * @cfg {Boolean} showOverflowArrows | |
189 | * If `true`, arrows will be conditionally shown at either end of each axis to indicate that | |
190 | * the axis is overflowing and can therefore be panned in that direction. Set this | |
191 | * to `false` to prevent the arrows from being displayed. | |
192 | */ | |
193 | showOverflowArrows: true, | |
194 | ||
195 | /** | |
196 | * @cfg {Object} overflowArrowOptions | |
197 | * A set of optional overrides for the overflow arrow sprites' options. Only relevant when | |
198 | * {@link #showOverflowArrows} is `true`. | |
199 | */ | |
200 | ||
201 | /** | |
202 | * @cfg {String} panGesture | |
203 | * Defines the gesture that initiates panning. | |
204 | * @private | |
205 | */ | |
206 | panGesture: 'drag', | |
207 | ||
208 | /** | |
209 | * @cfg {String} zoomGesture | |
210 | * Defines the gesture that initiates zooming. | |
211 | * @private | |
212 | */ | |
213 | zoomGesture: 'pinch', | |
214 | ||
215 | /** | |
216 | * @cfg {Boolean} zoomOnPanGesture | |
217 | * @deprecated 6.2 Please use {@link #zoomOnPan} instead. | |
218 | * If `true`, the pan gesture will zoom the chart. | |
219 | */ | |
220 | zoomOnPanGesture: null, | |
221 | ||
222 | /** | |
223 | * @cfg {Boolean} zoomOnPan | |
224 | * If `true`, the pan gesture will zoom the chart. | |
225 | */ | |
226 | zoomOnPan: false, | |
227 | ||
228 | /** | |
229 | * @cfg {Boolean} [doubleTapReset=false] | |
230 | * If `true`, the double tap on a chart will reset the current pan/zoom to show the whole | |
231 | * chart. | |
232 | */ | |
233 | doubleTapReset: false, | |
234 | ||
235 | modeToggleButton: { | |
236 | xtype: 'segmentedbutton', | |
237 | width: 200, | |
238 | defaults: { ui: 'default-toolbar' }, | |
239 | cls: Ext.baseCSSPrefix + 'panzoom-toggle', | |
240 | items: [{ | |
241 | text: 'Pan', | |
242 | value: 'pan' | |
243 | }, { | |
244 | text: 'Zoom', | |
245 | value: 'zoom' | |
246 | }] | |
247 | }, | |
248 | ||
249 | hideLabelInGesture: false // Ext.os.is.Android | |
250 | }, | |
251 | ||
252 | stopAnimationBeforeSync: true, | |
253 | ||
254 | applyAxes: function(axesConfig, oldAxesConfig) { | |
255 | return Ext.merge(oldAxesConfig || {}, axesConfig); | |
256 | }, | |
257 | ||
258 | updateZoomOnPan: function(zoomOnPan) { | |
259 | var button = this.getModeToggleButton(); | |
260 | ||
261 | button.setValue(zoomOnPan ? 'zoom' : 'pan'); | |
262 | }, | |
263 | ||
264 | updateZoomOnPanGesture: function(zoomOnPanGesture) { | |
265 | this.setZoomOnPan(zoomOnPanGesture); | |
266 | }, | |
267 | ||
268 | getZoomOnPanGesture: function() { | |
269 | return this.getZoomOnPan(); | |
270 | }, | |
271 | ||
272 | applyModeToggleButton: function(button, oldButton) { | |
273 | return Ext.factory(button, 'Ext.button.Segmented', oldButton); | |
274 | }, | |
275 | ||
276 | updateModeToggleButton: function(button) { | |
277 | if (button) { | |
278 | button.on('change', 'onModeToggleChange', this); | |
279 | } | |
280 | }, | |
281 | ||
282 | onModeToggleChange: function(segmentedButton, value) { | |
283 | this.setZoomOnPan(value === 'zoom'); | |
284 | }, | |
285 | ||
286 | getGestures: function() { | |
287 | var me = this, | |
288 | gestures = {}, | |
289 | pan = me.getPanGesture(), | |
290 | zoom = me.getZoomGesture(); | |
291 | ||
292 | gestures[zoom] = 'onZoomGestureMove'; | |
293 | gestures[zoom + 'start'] = 'onZoomGestureStart'; | |
294 | gestures[zoom + 'end'] = 'onZoomGestureEnd'; | |
295 | gestures[pan] = 'onPanGestureMove'; | |
296 | gestures[pan + 'start'] = 'onPanGestureStart'; | |
297 | gestures[pan + 'end'] = 'onPanGestureEnd'; | |
298 | gestures.doubletap = 'onDoubleTap'; | |
299 | ||
300 | return gestures; | |
301 | }, | |
302 | ||
303 | onDoubleTap: function(e) { | |
304 | var me = this, | |
305 | doubleTapReset = me.getDoubleTapReset(), | |
306 | chart, axes, axis, i, ln; | |
307 | ||
308 | if (doubleTapReset) { | |
309 | chart = me.getChart(); | |
310 | axes = chart.getAxes(); | |
311 | ||
312 | for (i = 0, ln = axes.length; i < ln; i++) { | |
313 | axis = axes[i]; | |
314 | axis.setVisibleRange([0, 1]); | |
315 | } | |
316 | ||
317 | chart.redraw(); | |
318 | } | |
319 | }, | |
320 | ||
321 | onPanGestureStart: function(e) { | |
322 | var me = this, | |
323 | chart, rect, xy; | |
324 | ||
325 | if (!e || !e.touches || e.touches.length < 2) { // Limit drags to single touch | |
326 | chart = me.getChart(); | |
327 | rect = chart.getInnerRect(); | |
328 | xy = chart.element.getXY(); | |
329 | ||
330 | e.claimGesture(); | |
331 | chart.suspendAnimation(); | |
332 | ||
333 | me.startX = e.getX() - xy[0] - rect[0]; | |
334 | me.startY = e.getY() - xy[1] - rect[1]; | |
335 | me.oldVisibleRanges = null; | |
336 | me.hideLabels(); | |
337 | chart.suspendThicknessChanged(); | |
338 | me.lockEvents(me.getPanGesture()); | |
339 | ||
340 | return false; | |
341 | } | |
342 | }, | |
343 | ||
344 | onPanGestureMove: function(e) { | |
345 | var me = this, | |
346 | isMouse = e.pointerType === 'mouse', | |
347 | isZoomOnPan = isMouse && me.getZoomOnPan(), | |
348 | chart, rect, xy; | |
349 | ||
350 | if (me.getLocks()[me.getPanGesture()] === me) { // Limit drags to single touch. | |
351 | chart = me.getChart(); | |
352 | rect = chart.getInnerRect(); | |
353 | xy = chart.element.getXY(); | |
354 | ||
355 | if (isZoomOnPan) { | |
356 | me.transformAxesBy( | |
357 | me.getZoomableAxes(e), 0, 0, | |
358 | (e.getX() - xy[0] - rect[0]) / me.startX, | |
359 | me.startY / (e.getY() - xy[1] - rect[1]) | |
360 | ); | |
361 | } | |
362 | else { | |
363 | me.transformAxesBy( | |
364 | me.getPannableAxes(e), | |
365 | e.getX() - xy[0] - rect[0] - me.startX, | |
366 | e.getY() - xy[1] - rect[1] - me.startY, | |
367 | 1, 1); | |
368 | } | |
369 | ||
370 | me.sync(); | |
371 | ||
372 | return false; | |
373 | } | |
374 | }, | |
375 | ||
376 | onPanGestureEnd: function(e) { | |
377 | var me = this, | |
378 | pan = me.getPanGesture(), | |
379 | chart; | |
380 | ||
381 | if (me.getLocks()[pan] === me) { | |
382 | chart = me.getChart(); | |
383 | chart.resumeThicknessChanged(); | |
384 | me.showLabels(); | |
385 | me.sync(); | |
386 | me.unlockEvents(pan); | |
387 | chart.resumeAnimation(); | |
388 | ||
389 | return false; | |
390 | } | |
391 | }, | |
392 | ||
393 | onZoomGestureStart: function(e) { | |
394 | if (e.touches && e.touches.length === 2) { | |
395 | // eslint-disable-next-line vars-on-top | |
396 | var me = this, | |
397 | chart = me.getChart(), | |
398 | xy = chart.element.getXY(), | |
399 | rect = chart.getInnerRect(), | |
400 | x = xy[0] + rect[0], | |
401 | y = xy[1] + rect[1], | |
402 | newPoints = [ | |
403 | e.touches[0].point.x - x, e.touches[0].point.y - y, | |
404 | e.touches[1].point.x - x, e.touches[1].point.y - y | |
405 | ], | |
406 | xDistance = Math.max(44, Math.abs(newPoints[2] - newPoints[0])), | |
407 | yDistance = Math.max(44, Math.abs(newPoints[3] - newPoints[1])); | |
408 | ||
409 | e.claimGesture(); | |
410 | chart.suspendAnimation(); | |
411 | chart.suspendThicknessChanged(); | |
412 | me.lastZoomDistances = [xDistance, yDistance]; | |
413 | me.lastPoints = newPoints; | |
414 | me.oldVisibleRanges = null; | |
415 | me.hideLabels(); | |
416 | me.lockEvents(me.getZoomGesture()); | |
417 | ||
418 | return false; | |
419 | } | |
420 | }, | |
421 | ||
422 | onZoomGestureMove: function(e) { | |
423 | var me = this; | |
424 | ||
425 | if (me.getLocks()[me.getZoomGesture()] === me) { | |
426 | // eslint-disable-next-line vars-on-top | |
427 | var chart = me.getChart(), | |
428 | rect = chart.getInnerRect(), | |
429 | xy = chart.element.getXY(), | |
430 | x = xy[0] + rect[0], | |
431 | y = xy[1] + rect[1], | |
432 | abs = Math.abs, | |
433 | lastPoints = me.lastPoints, | |
434 | newPoints = [ | |
435 | e.touches[0].point.x - x, e.touches[0].point.y - y, | |
436 | e.touches[1].point.x - x, e.touches[1].point.y - y | |
437 | ], | |
438 | xDistance = Math.max(44, abs(newPoints[2] - newPoints[0])), | |
439 | yDistance = Math.max(44, abs(newPoints[3] - newPoints[1])), | |
440 | lastDistances = this.lastZoomDistances || [xDistance, yDistance], | |
441 | zoomX = xDistance / lastDistances[0], | |
442 | zoomY = yDistance / lastDistances[1]; | |
443 | ||
444 | me.transformAxesBy(me.getZoomableAxes(e), | |
445 | rect[2] * (zoomX - 1) / 2 + newPoints[2] - lastPoints[2] * zoomX, | |
446 | rect[3] * (zoomY - 1) / 2 + newPoints[3] - lastPoints[3] * zoomY, | |
447 | zoomX, | |
448 | zoomY); | |
449 | me.sync(); | |
450 | ||
451 | return false; | |
452 | } | |
453 | }, | |
454 | ||
455 | onZoomGestureEnd: function(e) { | |
456 | var me = this, | |
457 | zoom = me.getZoomGesture(), | |
458 | chart; | |
459 | ||
460 | if (me.getLocks()[zoom] === me) { | |
461 | chart = me.getChart(); | |
462 | chart.resumeThicknessChanged(); | |
463 | me.showLabels(); | |
464 | me.sync(); | |
465 | me.unlockEvents(zoom); | |
466 | chart.resumeAnimation(); | |
467 | ||
468 | return false; | |
469 | } | |
470 | }, | |
471 | ||
472 | hideLabels: function() { | |
473 | if (this.getHideLabelInGesture()) { | |
474 | this.eachInteractiveAxes(function(axis) { | |
475 | axis.hideLabels(); | |
476 | }); | |
477 | } | |
478 | }, | |
479 | ||
480 | showLabels: function() { | |
481 | if (this.getHideLabelInGesture()) { | |
482 | this.eachInteractiveAxes(function(axis) { | |
483 | axis.showLabels(); | |
484 | }); | |
485 | } | |
486 | }, | |
487 | ||
488 | isEventOnAxis: function(e, axis) { | |
489 | // TODO: right now this uses the current event position but really we want to only | |
490 | // use the gesture's start event. Pinch does not give that to us though. | |
491 | var rect = axis.getSurface().getRect(); | |
492 | ||
493 | return rect[0] <= e.getX() && e.getX() <= rect[0] + rect[2] && | |
494 | rect[1] <= e.getY() && e.getY() <= rect[1] + rect[3]; | |
495 | }, | |
496 | ||
497 | getPannableAxes: function(e) { | |
498 | var me = this, | |
499 | axisConfigs = me.getAxes(), | |
500 | axes = me.getChart().getAxes(), | |
501 | i, | |
502 | ln = axes.length, | |
503 | result = [], | |
504 | isEventOnAxis = false, | |
505 | config; | |
506 | ||
507 | if (e) { | |
508 | for (i = 0; i < ln; i++) { | |
509 | if (this.isEventOnAxis(e, axes[i])) { | |
510 | isEventOnAxis = true; | |
511 | break; | |
512 | } | |
513 | } | |
514 | } | |
515 | ||
516 | for (i = 0; i < ln; i++) { | |
517 | config = axisConfigs[axes[i].getPosition()]; | |
518 | ||
519 | if (config && config.allowPan !== false && | |
520 | (!isEventOnAxis || this.isEventOnAxis(e, axes[i]))) { | |
521 | result.push(axes[i]); | |
522 | } | |
523 | } | |
524 | ||
525 | return result; | |
526 | }, | |
527 | ||
528 | getZoomableAxes: function(e) { | |
529 | var me = this, | |
530 | axisConfigs = me.getAxes(), | |
531 | axes = me.getChart().getAxes(), | |
532 | result = [], | |
533 | i, | |
534 | ln = axes.length, | |
535 | axis, | |
536 | isEventOnAxis = false, | |
537 | config; | |
538 | ||
539 | if (e) { | |
540 | for (i = 0; i < ln; i++) { | |
541 | if (this.isEventOnAxis(e, axes[i])) { | |
542 | isEventOnAxis = true; | |
543 | break; | |
544 | } | |
545 | } | |
546 | } | |
547 | ||
548 | for (i = 0; i < ln; i++) { | |
549 | axis = axes[i]; | |
550 | config = axisConfigs[axis.getPosition()]; | |
551 | ||
552 | if (config && config.allowZoom !== false && | |
553 | (!isEventOnAxis || this.isEventOnAxis(e, axis))) { | |
554 | result.push(axis); | |
555 | } | |
556 | } | |
557 | ||
558 | return result; | |
559 | }, | |
560 | ||
561 | eachInteractiveAxes: function(fn) { | |
562 | var me = this, | |
563 | axisConfigs = me.getAxes(), | |
564 | axes = me.getChart().getAxes(), | |
565 | i; | |
566 | ||
567 | for (i = 0; i < axes.length; i++) { | |
568 | if (axisConfigs[axes[i].getPosition()]) { | |
569 | if (false === fn.call(this, axes[i])) { | |
570 | return; | |
571 | } | |
572 | } | |
573 | } | |
574 | }, | |
575 | ||
576 | transformAxesBy: function(axes, panX, panY, sx, sy) { | |
577 | var rect = this.getChart().getInnerRect(), | |
578 | axesCfg = this.getAxes(), | |
579 | oldVisibleRanges = this.oldVisibleRanges, | |
580 | result = false, | |
581 | axisCfg, i; | |
582 | ||
583 | if (!oldVisibleRanges) { | |
584 | this.oldVisibleRanges = oldVisibleRanges = {}; | |
585 | this.eachInteractiveAxes(function(axis) { | |
586 | oldVisibleRanges[axis.getId()] = axis.getVisibleRange(); | |
587 | }); | |
588 | } | |
589 | ||
590 | if (!rect) { | |
591 | return; | |
592 | } | |
593 | ||
594 | for (i = 0; i < axes.length; i++) { | |
595 | axisCfg = axesCfg[axes[i].getPosition()]; | |
596 | result = this.transformAxisBy( | |
597 | axes[i], oldVisibleRanges[axes[i].getId()], panX, panY, sx, sy, | |
598 | this.minZoom || axisCfg.minZoom, this.maxZoom || axisCfg.maxZoom) || result; | |
599 | } | |
600 | ||
601 | return result; | |
602 | }, | |
603 | ||
604 | transformAxisBy: function(axis, oldVisibleRange, panX, panY, sx, sy, minZoom, maxZoom) { | |
605 | var me = this, | |
606 | visibleLength = oldVisibleRange[1] - oldVisibleRange[0], | |
607 | visibleRange = axis.getVisibleRange(), | |
608 | actualMinZoom = minZoom || me.getMinZoom() || axis.config.minZoom, | |
609 | actualMaxZoom = maxZoom || me.getMaxZoom() || axis.config.maxZoom, | |
610 | rect = me.getChart().getInnerRect(), | |
611 | left, right, isSide, pan, length; | |
612 | ||
613 | if (!rect) { | |
614 | return; | |
615 | } | |
616 | ||
617 | isSide = axis.isSide(); | |
618 | length = isSide ? rect[3] : rect[2]; | |
619 | pan = isSide ? -panY : panX; | |
620 | ||
621 | visibleLength /= isSide ? sy : sx; | |
622 | ||
623 | if (visibleLength < 0) { | |
624 | visibleLength = -visibleLength; | |
625 | } | |
626 | ||
627 | if (visibleLength * actualMinZoom > 1) { | |
628 | visibleLength = 1; | |
629 | } | |
630 | ||
631 | if (visibleLength * actualMaxZoom < 1) { | |
632 | visibleLength = 1 / actualMaxZoom; | |
633 | } | |
634 | ||
635 | left = oldVisibleRange[0]; | |
636 | right = oldVisibleRange[1]; | |
637 | ||
638 | visibleRange = visibleRange[1] - visibleRange[0]; | |
639 | ||
640 | if (visibleLength === visibleRange && visibleRange === 1) { | |
641 | return; | |
642 | } | |
643 | ||
644 | axis.setVisibleRange([ | |
645 | (oldVisibleRange[0] + oldVisibleRange[1] - visibleLength) * 0.5 - | |
646 | pan / length * visibleLength, | |
647 | (oldVisibleRange[0] + oldVisibleRange[1] + visibleLength) * 0.5 - | |
648 | pan / length * visibleLength | |
649 | ]); | |
650 | ||
651 | return Math.abs(left - axis.getVisibleRange()[0]) > 1e-10 || | |
652 | Math.abs(right - axis.getVisibleRange()[1]) > 1e-10; | |
653 | }, | |
654 | ||
655 | destroy: function() { | |
656 | this.setModeToggleButton(null); | |
657 | this.callParent(); | |
658 | } | |
659 | }); |