API Docs for: 3.17.2
Show:

File: node-flick/js/NodeFlick.js

  1. /**
  2. * Provide a simple Flick plugin, which can be used along with the "flick" gesture event, to
  3. * animate the motion of the host node in response to a (mouse or touch) flick gesture.
  4. *
  5. * <p>The current implementation is designed to move the node, relative to the bounds of a parent node and is suitable
  6. * for scroll/carousel type implementations. Future versions will remove that constraint, to allow open ended movement within
  7. * the document.</p>
  8. *
  9. * @module node-flick
  10. */
  11.  
  12. var HOST = "host",
  13. PARENT_NODE = "parentNode",
  14. BOUNDING_BOX = "boundingBox",
  15. OFFSET_HEIGHT = "offsetHeight",
  16. OFFSET_WIDTH = "offsetWidth",
  17. SCROLL_HEIGHT = "scrollHeight",
  18. SCROLL_WIDTH = "scrollWidth",
  19. BOUNCE = "bounce",
  20. MIN_DISTANCE = "minDistance",
  21. MIN_VELOCITY = "minVelocity",
  22. BOUNCE_DISTANCE = "bounceDistance",
  23. DECELERATION = "deceleration",
  24. STEP = "step",
  25. DURATION = "duration",
  26. EASING = "easing",
  27. FLICK = "flick",
  28.  
  29. getClassName = Y.ClassNameManager.getClassName;
  30.  
  31. /**
  32. * A plugin class which can be used to animate the motion of a node, in response to a flick gesture.
  33. *
  34. * @class Flick
  35. * @namespace Plugin
  36. * @param {Object} config The initial attribute values for the plugin
  37. */
  38. function Flick(config) {
  39. Flick.superclass.constructor.apply(this, arguments);
  40. }
  41.  
  42. Flick.ATTRS = {
  43.  
  44. /**
  45. * Drag coefficent for inertial scrolling. The closer to 1 this
  46. * value is, the less friction during scrolling.
  47. *
  48. * @attribute deceleration
  49. * @default 0.98
  50. */
  51. deceleration : {
  52. value: 0.98
  53. },
  54.  
  55. /**
  56. * Drag coefficient for intertial scrolling at the upper
  57. * and lower boundaries of the scrollview. Set to 0 to
  58. * disable "rubber-banding".
  59. *
  60. * @attribute bounce
  61. * @type Number
  62. * @default 0.7
  63. */
  64. bounce : {
  65. value: 0.7
  66. },
  67.  
  68. /**
  69. * The bounce distance in pixels
  70. *
  71. * @attribute bounceDistance
  72. * @type Number
  73. * @default 150
  74. */
  75. bounceDistance : {
  76. value: 150
  77. },
  78.  
  79. /**
  80. * The minimum flick gesture velocity (px/ms) at which to trigger the flick response
  81. *
  82. * @attribute minVelocity
  83. * @type Number
  84. * @default 0
  85. */
  86. minVelocity : {
  87. value: 0
  88. },
  89.  
  90. /**
  91. * The minimum flick gesture distance (px) for which to trigger the flick response
  92. *
  93. * @attribute minVelocity
  94. * @type Number
  95. * @default 10
  96. */
  97. minDistance : {
  98. value: 10
  99. },
  100.  
  101. /**
  102. * The constraining box relative to which the flick animation and bounds should be calculated.
  103. *
  104. * @attribute boundingBox
  105. * @type Node
  106. * @default parentNode
  107. */
  108. boundingBox : {
  109. valueFn : function() {
  110. return this.get(HOST).get(PARENT_NODE);
  111. }
  112. },
  113.  
  114. /**
  115. * Time between flick animation frames.
  116. *
  117. * @attribute step
  118. * @type Number
  119. * @default 10
  120. */
  121. step : {
  122. value:10
  123. },
  124.  
  125. /**
  126. * The custom duration to apply to the flick animation. By default,
  127. * the animation duration is controlled by the deceleration factor.
  128. *
  129. * @attribute duration
  130. * @type Number
  131. * @default null
  132. */
  133. duration : {
  134. value:null
  135. },
  136.  
  137. /**
  138. * The custom transition easing to use for the flick animation. If not
  139. * provided defaults to internally to Flick.EASING, or Flick.SNAP_EASING based
  140. * on whether or not we're animating the flick or bounce step.
  141. *
  142. * @attribute easing
  143. * @type String
  144. * @default null
  145. */
  146. easing : {
  147. value:null
  148. }
  149. };
  150.  
  151. /**
  152. * The NAME of the Flick class. Used to prefix events generated
  153. * by the plugin.
  154. *
  155. * @property NAME
  156. * @static
  157. * @type String
  158. * @default "pluginFlick"
  159. */
  160. Flick.NAME = "pluginFlick";
  161.  
  162. /**
  163. * The namespace for the plugin. This will be the property on the node, which will
  164. * reference the plugin instance, when it's plugged in.
  165. *
  166. * @property NS
  167. * @static
  168. * @type String
  169. * @default "flick"
  170. */
  171. Flick.NS = "flick";
  172.  
  173. Y.extend(Flick, Y.Plugin.Base, {
  174.  
  175. /**
  176. * The initializer lifecycle implementation.
  177. *
  178. * @method initializer
  179. * @param {Object} config The user configuration for the plugin
  180. */
  181. initializer : function(config) {
  182. this._node = this.get(HOST);
  183.  
  184. this._renderClasses();
  185. this.setBounds();
  186.  
  187. this._node.on(FLICK, Y.bind(this._onFlick, this), {
  188. minDistance : this.get(MIN_DISTANCE),
  189. minVelocity : this.get(MIN_VELOCITY)
  190. });
  191. },
  192.  
  193. /**
  194. * Sets the min/max boundaries for the flick animation,
  195. * based on the boundingBox dimensions.
  196. *
  197. * @method setBounds
  198. */
  199. setBounds : function () {
  200. var box = this.get(BOUNDING_BOX),
  201. node = this._node,
  202.  
  203. boxHeight = box.get(OFFSET_HEIGHT),
  204. boxWidth = box.get(OFFSET_WIDTH),
  205.  
  206. contentHeight = node.get(SCROLL_HEIGHT),
  207. contentWidth = node.get(SCROLL_WIDTH);
  208.  
  209. if (contentHeight > boxHeight) {
  210. this._maxY = contentHeight - boxHeight;
  211. this._minY = 0;
  212. this._scrollY = true;
  213. }
  214.  
  215. if (contentWidth > boxWidth) {
  216. this._maxX = contentWidth - boxWidth;
  217. this._minX = 0;
  218. this._scrollX = true;
  219. }
  220.  
  221. this._x = this._y = 0;
  222.  
  223. node.set("top", this._y + "px");
  224. node.set("left", this._x + "px");
  225. },
  226.  
  227. /**
  228. * Adds the CSS classes, necessary to set up overflow/position properties on the
  229. * node and boundingBox.
  230. *
  231. * @method _renderClasses
  232. * @protected
  233. */
  234. _renderClasses : function() {
  235. this.get(BOUNDING_BOX).addClass(Flick.CLASS_NAMES.box);
  236. this._node.addClass(Flick.CLASS_NAMES.content);
  237. },
  238.  
  239. /**
  240. * The flick event listener. Kicks off the flick animation.
  241. *
  242. * @method _onFlick
  243. * @param e {EventFacade} The flick event facade, containing e.flick.distance, e.flick.velocity etc.
  244. * @protected
  245. */
  246. _onFlick: function(e) {
  247. this._v = e.flick.velocity;
  248. this._flick = true;
  249. this._flickAnim();
  250. },
  251.  
  252. /**
  253. * Executes a single frame in the flick animation
  254. *
  255. * @method _flickFrame
  256. * @protected
  257. */
  258. _flickAnim: function() {
  259.  
  260. var y = this._y,
  261. x = this._x,
  262.  
  263. maxY = this._maxY,
  264. minY = this._minY,
  265. maxX = this._maxX,
  266. minX = this._minX,
  267. velocity = this._v,
  268.  
  269. step = this.get(STEP),
  270. deceleration = this.get(DECELERATION),
  271. bounce = this.get(BOUNCE);
  272.  
  273. this._v = (velocity * deceleration);
  274.  
  275. this._snapToEdge = false;
  276.  
  277. if (this._scrollX) {
  278. x = x - (velocity * step);
  279. }
  280.  
  281. if (this._scrollY) {
  282. y = y - (velocity * step);
  283. }
  284.  
  285. if (Math.abs(velocity).toFixed(4) <= Flick.VELOCITY_THRESHOLD) {
  286.  
  287. this._flick = false;
  288.  
  289. this._killTimer(!(this._exceededYBoundary || this._exceededXBoundary));
  290.  
  291. if (this._scrollX) {
  292. if (x < minX) {
  293. this._snapToEdge = true;
  294. this._setX(minX);
  295. } else if (x > maxX) {
  296. this._snapToEdge = true;
  297. this._setX(maxX);
  298. }
  299. }
  300.  
  301. if (this._scrollY) {
  302. if (y < minY) {
  303. this._snapToEdge = true;
  304. this._setY(minY);
  305. } else if (y > maxY) {
  306. this._snapToEdge = true;
  307. this._setY(maxY);
  308. }
  309. }
  310.  
  311. } else {
  312.  
  313. if (this._scrollX && (x < minX || x > maxX)) {
  314. this._exceededXBoundary = true;
  315. this._v *= bounce;
  316. }
  317.  
  318. if (this._scrollY && (y < minY || y > maxY)) {
  319. this._exceededYBoundary = true;
  320. this._v *= bounce;
  321. }
  322.  
  323. if (this._scrollX) {
  324. this._setX(x);
  325. }
  326.  
  327. if (this._scrollY) {
  328. this._setY(y);
  329. }
  330.  
  331. this._flickTimer = Y.later(step, this, this._flickAnim);
  332. }
  333. },
  334.  
  335. /**
  336. * Internal utility method to set the X offset position
  337. *
  338. * @method _setX
  339. * @param {Number} val
  340. * @private
  341. */
  342. _setX : function(val) {
  343. this._move(val, null, this.get(DURATION), this.get(EASING));
  344. },
  345.  
  346. /**
  347. * Internal utility method to set the Y offset position
  348. *
  349. * @method _setY
  350. * @param {Number} val
  351. * @private
  352. */
  353. _setY : function(val) {
  354. this._move(null, val, this.get(DURATION), this.get(EASING));
  355. },
  356.  
  357. /**
  358. * Internal utility method to move the node to a given XY position,
  359. * using transitions, if specified.
  360. *
  361. * @method _move
  362. * @param {Number} x The X offset position
  363. * @param {Number} y The Y offset position
  364. * @param {Number} duration The duration to use for the transition animation
  365. * @param {String} easing The easing to use for the transition animation.
  366. *
  367. * @private
  368. */
  369. _move: function(x, y, duration, easing) {
  370.  
  371. if (x !== null) {
  372. x = this._bounce(x);
  373. } else {
  374. x = this._x;
  375. }
  376.  
  377. if (y !== null) {
  378. y = this._bounce(y);
  379. } else {
  380. y = this._y;
  381. }
  382.  
  383. duration = duration || this._snapToEdge ? Flick.SNAP_DURATION : 0;
  384. easing = easing || this._snapToEdge ? Flick.SNAP_EASING : Flick.EASING;
  385.  
  386. this._x = x;
  387. this._y = y;
  388.  
  389. this._anim(x, y, duration, easing);
  390. },
  391.  
  392. /**
  393. * Internal utility method to perform the transition step
  394. *
  395. * @method _anim
  396. * @param {Number} x The X offset position
  397. * @param {Number} y The Y offset position
  398. * @param {Number} duration The duration to use for the transition animation
  399. * @param {String} easing The easing to use for the transition animation.
  400. *
  401. * @private
  402. */
  403. _anim : function(x, y, duration, easing) {
  404. var xn = x * -1,
  405. yn = y * -1,
  406.  
  407. transition = {
  408. duration : duration / 1000,
  409. easing : easing
  410. };
  411.  
  412. Y.log("Transition: duration, easing:" + transition.duration, transition.easing, "node-flick");
  413.  
  414. if (Y.Transition.useNative) {
  415. transition.transform = 'translate('+ (xn) + 'px,' + (yn) +'px)';
  416. } else {
  417. transition.left = xn + 'px';
  418. transition.top = yn + 'px';
  419. }
  420.  
  421. this._node.transition(transition);
  422. },
  423.  
  424. /**
  425. * Internal utility method to constrain the offset value
  426. * based on the bounce criteria.
  427. *
  428. * @method _bounce
  429. * @param {Number} x The offset value to constrain.
  430. * @param {Number} max The max offset value.
  431. *
  432. * @private
  433. */
  434. _bounce : function(val, max) {
  435. var bounce = this.get(BOUNCE),
  436. dist = this.get(BOUNCE_DISTANCE),
  437. min = bounce ? -dist : 0;
  438.  
  439. max = bounce ? max + dist : max;
  440.  
  441. if(!bounce) {
  442. if(val < min) {
  443. val = min;
  444. } else if(val > max) {
  445. val = max;
  446. }
  447. }
  448. return val;
  449. },
  450.  
  451. /**
  452. * Stop the animation timer
  453. *
  454. * @method _killTimer
  455. * @private
  456. */
  457. _killTimer: function() {
  458. if(this._flickTimer) {
  459. this._flickTimer.cancel();
  460. }
  461. }
  462.  
  463. }, {
  464.  
  465. /**
  466. * The threshold used to determine when the decelerated velocity of the node
  467. * is practically 0.
  468. *
  469. * @property VELOCITY_THRESHOLD
  470. * @static
  471. * @type Number
  472. * @default 0.015
  473. */
  474. VELOCITY_THRESHOLD : 0.015,
  475.  
  476. /**
  477. * The duration to use for the bounce snap-back transition
  478. *
  479. * @property SNAP_DURATION
  480. * @static
  481. * @type Number
  482. * @default 400
  483. */
  484. SNAP_DURATION : 400,
  485.  
  486. /**
  487. * The default easing to use for the main flick movement transition
  488. *
  489. * @property EASING
  490. * @static
  491. * @type String
  492. * @default 'cubic-bezier(0, 0.1, 0, 1.0)'
  493. */
  494. EASING : 'cubic-bezier(0, 0.1, 0, 1.0)',
  495.  
  496. /**
  497. * The default easing to use for the bounce snap-back transition
  498. *
  499. * @property SNAP_EASING
  500. * @static
  501. * @type String
  502. * @default 'ease-out'
  503. */
  504. SNAP_EASING : 'ease-out',
  505.  
  506. /**
  507. * The default CSS class names used by the plugin
  508. *
  509. * @property CLASS_NAMES
  510. * @static
  511. * @type Object
  512. */
  513. CLASS_NAMES : {
  514. box: getClassName(Flick.NS),
  515. content: getClassName(Flick.NS, "content")
  516. }
  517. });
  518.  
  519. Y.Plugin.Flick = Flick;
  520.