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