Guest User

Untitled

a guest
Feb 22nd, 2012
229
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 9.38 KB | None | 0 0
  1. (function($) {
  2.  
  3. // Define default scroll settings
  4. var defaults = {
  5. y: 0,
  6. scrollHeight: 0,
  7. elastic: !navigator.userAgent.match(/android/i),
  8. momentum: true,
  9. elasticDamp: 0.6,
  10. elasticTime: 50,
  11. reboundTime: 400,
  12. momentumDamp: 0.9,
  13. momentumTime: 300,
  14. iPadMomentumDamp: 0.95,
  15. iPadMomentumTime: 1200,
  16. touchTags: ['select', 'input', 'textarea']
  17. };
  18.  
  19. // Define methods
  20. var methods = {
  21.  
  22. init: function(options) {
  23. return this.each(function() {
  24.  
  25. var o = $.extend(defaults, options);
  26.  
  27. // Prevent double-init, just update instead
  28. if (!!this._init) {
  29. return this.update();
  30. }
  31. this._init = true;
  32.  
  33. // Define element variables
  34. var $this = $(this),
  35. scrollY = -o.y,
  36. touchY = 0,
  37. movedY = 0,
  38. pollY = 0,
  39. height = 0,
  40. maxHeight = 0,
  41. scrollHeight = 0,
  42. scrolling = false,
  43. bouncing = false,
  44. moved = false,
  45. timeoutID,
  46. isiPad = !!navigator.platform.match(/ipad/i),
  47. hasMatrix = 'WebKitCSSMatrix' in window,
  48. has3d = hasMatrix && 'm11' in new WebKitCSSMatrix();
  49.  
  50. // Keep bottom of scroll area at the bottom on resize
  51. var update = this.update = function() {
  52. height = $this.height();
  53. if (o.scrollHeight) {
  54. scrollHeight = o.scrollHeight;
  55. } else if ($this.prop) {
  56. scrollHeight = $this.prop('scrollHeight'); // jQuery 1.6 uses .prop(), older versions use .attr()
  57. } else {
  58. scrollHeight = $this.attr('scrollHeight');
  59. }
  60. if (scrollHeight < height) {
  61. scrollHeight = height;
  62. }
  63. maxHeight = height - scrollHeight;
  64. clearTimeout(timeoutID);
  65. clampScroll(false);
  66. };
  67.  
  68. // Set up initial variables
  69. update();
  70.  
  71. // Set up transform CSS
  72. $this.css({'-webkit-transition-property': '-webkit-transform',
  73. '-webkit-transition-timing-function': 'cubic-bezier(0,0,0.2,1)',
  74. '-webkit-transition-duration': '0',
  75. '-webkit-transform': cssTranslate(scrollY)});
  76.  
  77. // Listen for screen size change event
  78. window.addEventListener('onorientationchange' in window ? 'orientationchange' : 'resize', update, false);
  79.  
  80. // Listen for touch events
  81. $this.bind('touchstart.touchScroll', touchStart);
  82. $this.bind('touchmove.touchScroll', touchMove);
  83. $this.bind('touchend.touchScroll touchcancel.touchScroll', touchEnd);
  84. $this.bind('webkitTransitionEnd.touchScroll', transitionEnd);
  85.  
  86. // Set the position of the scroll area using transform CSS
  87. var setPosition = this.setPosition = function(y) {
  88. scrollY = y;
  89. $this.css('-webkit-transform', cssTranslate(scrollY));
  90. };
  91.  
  92. // Transform using a 3D translate if available
  93. function cssTranslate(y) {
  94. return 'translate' + (has3d ? '3d(0,' : '(0,') + y + 'px' + (has3d ? ',0)' : ')');
  95. }
  96.  
  97. // Set CSS transition time
  98. function setTransitionTime(time) {
  99. time = time || '0';
  100. $this.css('-webkit-transition-duration', time + 'ms');
  101. }
  102.  
  103. // Get the actual pixel position made by transform CSS
  104. function getPosition() {
  105. if (hasMatrix) {
  106. var transform = window.getComputedStyle($this[0]).webkitTransform;
  107. if (!!transform && transform !== 'none') {
  108. var matrix = new WebKitCSSMatrix(transform);
  109. return matrix.f;
  110. }
  111. }
  112. return scrollY;
  113. }
  114.  
  115. // Expose getPosition API
  116. this.getPosition = function() {
  117. return getPosition();
  118. };
  119.  
  120. // Bounce back to the bounds after momentum scrolling
  121. function reboundScroll() {
  122. if (scrollY > 0) {
  123. scrollTo(0, o.reboundTime);
  124. } else if (scrollY < maxHeight) {
  125. scrollTo(maxHeight, o.reboundTime);
  126. }
  127. }
  128.  
  129. // Stop everything once the CSS transition in complete
  130. function transitionEnd() {
  131. if (bouncing) {
  132. bouncing = false;
  133. reboundScroll();
  134. }
  135.  
  136. clearTimeout(timeoutID);
  137. }
  138.  
  139. // Limit the scrolling to within the bounds
  140. function clampScroll(poll) {
  141. if (!hasMatrix || bouncing) {
  142. return;
  143. }
  144.  
  145. var oldY = pollY;
  146. pollY = getPosition();
  147.  
  148. if (pollY > 0) {
  149. if (o.elastic) {
  150. // Slow down outside top bound
  151. bouncing = true;
  152. scrollY = 0;
  153. momentumScroll(pollY - oldY, o.elasticDamp, 1, height, o.elasticTime);
  154. } else {
  155. // Stop outside top bound
  156. setTransitionTime(0);
  157. setPosition(0);
  158. }
  159. } else if (pollY < maxHeight) {
  160. if (o.elastic) {
  161. // Slow down outside bottom bound
  162. bouncing = true;
  163. scrollY = maxHeight;
  164. momentumScroll(pollY - oldY, o.elasticDamp, 1, height, o.elasticTime);
  165. } else {
  166. // Stop outside bottom bound
  167. setTransitionTime(0);
  168. setPosition(maxHeight);
  169. }
  170. } else if (poll) {
  171. // Poll the computed position to check if element is out of bounds
  172. timeoutID = setTimeout(clampScroll, 20, true);
  173. }
  174. }
  175.  
  176. // Animate to a position using CSS
  177. function scrollTo(destY, time) {
  178. if (destY === scrollY) {
  179. return;
  180. }
  181.  
  182. moved = true;
  183. setTransitionTime(time);
  184. setPosition(destY);
  185. }
  186.  
  187. // Perform a momentum-based scroll using CSS
  188. function momentumScroll(d, k, minDist, maxDist, t) {
  189. var ad = Math.abs(d),
  190. dy = 0;
  191.  
  192. // Calculate the total distance
  193. while (ad > 0.1) {
  194. ad *= k;
  195. dy += ad;
  196. }
  197.  
  198. // Limit to within min and max distances
  199. if (dy > maxDist) {
  200. dy = maxDist;
  201. }
  202. if (dy > minDist) {
  203. if (d < 0) {
  204. dy = -dy;
  205. }
  206.  
  207. dy += scrollY;
  208.  
  209. // If outside the bounds, don't go too far
  210. if (height > 0) {
  211. if (dy > height * 2) {
  212. var ody = dy;
  213. dy = height * 2;
  214. } else if (dy < maxHeight - height * 2) {
  215. dy = maxHeight - height * 2;
  216. }
  217. }
  218.  
  219. // Perform scroll
  220. scrollTo(Math.round(dy), t);
  221. }
  222.  
  223. clampScroll(true);
  224. }
  225.  
  226. // Get the touch points from this event
  227. function getTouches(e) {
  228. if (e.originalEvent) {
  229. if (e.originalEvent.touches && e.originalEvent.touches.length) {
  230. return e.originalEvent.touches;
  231. } else if (e.originalEvent.changedTouches && e.originalEvent.changedTouches.length) {
  232. return e.originalEvent.changedTouches;
  233. }
  234. }
  235. return e.touches;
  236. }
  237.  
  238. // Dispatches a fake mouse event from a touch event
  239. function dispatchMouseEvent(name, touch, target) {
  240. var e = document.createEvent('MouseEvent');
  241. e.initMouseEvent(name, true, true, touch.view, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
  242. target.dispatchEvent(e);
  243. }
  244.  
  245. // Find the root node of this target
  246. function getRootNode(target) {
  247. while (target.nodeType !== 1) {
  248. target = target.parentNode;
  249. }
  250. return target;
  251. }
  252.  
  253. // Perform a touch start event
  254. function touchStart(e) {
  255.  
  256. // Allow certain HTML tags to receive touch events
  257. if ($.inArray(e.target.tagName.toLowerCase(), o.touchTags) !== -1) {
  258. return;
  259. }
  260.  
  261. // Stop the default touches
  262. e.preventDefault();
  263. e.stopPropagation();
  264.  
  265. var touch = getTouches(e)[0];
  266.  
  267. // Dispatch a fake mouse down event
  268. dispatchMouseEvent('mousedown', touch, getRootNode(touch.target));
  269.  
  270. scrolling = true;
  271. moved = false;
  272. movedY = 0;
  273.  
  274. clearTimeout(timeoutID);
  275. setTransitionTime(0);
  276.  
  277. // Check scroll position
  278. if (o.momentum) {
  279. var y = getPosition();
  280. if (y !== scrollY) {
  281. setPosition(y);
  282. moved = true;
  283. }
  284. }
  285.  
  286. touchY = touch.pageY - scrollY;
  287.  
  288. }
  289.  
  290. // Perform a touch move event
  291. function touchMove(e) {
  292.  
  293. if (!scrolling) {
  294. return;
  295. }
  296.  
  297. var dy = getTouches(e)[0].pageY - touchY;
  298.  
  299. // Elastic-drag or stop when moving outside of boundaries
  300. if (dy > 0) {
  301. if (o.elastic) {
  302. dy /= 2;
  303. } else {
  304. dy = 0;
  305. }
  306. } else if (dy < maxHeight) {
  307. if (o.elastic) {
  308. dy = (dy + maxHeight) / 2;
  309. } else {
  310. dy = maxHeight;
  311. }
  312. }
  313.  
  314. movedY = dy - scrollY;
  315. moved = true;
  316. setPosition(dy);
  317. }
  318.  
  319. // Perform a touch end event
  320. function touchEnd(e) {
  321. if (!scrolling) {
  322. return;
  323. }
  324.  
  325. scrolling = false;
  326.  
  327. if (moved) {
  328. // Ease back to within boundaries
  329. if (scrollY > 0 || scrollY < maxHeight) {
  330. reboundScroll();
  331. } else if (o.momentum) {
  332. // Free scroll with momentum
  333. momentumScroll(movedY, isiPad ? o.iPadMomentumDamp : o.momentumDamp, 40, 2000, isiPad ? o.iPadMomentumTime : o.momentumTime);
  334. }
  335. } else {
  336. var touch = getTouches(e)[0],
  337. target = getRootNode(touch.target);
  338.  
  339. // Dispatch fake mouse up and click events if this touch event did not move
  340. dispatchMouseEvent('mouseup', touch, target);
  341. dispatchMouseEvent('click', touch, target);
  342. }
  343. }
  344.  
  345. });
  346. },
  347.  
  348. update: function() {
  349. return this.each(function() {
  350. this.update();
  351. });
  352. },
  353.  
  354. getPosition: function() {
  355. var a = [];
  356. this.each(function() {
  357. a.push(-this.getPosition());
  358. });
  359. return a;
  360. },
  361.  
  362. setPosition: function(y) {
  363. return this.each(function() {
  364. this.setPosition(-y);
  365. });
  366. }
  367.  
  368. };
  369.  
  370. // Public method for touchScroll
  371. $.fn.touchScroll = function(method) {
  372. if (methods[method]) {
  373. return methods[method].apply(this, Array.prototype.slice.call(arguments, 1));
  374. } else if (typeof method === 'object' || !method) {
  375. return methods.init.apply(this, arguments);
  376. } else {
  377. $.error('Method ' + method + ' does not exist on jQuery.touchScroll');
  378. }
  379. };
  380.  
  381. })(jQuery);
Advertisement
Add Comment
Please, Sign In to add comment