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