simulate-scroll.js 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701
  1. define(function(require, exports, module) {
  2. "use strict";
  3. var Util = require('./util'),
  4. Base = require('./base'),
  5. Core = require('./core'),
  6. Animate = require('./animate'),
  7. Hammer = require('./hammer'),
  8. ScrollBar = require('./components/scrollbar'),
  9. Controller = require('./components/controller');
  10. //reduced boundry pan distance
  11. var PAN_RATE = 1 - 0.618;
  12. //constant for scrolling acceleration
  13. var SCROLL_ACCELERATION = 0.0005;
  14. //constant for outside of boundry acceleration
  15. var BOUNDRY_ACCELERATION = 0.03;
  16. //transform-origin
  17. var transformOrigin = Util.prefixStyle("transformOrigin");
  18. //transform
  19. var transform = Util.prefixStyle("transform");
  20. /**
  21. * @constructor
  22. * @param {object} cfg config for scroll
  23. * @param {number} cfg.SCROLL_ACCELERATION acceleration for scroll, min value make the scrolling smoothly
  24. * @param {number} cfg.BOUNDRY_CHECK_DURATION duration for boundry bounce
  25. * @param {number} cfg.BOUNDRY_CHECK_EASING easing for boundry bounce
  26. * @param {number} cfg.BOUNDRY_CHECK_ACCELERATION acceleration for boundry bounce
  27. * @param {boolean} cfg.lockX just like overflow-x:hidden
  28. * @param {boolean} cfg.lockY just like overflow-y:hidden
  29. * @param {boolean} cfg.scrollbarX config if the scrollbar-x is visible
  30. * @param {boolean} cfg.scrollbarY config if the scrollbar-y is visible
  31. * @param {boolean} cfg.useTransition config if use css3 transition or raf for scroll animation
  32. * @param {boolean} cfg.bounce config if use has the bounce effect when scrolling outside of the boundry
  33. * @param {boolean} cfg.boundryCheck config if scrolling inside of the boundry
  34. * @param {boolean} cfg.preventDefault prevent touchstart
  35. * @param {boolean} cfg.preventTouchMove prevent touchmove
  36. * @param {string|HTMLElement} cfg.container config for scroller's container which default value is ".xs-container"
  37. * @param {string|HTMLElement} cfg.content config for scroller's content which default value is ".xs-content"
  38. * @param {object} cfg.indicatorInsets config scrollbars position {top: number, left: number, bottom: number, right: number}
  39. * @param {string} cfg.stickyElements config for sticky-positioned elements
  40. * @param {string} cfg.fixedElements config for fixed-positioned elements
  41. * @param {string} cfg.touchAction config for touchAction of the scroller
  42. * @extends XScroll
  43. * @example
  44. * var xscroll = new SimuScroll({
  45. * renderTo:"#scroll",
  46. * lockX:false,
  47. * scrollbarX:true
  48. * });
  49. * xscroll.render();
  50. */
  51. function SimuScroll(cfg) {
  52. SimuScroll.superclass.constructor.call(this, cfg);
  53. }
  54. Util.extend(SimuScroll, Core, {
  55. /**
  56. * @memberof SimuScroll
  57. * @override
  58. */
  59. init: function() {
  60. var self = this;
  61. var defaultCfg = {
  62. preventDefault: true,
  63. preventTouchMove: true
  64. };
  65. SimuScroll.superclass.init.call(this);
  66. self.userConfig = Util.mix(defaultCfg, self.userConfig);
  67. self.SCROLL_ACCELERATION = self.userConfig.SCROLL_ACCELERATION || SCROLL_ACCELERATION;
  68. self.BOUNDRY_ACCELERATION = self.userConfig.BOUNDRY_ACCELERATION || BOUNDRY_ACCELERATION;
  69. self._initContainer();
  70. self.resetSize();
  71. //set overflow behaviors
  72. self._setOverflowBehavior();
  73. self.defaltConfig = {
  74. lockY: self.userConfig.lockY,
  75. lockX: self.userConfig.lockX
  76. }
  77. return self;
  78. },
  79. destroy: function() {
  80. var self = this;
  81. SimuScroll.superclass.destroy.call(this);
  82. self.renderTo.style.overflow = "";
  83. self.renderTo.style.touchAction = "";
  84. self.container.style.transform = "";
  85. self.container.style.transformOrigin = "";
  86. self.content.style.transform = "";
  87. self.content.style.transformOrigin = "";
  88. self.off("touchstart mousedown", self._ontouchstart);
  89. self.off("touchmove", self._ontouchmove);
  90. window.removeEventListener("resize", self.resizeHandler, self);
  91. self.destroyScrollBars();
  92. },
  93. /**
  94. * set overflow behavior
  95. * @return {boolean} [description]
  96. */
  97. _setOverflowBehavior: function() {
  98. var self = this;
  99. var renderTo = self.renderTo;
  100. var computeStyle = getComputedStyle(renderTo);
  101. self.userConfig.lockX = undefined === self.userConfig.lockX ? ((computeStyle['overflow-x'] == "hidden" || self.width == self.containerWidth) ? true : false) : self.userConfig.lockX;
  102. self.userConfig.lockY = undefined === self.userConfig.lockY ? ((computeStyle['overflow-y'] == "hidden" || self.height == self.containerHeight) ? true : false) : self.userConfig.lockY;
  103. self.userConfig.scrollbarX = undefined === self.userConfig.scrollbarX ? (self.userConfig.lockX ? false : true) : self.userConfig.scrollbarX;
  104. self.userConfig.scrollbarY = undefined === self.userConfig.scrollbarY ? (self.userConfig.lockY ? false : true) : self.userConfig.scrollbarY;
  105. return self;
  106. },
  107. /**
  108. * reset lockX or lockY config to the default value
  109. */
  110. _resetLockConfig: function() {
  111. var self = this;
  112. self.userConfig.lockX = self.defaltConfig.lockX;
  113. self.userConfig.lockY = self.defaltConfig.lockY;
  114. return self;
  115. },
  116. /**
  117. * init container
  118. * @override
  119. * @return {SimuScroll}
  120. */
  121. _initContainer: function() {
  122. var self = this;
  123. SimuScroll.superclass._initContainer.call(self);
  124. if (self.__isContainerInited || !self.container || !self.content) return;
  125. self.container.style[transformOrigin] = "0 0";
  126. self.content.style[transformOrigin] = "0 0";
  127. self.translate(0, 0);
  128. self.__isContainerInited = true;
  129. return self;
  130. },
  131. /**
  132. * get scroll top value
  133. * @memberof SimuScroll
  134. * @return {number} scrollTop
  135. */
  136. getScrollTop: function() {
  137. var transY = window.getComputedStyle(this.container)[transform].match(/[-\d\.*\d*]+/g);
  138. return transY ? Math.round(transY[5]) === 0 ? 0 : -Math.round(transY[5]) : 0;
  139. },
  140. /**
  141. * get scroll left value
  142. * @memberof SimuScroll
  143. * @return {number} scrollLeft
  144. */
  145. getScrollLeft: function() {
  146. var transX = window.getComputedStyle(this.content)[transform].match(/[-\d\.*\d*]+/g);
  147. return transX ? Math.round(transX[4]) === 0 ? 0 : -Math.round(transX[4]) : 0;
  148. },
  149. /**
  150. * horizontal scroll absolute to the destination
  151. * @memberof SimuScroll
  152. * @param scrollLeft {number} scrollLeft
  153. * @param duration {number} duration for animte
  154. * @param easing {string} easing functio for animate : ease-in | ease-in-out | ease | bezier(n,n,n,n)
  155. **/
  156. scrollLeft: function(x, duration, easing, callback) {
  157. if (this.userConfig.lockX) return;
  158. var translateZ = this.userConfig.gpuAcceleration ? " translateZ(0) " : "";
  159. this.x = (undefined === x || isNaN(x) || 0 === x) ? 0 : -Math.round(x);
  160. this._animate("x", "translateX(" + this.x + "px) scale(" + this.scale + ")" + translateZ, duration, easing, callback);
  161. return this;
  162. },
  163. /**
  164. * vertical scroll absolute to the destination
  165. * @memberof SimuScroll
  166. * @param scrollTop {number} scrollTop
  167. * @param duration {number} duration for animte
  168. * @param easing {string} easing functio for animate : ease-in | ease-in-out | ease | bezier(n,n,n,n)
  169. **/
  170. scrollTop: function(y, duration, easing, callback) {
  171. if (this.userConfig.lockY) return;
  172. var translateZ = this.userConfig.gpuAcceleration ? " translateZ(0) " : "";
  173. this.y = (undefined === y || isNaN(y) || 0 === y) ? 0 : -Math.round(y);
  174. this._animate("y", "translateY(" + this.y + "px) " + translateZ, duration, easing, callback);
  175. return this;
  176. },
  177. /**
  178. * translate the scroller to a new destination includes x , y , scale
  179. * @memberof SimuScroll
  180. * @param x {number} x
  181. * @param y {number} y
  182. * @param scale {number} scale
  183. **/
  184. translate: function(x, y, scale) {
  185. var translateZ = this.userConfig.gpuAcceleration ? " translateZ(0) " : "";
  186. this.x = x || this.x || 0;
  187. this.y = y || this.y || 0;
  188. this.scale = scale || this.scale || 1;
  189. this.content.style[transform] = "translate(" + this.x + "px,0px) scale(" + this.scale + ") " + translateZ;
  190. this.container.style[transform] = "translate(0px," + this.y + "px) " + translateZ;
  191. return this;
  192. },
  193. _animate: function(type, transform, duration, easing, callback) {
  194. var self = this;
  195. var duration = duration || 0;
  196. var easing = easing || "quadratic";
  197. var el = type == "y" ? self.container : self.content;
  198. var config = {
  199. css: {
  200. transform: transform
  201. },
  202. duration: duration,
  203. easing: easing,
  204. run: function(e) {
  205. /**
  206. * @event {@link SimuScroll#"scroll"}
  207. */
  208. self.trigger("scroll", {
  209. scrollTop: self.getScrollTop(),
  210. scrollLeft: self.getScrollLeft(),
  211. type: "scroll"
  212. });
  213. },
  214. useTransition: self.userConfig.useTransition,
  215. end: function(e) {
  216. callback && callback();
  217. if ((self["_bounce" + type] === 0 || self["_bounce" + type] === undefined) && easing != "linear") {
  218. self['isScrolling' + type.toUpperCase()] = false;
  219. self['isRealScrolling' + type.toUpperCase()] = false;
  220. self.trigger("scrollend", {
  221. type: "scrollend",
  222. scrollTop: self.getScrollTop(),
  223. scrollLeft: self.getScrollLeft(),
  224. zoomType: type,
  225. duration: duration,
  226. easing: easing
  227. });
  228. }
  229. }
  230. };
  231. var timer = self.__timers[type] = self.__timers[type] || new Animate(el, config);
  232. timer.stop();
  233. timer.reset(config);
  234. timer.run();
  235. self.trigger("scrollanimate", {
  236. type: "scrollanimate",
  237. scrollTop: -self.y,
  238. scrollLeft: -self.x,
  239. duration: duration,
  240. easing: easing,
  241. zoomType: type
  242. })
  243. return this;
  244. },
  245. _ontap: function(e) {
  246. var self = this;
  247. self.boundryCheck();
  248. // self._unPreventHref(e);
  249. if (!self.isRealScrollingX && !self.isRealScrollingY) {
  250. // self._triggerClick(e);
  251. }
  252. // self._preventHref(e);
  253. self.isRealScrollingY = false;
  254. self.isRealScrollingY = false;
  255. },
  256. _bindEvt: function() {
  257. SimuScroll.superclass._bindEvt.call(this);
  258. var self = this;
  259. if (self.__isEvtBind) return;
  260. self.__isEvtBind = true;
  261. var pinch = new Hammer.Pinch();
  262. self.mc.add(pinch);
  263. self.on("touchstart mousedown", self._ontouchstart, self);
  264. self.on("touchmove", self._ontouchmove, self);
  265. self.on("tap", self._ontap, self);
  266. self.on("panstart", self._onpanstart, self);
  267. self.on("pan", self._onpan, self);
  268. self.on("panend", self._onpanend, self);
  269. self.resizeHandler = function(e) {
  270. setTimeout(function() {
  271. self.resetSize();
  272. self.boundryCheck(0);
  273. self.render();
  274. }, 100);
  275. }
  276. //window resize
  277. window.addEventListener("resize", self.resizeHandler, self);
  278. return this;
  279. },
  280. _ontouchstart: function(e) {
  281. var self = this;
  282. if (!(/(SELECT|INPUT|TEXTAREA)/i).test(e.target.tagName) && self.userConfig.preventDefault) {
  283. e.preventDefault();
  284. }
  285. self.stop();
  286. },
  287. _ontouchmove: function(e) {
  288. this.userConfig.preventTouchMove && e.preventDefault();
  289. },
  290. _onpanstart: function(e) {
  291. this.userConfig.preventTouchMove && e.preventDefault();
  292. var self = this;
  293. var scrollLeft = self.getScrollLeft();
  294. var scrollTop = self.getScrollTop();
  295. self.stop();
  296. self.translate(-scrollLeft, -scrollTop);
  297. var threshold = self.mc.get("pan").options.threshold;
  298. self.thresholdY = e.direction == "8" ? threshold : e.direction == "16" ? -threshold : 0;
  299. self.thresholdX = e.direction == "2" ? threshold : e.direction == "4" ? -threshold : 0;
  300. return self;
  301. },
  302. _onpan: function(e) {
  303. this.userConfig.preventTouchMove && e.preventDefault();
  304. var self = this;
  305. var boundry = self.boundry;
  306. var userConfig = self.userConfig;
  307. var boundryCheck = userConfig.boundryCheck;
  308. var bounce = userConfig.bounce;
  309. var scrollTop = self.__topstart || (self.__topstart = -self.getScrollTop());
  310. var scrollLeft = self.__leftstart || (self.__leftstart = -self.getScrollLeft());
  311. var y = userConfig.lockY ? Number(scrollTop) : Number(scrollTop) + (e.deltaY + self.thresholdY);
  312. var x = userConfig.lockX ? Number(scrollLeft) : Number(scrollLeft) + (e.deltaX + self.thresholdX);
  313. var containerWidth = self.containerWidth;
  314. var containerHeight = self.containerHeight;
  315. if (boundryCheck) {
  316. //over top
  317. y = y > boundry.top ? bounce ? (y - boundry.top) * PAN_RATE + boundry.top : boundry.top : y;
  318. //over bottom
  319. y = y < boundry.bottom - containerHeight ? bounce ? y + (boundry.bottom - containerHeight - y) * PAN_RATE : boundry.bottom - containerHeight : y;
  320. //over left
  321. x = x > boundry.left ? bounce ? (x - boundry.left) * PAN_RATE + boundry.left : boundry.left : x;
  322. //over right
  323. x = x < boundry.right - containerWidth ? bounce ? x + (boundry.right - containerWidth - x) * PAN_RATE : boundry.right - containerWidth : x;
  324. }
  325. //move to x,y
  326. self.translate(x, y);
  327. //pan trigger the opposite direction
  328. self.directionX = e.type == 'panleft' ? 'right' : e.type == 'panright' ? 'left' : '';
  329. self.directionY = e.type == 'panup' ? 'down' : e.type == 'pandown' ? 'up' : '';
  330. self.trigger("scroll", {
  331. scrollTop: -y,
  332. scrollLeft: -x,
  333. triggerType: "pan",
  334. type: "scroll"
  335. });
  336. return self;
  337. },
  338. _onpanend: function(e) {
  339. var self = this;
  340. var userConfig = self.userConfig;
  341. var transX = self.computeScroll("x", e.velocityX);
  342. var transY = self.computeScroll("y", e.velocityY);
  343. var scrollLeft = transX ? transX.pos : 0;
  344. var scrollTop = transY ? transY.pos : 0;
  345. var duration;
  346. if (transX && transY && transX.status == "inside" && transY.status == "inside" && transX.duration && transY.duration) {
  347. //ensure the same duration
  348. duration = Math.max(transX.duration, transY.duration);
  349. }
  350. transX && self.scrollLeft(scrollLeft, duration || transX.duration, transX.easing, function(e) {
  351. self.boundryCheckX();
  352. });
  353. transY && self.scrollTop(scrollTop, duration || transY.duration, transY.easing, function(e) {
  354. self.boundryCheckY();
  355. });
  356. //judge the direction
  357. self.directionX = e.velocityX < 0 ? "left" : "right";
  358. self.directionY = e.velocityY < 0 ? "up" : "down";
  359. //clear start
  360. self.__topstart = null;
  361. self.__leftstart = null;
  362. return self;
  363. },
  364. /**
  365. * judge the scroller is out of boundry horizontally and vertically
  366. * @memberof SimuScroll
  367. * @return {boolean} isBoundryOut
  368. **/
  369. isBoundryOut: function() {
  370. return this.isBoundryOutLeft() || this.isBoundryOutRight() || this.isBoundryOutTop() || this.isBoundryOutBottom();
  371. },
  372. /**
  373. * judge if the scroller is outsideof left
  374. * @memberof SimuScroll
  375. * @return {boolean} isBoundryOut
  376. **/
  377. isBoundryOutLeft: function() {
  378. return this.getBoundryOutLeft() > 0 ? true : false;
  379. },
  380. /**
  381. * judge if the scroller is outsideof right
  382. * @memberof SimuScroll
  383. * @return {boolean} isBoundryOut
  384. **/
  385. isBoundryOutRight: function() {
  386. return this.getBoundryOutRight() > 0 ? true : false;
  387. },
  388. /**
  389. * judge if the scroller is outsideof top
  390. * @memberof SimuScroll
  391. * @return {boolean} isBoundryOut
  392. **/
  393. isBoundryOutTop: function() {
  394. return this.getBoundryOutTop() > 0 ? true : false;
  395. },
  396. /**
  397. * judge if the scroller is outsideof bottom
  398. * @memberof SimuScroll
  399. * @return {boolean} isBoundryOut
  400. **/
  401. isBoundryOutBottom: function() {
  402. return this.getBoundryOutBottom() > 0 ? true : false;
  403. },
  404. /**
  405. * get the offset value outsideof top
  406. * @memberof SimuScroll
  407. * @return {number} offset
  408. **/
  409. getBoundryOutTop: function() {
  410. return -this.boundry.top - this.getScrollTop();
  411. },
  412. /**
  413. * get the offset value outsideof left
  414. * @memberof SimuScroll
  415. * @return {number} offset
  416. **/
  417. getBoundryOutLeft: function() {
  418. return -this.boundry.left - this.getScrollLeft();
  419. },
  420. /**
  421. * get the offset value outsideof bottom
  422. * @memberof SimuScroll
  423. * @return {number} offset
  424. **/
  425. getBoundryOutBottom: function() {
  426. return this.boundry.bottom - this.containerHeight + this.getScrollTop();
  427. },
  428. /**
  429. * get the offset value outsideof right
  430. * @memberof SimuScroll
  431. * @return {number} offset
  432. **/
  433. getBoundryOutRight: function() {
  434. return this.boundry.right - this.containerWidth + this.getScrollLeft();
  435. },
  436. /**
  437. * compute scroll transition by zoomType and velocity
  438. * @memberof SimuScroll
  439. * @param {string} zoomType zoomType of scrolling
  440. * @param {number} velocity velocity after panend
  441. * @example
  442. * var info = xscroll.computeScroll("x",2);
  443. * // return {pos:90,easing:"easing",status:"inside",duration:500}
  444. * @return {Object}
  445. **/
  446. computeScroll: function(type, v) {
  447. var self = this;
  448. var userConfig = self.userConfig;
  449. var boundry = self.boundry;
  450. var pos = type == "x" ? self.getScrollLeft() : self.getScrollTop();
  451. var boundryStart = type == "x" ? boundry.left : boundry.top;
  452. var boundryEnd = type == "x" ? boundry.right : boundry.bottom;
  453. var innerSize = type == "x" ? self.containerWidth : self.containerHeight;
  454. var maxSpeed = userConfig.maxSpeed || 2;
  455. var boundryCheck = userConfig.boundryCheck;
  456. var bounce = userConfig.bounce;
  457. var transition = {};
  458. var status = "inside";
  459. if (boundryCheck) {
  460. if (type == "x" && (self.isBoundryOutLeft() || self.isBoundryOutRight())) {
  461. self.boundryCheckX();
  462. return;
  463. } else if (type == "y" && (self.isBoundryOutTop() || self.isBoundryOutBottom())) {
  464. self.boundryCheckY();
  465. return;
  466. }
  467. }
  468. if (type == "x" && self.userConfig.lockX) return;
  469. if (type == "y" && self.userConfig.lockY) return;
  470. v = v > maxSpeed ? maxSpeed : v < -maxSpeed ? -maxSpeed : v;
  471. var a = self.SCROLL_ACCELERATION * (v / (Math.abs(v) || 1));
  472. var a2 = self.BOUNDRY_ACCELERATION;
  473. var t = isNaN(v / a) ? 0 : v / a;
  474. var s = Number(pos) + t * v / 2;
  475. //over top boundry check bounce
  476. if (s < -boundryStart && boundryCheck) {
  477. var _s = -boundryStart - pos;
  478. var _t = (Math.sqrt(-2 * a * _s + v * v) + v) / a;
  479. var v0 = v - a * _t;
  480. var _t2 = Math.abs(v0 / a2);
  481. var s2 = v0 / 2 * _t2;
  482. t = _t + _t2;
  483. s = bounce ? -boundryStart + s2 : -boundryStart;
  484. status = "outside";
  485. } else if (s > innerSize - boundryEnd && boundryCheck) {
  486. var _s = (boundryEnd - innerSize) + pos;
  487. var _t = (Math.sqrt(-2 * a * _s + v * v) - v) / a;
  488. var v0 = v - a * _t;
  489. var _t2 = Math.abs(v0 / a2);
  490. var s2 = v0 / 2 * _t2;
  491. t = _t + _t2;
  492. s = bounce ? innerSize - boundryEnd + s2 : innerSize - boundryEnd;
  493. status = "outside";
  494. }
  495. if (isNaN(s) || isNaN(t)) return;
  496. transition.pos = s;
  497. transition.duration = t;
  498. transition.easing = Math.abs(v) > 2 ? "circular" : "quadratic";
  499. transition.status = status;
  500. var Type = type.toUpperCase();
  501. self['isScrolling' + Type] = true;
  502. self['isRealScrolling' + Type] = true;
  503. return transition;
  504. },
  505. /**
  506. * bounce to the boundry horizontal
  507. * @memberof SimuScroll
  508. * @return {SimuScroll}
  509. **/
  510. boundryCheckX: function(duration, easing, callback) {
  511. var self = this;
  512. if (!self.userConfig.boundryCheck) return;
  513. if (typeof arguments[0] == "function") {
  514. callback = arguments[0];
  515. duration = self.userConfig.BOUNDRY_CHECK_DURATION;
  516. easing = self.userConfig.BOUNDRY_CHECK_EASING;
  517. } else {
  518. duration = duration === 0 ? 0 : self.userConfig.BOUNDRY_CHECK_DURATION,
  519. easing = easing || self.userConfig.BOUNDRY_CHECK_EASING;
  520. }
  521. if (!self.userConfig.bounce || self.userConfig.lockX) return;
  522. var boundry = self.boundry;
  523. if (self.isBoundryOutLeft()) {
  524. self.scrollLeft(-boundry.left, duration, easing, callback);
  525. } else if (self.isBoundryOutRight()) {
  526. self.scrollLeft(self.containerWidth - boundry.right, duration, easing, callback);
  527. }
  528. return self;
  529. },
  530. /**
  531. * bounce to the boundry vertical
  532. * @memberof SimuScroll
  533. * @return {SimuScroll}
  534. **/
  535. boundryCheckY: function(duration, easing, callback) {
  536. var self = this;
  537. if (!self.userConfig.boundryCheck) return;
  538. if (typeof arguments[0] == "function") {
  539. callback = arguments[0];
  540. duration = self.userConfig.BOUNDRY_CHECK_DURATION;
  541. easing = self.userConfig.BOUNDRY_CHECK_EASING;
  542. } else {
  543. duration = duration === 0 ? 0 : self.userConfig.BOUNDRY_CHECK_DURATION,
  544. easing = easing || self.userConfig.BOUNDRY_CHECK_EASING;
  545. }
  546. if (!self.userConfig.boundryCheck || self.userConfig.lockY) return;
  547. var boundry = self.boundry;
  548. if (self.isBoundryOutTop()) {
  549. self.scrollTop(-boundry.top, duration, easing, callback);
  550. } else if (self.isBoundryOutBottom()) {
  551. self.scrollTop(self.containerHeight - boundry.bottom, duration, easing, callback);
  552. }
  553. return self;
  554. },
  555. /**
  556. * bounce to the boundry vertical and horizontal
  557. * @memberof SimuScroll
  558. * @return {SimuScroll}
  559. **/
  560. boundryCheck: function(duration, easing, callback) {
  561. this.boundryCheckX(duration, easing, callback);
  562. this.boundryCheckY(duration, easing, callback);
  563. return this;
  564. },
  565. /**
  566. * stop scrolling immediatelly
  567. * @memberof SimuScroll
  568. * @return {SimuScroll}
  569. **/
  570. stop: function() {
  571. var self = this;
  572. self.__timers.x && self.__timers.x.stop();
  573. self.__timers.y && self.__timers.y.stop();
  574. if (self.isScrollingX || self.isScrollingY) {
  575. var scrollTop = self.getScrollTop(),
  576. scrollLeft = self.getScrollLeft();
  577. self.trigger("scrollend", {
  578. scrollTop: scrollTop,
  579. scrollLeft: scrollLeft
  580. });
  581. self.trigger("stop", {
  582. scrollTop: scrollTop,
  583. scrollLeft: scrollLeft
  584. })
  585. self.isScrollingX = false;
  586. self.isScrollingY = false;
  587. }
  588. return self;
  589. },
  590. /**
  591. * render scroll
  592. * @memberof SimuScroll
  593. * @return {SimuScroll}
  594. **/
  595. render: function() {
  596. var self = this;
  597. SimuScroll.superclass.render.call(this);
  598. //fixed for scrollbars
  599. if (getComputedStyle(self.renderTo).position == "static") {
  600. self.renderTo.style.position = "relative";
  601. }
  602. self.renderTo.style.overflow = "hidden";
  603. self.initScrollBars();
  604. self.initController();
  605. return self;
  606. },
  607. /**
  608. * init scrollbars
  609. * @memberof SimuScroll
  610. * @return {SimuScroll}
  611. */
  612. initScrollBars: function() {
  613. var self = this;
  614. if (!self.userConfig.boundryCheck) return;
  615. var indicatorInsets = self.userConfig.indicatorInsets;
  616. if (self.userConfig.scrollbarX) {
  617. self.scrollbarX = self.scrollbarX || new ScrollBar({
  618. xscroll: self,
  619. type: "x",
  620. spacing: indicatorInsets.spacing
  621. });
  622. self.scrollbarX.render();
  623. self.scrollbarX._update();
  624. self.scrollbarX.hide();
  625. }
  626. if (self.userConfig.scrollbarY) {
  627. self.scrollbarY = self.scrollbarY || new ScrollBar({
  628. xscroll: self,
  629. type: "y",
  630. spacing: indicatorInsets.spacing
  631. });
  632. self.scrollbarY.render();
  633. self.scrollbarY._update();
  634. self.scrollbarY.hide();
  635. }
  636. return self;
  637. },
  638. /**
  639. * destroy scrollbars
  640. * @memberof SimuScroll
  641. * @return {SimuScroll}
  642. */
  643. destroyScrollBars: function() {
  644. this.scrollbarX && this.scrollbarX.destroy();
  645. this.scrollbarY && this.scrollbarY.destroy();
  646. return this;
  647. },
  648. /**
  649. * init controller for multi-scrollers
  650. * @memberof SimuScroll
  651. * @return {SimuScroll}
  652. */
  653. initController: function() {
  654. var self = this;
  655. self.controller = self.controller || new Controller({
  656. xscroll: self
  657. });
  658. return self;
  659. },
  660. _unPreventHref: function(e) {
  661. var target = Util.findParentEl(e.target,'a',this.renderTo);
  662. if(!target) return;
  663. if (target.tagName.toLowerCase() == "a") {
  664. var href = target.getAttribute("data-xs-href");
  665. if (href) {
  666. target.setAttribute("href", href);
  667. }
  668. }
  669. },
  670. _preventHref: function(e) {
  671. var target = Util.findParentEl(e.target,'a',this.renderTo);
  672. if(!target) return;
  673. if (target.tagName.toLowerCase() == "a") {
  674. var href = target.getAttribute("href");
  675. href && target.setAttribute("href", "javascript:void(0)");
  676. href && target.setAttribute("data-xs-href", href);
  677. }
  678. },
  679. _triggerClick: function(e) {
  680. var target = e.target;
  681. if (!(/(SELECT|INPUT|TEXTAREA)/i).test(target.tagName)) {
  682. var ev = document.createEvent('MouseEvents');
  683. ev.initMouseEvent('click', true, true, e.view, 1,
  684. target.screenX, target.screenY, target.clientX, target.clientY,
  685. e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
  686. 0, null);
  687. target.dispatchEvent(ev);
  688. }
  689. }
  690. });
  691. if (typeof module == 'object' && module.exports) {
  692. module.exports = SimuScroll;
  693. }
  694. /** ignored by jsdoc **/
  695. else {
  696. return SimuScroll;
  697. }
  698. });