hjg
2024-07-09 30304784e82d4bba24121328da8eb8490aec4f4f
提交 | 用户 | 时间
58d006 1 /* Flot plugin for adding the ability to pan and zoom the plot.
A 2
3 Copyright (c) 2007-2013 IOLA and Ole Laursen.
4 Licensed under the MIT license.
5
6 The default behaviour is double click and scrollwheel up/down to zoom in, drag
7 to pan. The plugin defines plot.zoom({ center }), plot.zoomOut() and
8 plot.pan( offset ) so you easily can add custom controls. It also fires
9 "plotpan" and "plotzoom" events, useful for synchronizing plots.
10
11 The plugin supports these options:
12
13     zoom: {
14         interactive: false
15         trigger: "dblclick" // or "click" for single click
16         amount: 1.5         // 2 = 200% (zoom in), 0.5 = 50% (zoom out)
17     }
18
19     pan: {
20         interactive: false
21         cursor: "move"      // CSS mouse cursor value used when dragging, e.g. "pointer"
22         frameRate: 20
23     }
24
25     xaxis, yaxis, x2axis, y2axis: {
26         zoomRange: null  // or [ number, number ] (min range, max range) or false
27         panRange: null   // or [ number, number ] (min, max) or false
28     }
29
30 "interactive" enables the built-in drag/click behaviour. If you enable
31 interactive for pan, then you'll have a basic plot that supports moving
32 around; the same for zoom.
33
34 "amount" specifies the default amount to zoom in (so 1.5 = 150%) relative to
35 the current viewport.
36
37 "cursor" is a standard CSS mouse cursor string used for visual feedback to the
38 user when dragging.
39
40 "frameRate" specifies the maximum number of times per second the plot will
41 update itself while the user is panning around on it (set to null to disable
42 intermediate pans, the plot will then not update until the mouse button is
43 released).
44
45 "zoomRange" is the interval in which zooming can happen, e.g. with zoomRange:
46 [1, 100] the zoom will never scale the axis so that the difference between min
47 and max is smaller than 1 or larger than 100. You can set either end to null
48 to ignore, e.g. [1, null]. If you set zoomRange to false, zooming on that axis
49 will be disabled.
50
51 "panRange" confines the panning to stay within a range, e.g. with panRange:
52 [-10, 20] panning stops at -10 in one end and at 20 in the other. Either can
53 be null, e.g. [-10, null]. If you set panRange to false, panning on that axis
54 will be disabled.
55
56 Example API usage:
57
58     plot = $.plot(...);
59
60     // zoom default amount in on the pixel ( 10, 20 )
61     plot.zoom({ center: { left: 10, top: 20 } });
62
63     // zoom out again
64     plot.zoomOut({ center: { left: 10, top: 20 } });
65
66     // zoom 200% in on the pixel (10, 20)
67     plot.zoom({ amount: 2, center: { left: 10, top: 20 } });
68
69     // pan 100 pixels to the left and 20 down
70     plot.pan({ left: -100, top: 20 })
71
72 Here, "center" specifies where the center of the zooming should happen. Note
73 that this is defined in pixel space, not the space of the data points (you can
74 use the p2c helpers on the axes in Flot to help you convert between these).
75
76 "amount" is the amount to zoom the viewport relative to the current range, so
77 1 is 100% (i.e. no change), 1.5 is 150% (zoom in), 0.7 is 70% (zoom out). You
78 can set the default in the options.
79
80 */
81
82 // First two dependencies, jquery.event.drag.js and
83 // jquery.mousewheel.js, we put them inline here to save people the
84 // effort of downloading them.
85
86 /*
87 jquery.event.drag.js ~ v1.5 ~ Copyright (c) 2008, Three Dub Media (http://threedubmedia.com)
88 Licensed under the MIT License ~ http://threedubmedia.googlecode.com/files/MIT-LICENSE.txt
89 */
90 (function(a){function e(h){var k,j=this,l=h.data||{};if(l.elem)j=h.dragTarget=l.elem,h.dragProxy=d.proxy||j,h.cursorOffsetX=l.pageX-l.left,h.cursorOffsetY=l.pageY-l.top,h.offsetX=h.pageX-h.cursorOffsetX,h.offsetY=h.pageY-h.cursorOffsetY;else if(d.dragging||l.which>0&&h.which!=l.which||a(h.target).is(l.not))return;switch(h.type){case"mousedown":return a.extend(l,a(j).offset(),{elem:j,target:h.target,pageX:h.pageX,pageY:h.pageY}),b.add(document,"mousemove mouseup",e,l),i(j,!1),d.dragging=null,!1;case!d.dragging&&"mousemove":if(g(h.pageX-l.pageX)+g(h.pageY-l.pageY)<l.distance)break;h.target=l.target,k=f(h,"dragstart",j),k!==!1&&(d.dragging=j,d.proxy=h.dragProxy=a(k||j)[0]);case"mousemove":if(d.dragging){if(k=f(h,"drag",j),c.drop&&(c.drop.allowed=k!==!1,c.drop.handler(h)),k!==!1)break;h.type="mouseup"}case"mouseup":b.remove(document,"mousemove mouseup",e),d.dragging&&(c.drop&&c.drop.handler(h),f(h,"dragend",j)),i(j,!0),d.dragging=d.proxy=l.elem=!1}return!0}function f(b,c,d){b.type=c;var e=a.event.dispatch.call(d,b);return e===!1?!1:e||b.result}function g(a){return Math.pow(a,2)}function h(){return d.dragging===!1}function i(a,b){a&&(a.unselectable=b?"off":"on",a.onselectstart=function(){return b},a.style&&(a.style.MozUserSelect=b?"":"none"))}a.fn.drag=function(a,b,c){return b&&this.bind("dragstart",a),c&&this.bind("dragend",c),a?this.bind("drag",b?b:a):this.trigger("drag")};var b=a.event,c=b.special,d=c.drag={not:":input",distance:0,which:1,dragging:!1,setup:function(c){c=a.extend({distance:d.distance,which:d.which,not:d.not},c||{}),c.distance=g(c.distance),b.add(this,"mousedown",e,c),this.attachEvent&&this.attachEvent("ondragstart",h)},teardown:function(){b.remove(this,"mousedown",e),this===d.dragging&&(d.dragging=d.proxy=!1),i(this,!0),this.detachEvent&&this.detachEvent("ondragstart",h)}};c.dragstart=c.dragend={setup:function(){},teardown:function(){}}})(jQuery);
91
92 /* jquery.mousewheel.min.js
93  * Copyright (c) 2011 Brandon Aaron (http://brandonaaron.net)
94  * Licensed under the MIT License (LICENSE.txt).
95  * Thanks to: http://adomas.org/javascript-mouse-wheel/ for some pointers.
96  * Thanks to: Mathias Bank(http://www.mathias-bank.de) for a scope bug fix.
97  * Thanks to: Seamus Leahy for adding deltaX and deltaY
98  *
99  * Version: 3.0.6
100  *
101  * Requires: 1.2.2+
102  */
103 (function(d){function e(a){var b=a||window.event,c=[].slice.call(arguments,1),f=0,e=0,g=0,a=d.event.fix(b);a.type="mousewheel";b.wheelDelta&&(f=b.wheelDelta/120);b.detail&&(f=-b.detail/3);g=f;void 0!==b.axis&&b.axis===b.HORIZONTAL_AXIS&&(g=0,e=-1*f);void 0!==b.wheelDeltaY&&(g=b.wheelDeltaY/120);void 0!==b.wheelDeltaX&&(e=-1*b.wheelDeltaX/120);c.unshift(a,f,e,g);return(d.event.dispatch||d.event.handle).apply(this,c)}var c=["DOMMouseScroll","mousewheel"];if(d.event.fixHooks)for(var h=c.length;h;)d.event.fixHooks[c[--h]]=d.event.mouseHooks;d.event.special.mousewheel={setup:function(){if(this.addEventListener)for(var a=c.length;a;)this.addEventListener(c[--a],e,!1);else this.onmousewheel=e},teardown:function(){if(this.removeEventListener)for(var a=c.length;a;)this.removeEventListener(c[--a],e,!1);else this.onmousewheel=null}};d.fn.extend({mousewheel:function(a){return a?this.bind("mousewheel",a):this.trigger("mousewheel")},unmousewheel:function(a){return this.unbind("mousewheel",a)}})})(jQuery);
104
105
106
107
108 (function ($) {
109     var options = {
110         xaxis: {
111             zoomRange: null, // or [number, number] (min range, max range)
112             panRange: null // or [number, number] (min, max)
113         },
114         zoom: {
115             interactive: false,
116             trigger: "dblclick", // or "click" for single click
117             amount: 1.5 // how much to zoom relative to current position, 2 = 200% (zoom in), 0.5 = 50% (zoom out)
118         },
119         pan: {
120             interactive: false,
121             cursor: "move",
122             frameRate: 20
123         }
124     };
125
126     function init(plot) {
127         function onZoomClick(e, zoomOut) {
128             var c = plot.offset();
129             c.left = e.pageX - c.left;
130             c.top = e.pageY - c.top;
131             if (zoomOut)
132                 plot.zoomOut({ center: c });
133             else
134                 plot.zoom({ center: c });
135         }
136
137         function onMouseWheel(e, delta) {
138             e.preventDefault();
139             onZoomClick(e, delta < 0);
140             return false;
141         }
142         
143         var prevCursor = 'default', prevPageX = 0, prevPageY = 0,
144             panTimeout = null;
145
146         function onDragStart(e) {
147             if (e.which != 1)  // only accept left-click
148                 return false;
149             var c = plot.getPlaceholder().css('cursor');
150             if (c)
151                 prevCursor = c;
152             plot.getPlaceholder().css('cursor', plot.getOptions().pan.cursor);
153             prevPageX = e.pageX;
154             prevPageY = e.pageY;
155         }
156         
157         function onDrag(e) {
158             var frameRate = plot.getOptions().pan.frameRate;
159             if (panTimeout || !frameRate)
160                 return;
161
162             panTimeout = setTimeout(function () {
163                 plot.pan({ left: prevPageX - e.pageX,
164                            top: prevPageY - e.pageY });
165                 prevPageX = e.pageX;
166                 prevPageY = e.pageY;
167                                                     
168                 panTimeout = null;
169             }, 1 / frameRate * 1000);
170         }
171
172         function onDragEnd(e) {
173             if (panTimeout) {
174                 clearTimeout(panTimeout);
175                 panTimeout = null;
176             }
177                     
178             plot.getPlaceholder().css('cursor', prevCursor);
179             plot.pan({ left: prevPageX - e.pageX,
180                        top: prevPageY - e.pageY });
181         }
182         
183         function bindEvents(plot, eventHolder) {
184             var o = plot.getOptions();
185             if (o.zoom.interactive) {
186                 eventHolder[o.zoom.trigger](onZoomClick);
187                 eventHolder.mousewheel(onMouseWheel);
188             }
189
190             if (o.pan.interactive) {
191                 eventHolder.bind("dragstart", { distance: 10 }, onDragStart);
192                 eventHolder.bind("drag", onDrag);
193                 eventHolder.bind("dragend", onDragEnd);
194             }
195         }
196
197         plot.zoomOut = function (args) {
198             if (!args)
199                 args = {};
200             
201             if (!args.amount)
202                 args.amount = plot.getOptions().zoom.amount;
203
204             args.amount = 1 / args.amount;
205             plot.zoom(args);
206         };
207         
208         plot.zoom = function (args) {
209             if (!args)
210                 args = {};
211             
212             var c = args.center,
213                 amount = args.amount || plot.getOptions().zoom.amount,
214                 w = plot.width(), h = plot.height();
215
216             if (!c)
217                 c = { left: w / 2, top: h / 2 };
218                 
219             var xf = c.left / w,
220                 yf = c.top / h,
221                 minmax = {
222                     x: {
223                         min: c.left - xf * w / amount,
224                         max: c.left + (1 - xf) * w / amount
225                     },
226                     y: {
227                         min: c.top - yf * h / amount,
228                         max: c.top + (1 - yf) * h / amount
229                     }
230                 };
231
232             $.each(plot.getAxes(), function(_, axis) {
233                 var opts = axis.options,
234                     min = minmax[axis.direction].min,
235                     max = minmax[axis.direction].max,
236                     zr = opts.zoomRange,
237                     pr = opts.panRange;
238
239                 if (zr === false) // no zooming on this axis
240                     return;
241                     
242                 min = axis.c2p(min);
243                 max = axis.c2p(max);
244                 if (min > max) {
245                     // make sure min < max
246                     var tmp = min;
247                     min = max;
248                     max = tmp;
249                 }
250
251                 //Check that we are in panRange
252                 if (pr) {
253                     if (pr[0] != null && min < pr[0]) {
254                         min = pr[0];
255                     }
256                     if (pr[1] != null && max > pr[1]) {
257                         max = pr[1];
258                     }
259                 }
260
261                 var range = max - min;
262                 if (zr &&
263                     ((zr[0] != null && range < zr[0]) ||
264                      (zr[1] != null && range > zr[1])))
265                     return;
266             
267                 opts.min = min;
268                 opts.max = max;
269             });
270             
271             plot.setupGrid();
272             plot.draw();
273             
274             if (!args.preventEvent)
275                 plot.getPlaceholder().trigger("plotzoom", [ plot, args ]);
276         };
277
278         plot.pan = function (args) {
279             var delta = {
280                 x: +args.left,
281                 y: +args.top
282             };
283
284             if (isNaN(delta.x))
285                 delta.x = 0;
286             if (isNaN(delta.y))
287                 delta.y = 0;
288
289             $.each(plot.getAxes(), function (_, axis) {
290                 var opts = axis.options,
291                     min, max, d = delta[axis.direction];
292
293                 min = axis.c2p(axis.p2c(axis.min) + d),
294                 max = axis.c2p(axis.p2c(axis.max) + d);
295
296                 var pr = opts.panRange;
297                 if (pr === false) // no panning on this axis
298                     return;
299                 
300                 if (pr) {
301                     // check whether we hit the wall
302                     if (pr[0] != null && pr[0] > min) {
303                         d = pr[0] - min;
304                         min += d;
305                         max += d;
306                     }
307                     
308                     if (pr[1] != null && pr[1] < max) {
309                         d = pr[1] - max;
310                         min += d;
311                         max += d;
312                     }
313                 }
314                 
315                 opts.min = min;
316                 opts.max = max;
317             });
318             
319             plot.setupGrid();
320             plot.draw();
321             
322             if (!args.preventEvent)
323                 plot.getPlaceholder().trigger("plotpan", [ plot, args ]);
324         };
325
326         function shutdown(plot, eventHolder) {
327             eventHolder.unbind(plot.getOptions().zoom.trigger, onZoomClick);
328             eventHolder.unbind("mousewheel", onMouseWheel);
329             eventHolder.unbind("dragstart", onDragStart);
330             eventHolder.unbind("drag", onDrag);
331             eventHolder.unbind("dragend", onDragEnd);
332             if (panTimeout)
333                 clearTimeout(panTimeout);
334         }
335         
336         plot.hooks.bindEvents.push(bindEvents);
337         plot.hooks.shutdown.push(shutdown);
338     }
339     
340     $.plot.plugins.push({
341         init: init,
342         options: options,
343         name: 'navigate',
344         version: '1.3'
345     });
346 })(jQuery);