5 * new Roo.ux.TouchScroll( { el : id| dom element | roo element } )
11 Roo.ux.TouchScroll = function (cfg) {
13 this.el = Roo.get(this.el);
17 Roo.apply(Roo.ux.TouchScroll.prototype, {
19 // Define default scroll settings
23 elastic: !navigator.userAgent.match(/android/i),
30 iPadMomentumDamp: 0.95,
31 iPadMomentumTime: 1200,
32 touchTags: ['select', 'input', 'textarea'],
39 // Prevent double-init, just update instead
46 // Define element variables
48 scrollY = -this.el.getY(),
59 isiPad = navigator.platform.match(/ipad/i) ? true : false,
60 hasMatrix = 'WebKitCSSMatrix' in window,
61 has3d = hasMatrix && 'm11' in new WebKitCSSMatrix();
63 // Keep bottom of scroll area at the bottom on resize
64 var update = this.update = function() {
65 height = el.getHeight();
66 if (el.dom.scrollHeight) {
67 scrollHeight = el.dom.scrollHeight;
69 scrollHeight = el.dom.getAttribute('scrollHeight');
71 if (scrollHeight < height) {
72 scrollHeight = height;
74 maxHeight = height - scrollHeight;
75 clearTimeout(timeoutID);
79 // Set up initial variables
82 // Set up transform CSS
84 '-webkit-transition-property': '-webkit-transform',
85 '-webkit-transition-timing-function': 'cubic-bezier(0,0,0.2,1)',
86 '-webkit-transition-duration': '0',
87 '-webkit-transform': cssTranslate(scrollY)
90 // Listen for screen size change event
91 window.addEventListener('onorientationchange' in window ?
92 'orientationchange' : 'resize', update, false);
94 // Listen for touch events
95 this.el.on('touchstart.touchScroll', touchStart);
96 this.el.on('touchmove.touchScroll', touchMove);
97 this.el.on('touchend.touchScroll touchcancel.touchScroll', touchEnd);
98 this.el.on('webkitTransitionEnd.touchScroll', transitionEnd);
100 // Set the position of the scroll area using transform CSS
101 var setPosition = this.setPosition = function(y) {
103 this.el.setStyle('-webkit-transform', cssTranslate(scrollY));
106 // Transform using a 3D translate if available
107 function cssTranslate(y) {
108 return 'translate' + (has3d ? '3d(0,' : '(0,') + y + 'px' + (has3d ? ',0)' : ')');
111 // Set CSS transition time
112 function setTransitionTime(time) {
114 this.el.setStyle('-webkit-transition-duration', time + 'ms');
117 // Get the actual pixel position made by transform CSS
118 function getPosition() {
120 var transform = window.getComputedStyle(this.el.dom).webkitTransform;
121 if (!!transform && transform !== 'none') {
122 var matrix = new WebKitCSSMatrix(transform);
129 // Expose getPosition API
130 this.getPosition = function() {
131 return getPosition();
134 // Bounce back to the bounds after momentum scrolling
135 function reboundScroll() {
137 scrollTo(0, o.reboundTime);
138 } else if (scrollY < maxHeight) {
139 scrollTo(maxHeight, o.reboundTime);
143 // Stop everything once the CSS transition in complete
144 function transitionEnd() {
150 clearTimeout(timeoutID);
153 // Limit the scrolling to within the bounds
154 function clampScroll(poll) {
155 if (!hasMatrix || bouncing) {
160 pollY = getPosition();
164 // Slow down outside top bound
167 momentumScroll(pollY - oldY, o.elasticDamp, 1, height, o.elasticTime);
169 // Stop outside top bound
170 setTransitionTime(0);
173 } else if (pollY < maxHeight) {
175 // Slow down outside bottom bound
178 momentumScroll(pollY - oldY, o.elasticDamp, 1, height, o.elasticTime);
180 // Stop outside bottom bound
181 setTransitionTime(0);
182 setPosition(maxHeight);
185 // Poll the computed position to check if element is out of bounds
186 timeoutID = setTimeout(clampScroll, 20, true);
190 // Animate to a position using CSS
191 function scrollTo(destY, time) {
192 if (destY === scrollY) {
197 setTransitionTime(time);
201 // Perform a momentum-based scroll using CSS
202 function momentumScroll(d, k, minDist, maxDist, t) {
203 var ad = Math.abs(d),
206 // Calculate the total distance
212 // Limit to within min and max distances
223 // If outside the bounds, don't go too far
225 if (dy > height * 2) {
228 } else if (dy < maxHeight - height * 2) {
229 dy = maxHeight - height * 2;
234 scrollTo(Math.round(dy), t);
240 // Get the touch points from this event
241 function getTouches(e) {
242 if (e.originalEvent) {
243 if (e.originalEvent.touches && e.originalEvent.touches.length) {
244 return e.originalEvent.touches;
245 } else if (e.originalEvent.changedTouches && e.originalEvent.changedTouches.length) {
246 return e.originalEvent.changedTouches;
252 // Dispatches a fake mouse event from a touch event
253 function dispatchMouseEvent(name, touch, target) {
254 var e = document.createEvent('MouseEvent');
255 e.initMouseEvent(name, true, true, touch.view, 1,
256 touch.screenX, touch.screenY,
257 touch.clientX, touch.clientY,
258 false, false, false, false, 0, null);
259 target.dispatchEvent(e);
262 // Find the root node of this target
263 function getRootNode(target) {
264 while (target.nodeType !== 1) {
265 target = target.parentNode;
270 // Perform a touch start event
271 function touchStart(e) {
272 // Allow certain HTML tags to receive touch events
273 if (o.touchTags.indexOf(e.target.tagName.toLowerCase()) !== -1) {
277 // Stop the default touches
281 var touch = getTouches(e)[0];
283 // Dispatch a fake mouse down event
284 dispatchMouseEvent('mousedown', touch, getRootNode(touch.target));
290 clearTimeout(timeoutID);
291 setTransitionTime(0);
293 // Check scroll position
295 var y = getPosition();
302 touchY = touch.pageY - scrollY;
305 // Perform a touch move event
306 function touchMove(e) {
311 var dy = getTouches(e)[0].pageY - touchY;
313 // Elastic-drag or stop when moving outside of boundaries
320 } else if (dy < maxHeight) {
322 dy = (dy + maxHeight) / 2;
328 movedY = dy - scrollY;
333 // Perform a touch end event
334 function touchEnd(e) {
342 // Ease back to within boundaries
343 if (scrollY > 0 || scrollY < maxHeight) {
345 } else if (o.momentum) {
346 // Free scroll with momentum
347 momentumScroll(movedY, isiPad ? o.iPadMomentumDamp : o.momentumDamp, 40, 2000, isiPad ? o.iPadMomentumTime : o.momentumTime);
350 var touch = getTouches(e)[0],
351 target = getRootNode(touch.target);
353 // Dispatch fake mouse up and click events if this touch event did not move
354 dispatchMouseEvent('mouseup', touch, target);
355 dispatchMouseEvent('click', touch, target);
363 return this.each(function() {
368 getPosition: function() {
370 this.each(function() {
371 a.push(-this.getPosition());
376 setPosition: function(y) {
377 return this.each(function() {
378 this.setPosition(-y);
384 // Public method for touchScroll
385 $.fn.touchScroll = function(method) {
386 if (methods[method]) {
387 return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
388 } else if (typeof method === 'object' || !method) {
389 return methods.init.apply(this, arguments);
391 $.error('Method ' + method + ' does not exist on jQuery.touchScroll');