source: Dev/branches/rest-dojo-ui/client/dojox/charting/action2d/TouchZoomAndPan.js @ 256

Last change on this file since 256 was 256, checked in by hendrikvanantwerpen, 13 years ago

Reworked project structure based on REST interaction and Dojo library. As
soon as this is stable, the old jQueryUI branch can be removed (it's
kept for reference).

File size: 9.3 KB
Line 
1define(["dojo/_base/lang", "dojo/_base/declare", "dojo/_base/event", "dojo/_base/sniff",
2        "./ChartAction", "../Element", "dojox/gesture/tap", "../plot2d/common"],
3        function(lang, declare, eventUtil, has, ChartAction, Element, tap, common){
4        var GlassView = declare("dojox.charting.action2d._GlassView", [Element], {
5                //      summary: Private internal class used by TouchZoomAndPan actions.
6                //      tags:
7                //              private
8                constructor: function(chart){
9                },
10                render: function(){
11                        if(!this.isDirty()){
12                                return;
13                        }
14                        this.cleanGroup();
15                        this.group.createRect({width: this.chart.dim.width, height: this.chart.dim.height}).setFill("rgba(0,0,0,0)");
16                },
17                cleanGroup: function(creator){
18                        //      summary:
19                        //              Clean any elements (HTML or GFX-based) out of our group, and create a new one.
20                        //      creator: dojox.gfx.Surface?
21                        //              An optional surface to work with.
22                        //      returns: dojox.charting.Element
23                        //              A reference to this object for functional chaining.
24                        this.inherited(arguments);
25                        return this;    //      dojox.charting.Element
26                },
27                clear: function(){
28                        //      summary:
29                        //              Clear out any parameters set on this plot.
30                        //      returns: dojox.charting.action2d._IndicatorElement
31                        //              The reference to this plot for functional chaining.
32                        this.dirty = true;
33                        // glass view needs to be above
34                        if(this.chart.stack[0] != this){
35                                this.chart.movePlotToFront(this.name);
36                        }
37                        return this;    //      dojox.charting.plot2d._IndicatorElement
38                },
39                getSeriesStats: function(){
40                        //      summary:
41                        //              Returns default stats (irrelevant for this type of plot).
42                        //      returns: Object
43                        //              {hmin, hmax, vmin, vmax} min/max in both directions.
44                        return lang.delegate(common.defaultStats);
45                },
46                initializeScalers: function(){
47                        //      summary:
48                        //              Does nothing (irrelevant for this type of plot).
49                        return this;
50                },
51                isDirty: function(){
52                        //      summary:
53                        //              Return whether or not this plot needs to be redrawn.
54                        //      returns: Boolean
55                        //              If this plot needs to be rendered, this will return true.
56                        return this.dirty;
57                }
58        });
59       
60        /*=====
61       
62        declare("dojox.charting.action2d.__TouchZoomAndPanCtorArgs", null, {
63                //      summary:
64                //              Additional arguments for mouse zoom and pan actions.
65               
66                //      axis: String?
67                //              Target axis name for this action.  Default is "x".
68                axis: "x",
69                //      scaleFactor: Number?
70                //              The scale factor applied on double tap.  Default is 1.2.
71                scaleFactor: 1.2,
72                //      maxScale: Number?
73                //              The max scale factor accepted by this action.  Default is 100.
74                maxScale: 100,
75                //      enableScroll: Boolean?
76                //              Whether touch drag gesture should scroll the chart.  Default is true.
77                enableScroll: true,
78                //      enableZoom: Boolean?
79                //              Whether touch pinch and spread gesture should zoom out or in the chart.  Default is true.
80                enableZoom: true,
81        });
82        var ChartAction = dojox.charting.action2d.ChartAction;
83        =====*/
84       
85        return declare("dojox.charting.action2d.TouchZoomAndPan", ChartAction, {
86                //      summary:
87                //              Create a touch zoom and pan action.
88                //              You can zoom out or in the data window with pinch and spread gestures. You can scroll using drag gesture.
89                //              Finally this is possible to navigate between a fit window and a zoom one using double tap gesture.
90       
91                // the data description block for the widget parser
92                defaultParams: {
93                        axis: "x",
94                        scaleFactor: 1.2,       
95                        maxScale: 100,
96                        enableScroll: true,
97                        enableZoom: true
98                },
99                optionalParams: {},     // no optional parameters
100       
101                constructor: function(chart, plot, kwArgs){
102                        //      summary:
103                        //              Create a new touch zoom and pan action and connect it.
104                        //      chart: dojox.charting.Chart
105                        //              The chart this action applies to.
106                        //      kwArgs: dojox.charting.action2d.__TouchZoomAndPanCtorArgs?
107                        //              Optional arguments for the action.
108                        this._listeners = [{eventName: "ontouchstart", methodName: "onTouchStart"},
109                                           {eventName: "ontouchmove", methodName: "onTouchMove"},
110                                           {eventName: "ontouchend", methodName: "onTouchEnd"},
111                                                           {eventName: tap.doubletap, methodName: "onDoubleTap"}];
112                        if(!kwArgs){ kwArgs = {}; }
113                        this.axis = kwArgs.axis ? kwArgs.axis : "x";
114                        this.scaleFactor = kwArgs.scaleFactor ? kwArgs.scaleFactor : 1.2;
115                        this.maxScale = kwArgs.maxScale ? kwArgs.maxScale : 100;
116                        this.enableScroll = kwArgs.enableScroll != undefined ? kwArgs.enableScroll : true;
117                        this.enableZoom = kwArgs.enableScroll != undefined ? kwArgs.enableZoom : true;
118                        this._uName = "touchZoomPan"+this.axis;
119                        this.connect();
120                },
121               
122                connect: function(){
123                        //      summary:
124                        //              Connect this action to the chart. On Safari this adds a new glass view plot
125                        //              to the chart that's why Chart.render() must be called after connect.
126                        this.inherited(arguments);
127                        // this is needed to workaround issue on Safari + SVG, because a touch start action
128                        // started above a item that is removed during the touch action will stop
129                        // dispatching touch events!
130                        if(has("safari") && this.chart.surface.declaredClass.indexOf("svg")!=-1){
131                                this.chart.addPlot(this._uName, {type: GlassView});
132                        }
133                },
134               
135                disconnect: function(){
136                        //      summary:
137                        //              Disconnect this action from the chart.
138                        if(has("safari") && this.chart.surface.declaredClass.indexOf("svg")!=-1){
139                                this.chart.removePlot(this._uName);
140                        }
141                        this.inherited(arguments);
142                },
143       
144                onTouchStart: function(event){
145                        //      summary:
146                        //              Called when touch is started on the chart.
147                        // we always want to be above regular plots and not clipped
148                        var chart = this.chart, axis = chart.getAxis(this.axis);
149                        var length = event.touches.length;
150                        this._startPageCoord = {x: event.touches[0].pageX, y: event.touches[0].pageY};
151                        if((this.enableZoom || this.enableScroll) && chart._delayedRenderHandle){
152                                // we have pending rendering from a scroll, let's sync
153                                clearTimeout(chart._delayedRenderHandle);
154                                chart._delayedRenderHandle = null;
155                                chart.render();
156                        }
157                        if(this.enableZoom && length >= 2){
158                                this._endPageCoord =  {x: event.touches[1].pageX, y: event.touches[1].pageY};
159                                var middlePageCoord = {x: (this._startPageCoord.x + this._endPageCoord.x) / 2,
160                                                                                y: (this._startPageCoord.y + this._endPageCoord.y) / 2};
161                                var scaler = axis.getScaler();
162                                this._initScale = axis.getWindowScale();
163                                var t = this._initData =  this.plot.toData();
164                                this._middleCoord = t(middlePageCoord)[this.axis];
165                                this._startCoord = scaler.bounds.from;
166                                this._endCoord = scaler.bounds.to;
167                        }else if(this.enableScroll){
168                                this._startScroll(axis);
169                                // needed for Android, otherwise will get a touch cancel while swiping
170                                eventUtil.stop(event);
171                        }
172                },
173       
174                onTouchMove: function(event){
175                        //      summary:
176                        //              Called when touch is moved on the chart.
177                        var chart = this.chart, axis = chart.getAxis(this.axis);
178                        var length = event.touches.length;
179                        var pAttr = axis.vertical?"pageY":"pageX",
180                                        attr = axis.vertical?"y":"x";
181                        if(this.enableZoom && length >= 2){
182                                var newMiddlePageCoord = {x: (event.touches[1].pageX + event.touches[0].pageX) / 2,
183                                                                                        y: (event.touches[1].pageY + event.touches[0].pageY) / 2};             
184                                var scale = (this._endPageCoord[attr] - this._startPageCoord[attr]) /
185                                        (event.touches[1][pAttr] - event.touches[0][pAttr]);
186       
187                                if(this._initScale / scale > this.maxScale){
188                                        return;
189                                }
190       
191                                var newMiddleCoord = this._initData(newMiddlePageCoord)[this.axis];
192       
193                                var newStart = scale * (this._startCoord - newMiddleCoord)  + this._middleCoord,
194                                newEnd = scale * (this._endCoord - newMiddleCoord) + this._middleCoord;
195                                chart.zoomIn(this.axis, [newStart, newEnd]);
196                                // avoid browser pan
197                                eventUtil.stop(event);
198                        }else if(this.enableScroll){
199                                var delta = axis.vertical?(this._startPageCoord[attr] - event.touches[0][pAttr]):
200                                        (event.touches[0][pAttr] - this._startPageCoord[attr]);
201                                chart.setAxisWindow(this.axis, this._lastScale, this._initOffset - delta / this._lastFactor / this._lastScale);
202                                chart.delayedRender();
203                                // avoid browser pan
204                                eventUtil.stop(event);
205                        }               
206                },
207       
208                onTouchEnd: function(event){
209                        //      summary:
210                        //              Called when touch is ended on the chart.
211                        var chart = this.chart, axis = chart.getAxis(this.axis);
212                        if(event.touches.length == 1 && this.enableScroll){
213                                // still one touch available, let's start back from here for
214                                // potential pan
215                                this._startPageCoord = {x: event.touches[0].pageX, y: event.touches[0].pageY};
216                                this._startScroll(axis);
217                        }
218                },
219               
220                _startScroll: function(axis){
221                        var bounds = axis.getScaler().bounds;
222                        this._initOffset = axis.getWindowOffset();
223                        // we keep it because of delay rendering we might now always have access to the
224                        // information to compute it
225                        this._lastScale = axis.getWindowScale();
226                        this._lastFactor = bounds.span / (bounds.upper - bounds.lower);
227                },
228       
229                onDoubleTap: function(event){
230                        //      summary:
231                        //              Called when double tap is performed on the chart.
232                        var chart = this.chart, axis = chart.getAxis(this.axis);
233                        var scale = 1 / this.scaleFactor;
234                        // are we fit?
235                        if(axis.getWindowScale()==1){
236                                // fit => zoom
237                                var scaler = axis.getScaler(), start = scaler.bounds.from, end = scaler.bounds.to,
238                                oldMiddle = (start + end) / 2, newMiddle = this.plot.toData(this._startPageCoord)[this.axis],
239                                newStart = scale * (start - oldMiddle) + newMiddle, newEnd = scale * (end - oldMiddle) + newMiddle;
240                                chart.zoomIn(this.axis, [newStart, newEnd]);
241                        }else{
242                                // non fit => fit
243                                chart.setAxisWindow(this.axis, 1, 0);
244                                chart.render();
245                        }
246                        eventUtil.stop(event);
247                }
248        });
249});             
Note: See TracBrowser for help on using the repository browser.