source: Dev/trunk/src/client/dojox/charting/action2d/TouchZoomAndPan.js

Last change on this file was 483, checked in by hendrikvanantwerpen, 11 years ago

Added Dojo 1.9.3 release.

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