2 YUI 3.13.0 (build 508226d)
3 Copyright 2013 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
8 YUI.add('node-flick', function (Y, NAME) {
11 * Provide a simple Flick plugin, which can be used along with the "flick" gesture event, to
12 * animate the motion of the host node in response to a (mouse or touch) flick gesture.
14 * <p>The current implementation is designed to move the node, relative to the bounds of a parent node and is suitable
15 * for scroll/carousel type implementations. Future versions will remove that constraint, to allow open ended movement within
22 PARENT_NODE = "parentNode",
23 BOUNDING_BOX = "boundingBox",
24 OFFSET_HEIGHT = "offsetHeight",
25 OFFSET_WIDTH = "offsetWidth",
26 SCROLL_HEIGHT = "scrollHeight",
27 SCROLL_WIDTH = "scrollWidth",
29 MIN_DISTANCE = "minDistance",
30 MIN_VELOCITY = "minVelocity",
31 BOUNCE_DISTANCE = "bounceDistance",
32 DECELERATION = "deceleration",
34 DURATION = "duration",
38 getClassName = Y.ClassNameManager.getClassName;
41 * A plugin class which can be used to animate the motion of a node, in response to a flick gesture.
45 * @param {Object} config The initial attribute values for the plugin
47 function Flick(config) {
48 Flick.superclass.constructor.apply(this, arguments);
54 * Drag coefficent for inertial scrolling. The closer to 1 this
55 * value is, the less friction during scrolling.
57 * @attribute deceleration
65 * Drag coefficient for intertial scrolling at the upper
66 * and lower boundaries of the scrollview. Set to 0 to
67 * disable "rubber-banding".
78 * The bounce distance in pixels
80 * @attribute bounceDistance
89 * The minimum flick gesture velocity (px/ms) at which to trigger the flick response
91 * @attribute minVelocity
100 * The minimum flick gesture distance (px) for which to trigger the flick response
102 * @attribute minVelocity
111 * The constraining box relative to which the flick animation and bounds should be calculated.
113 * @attribute boundingBox
115 * @default parentNode
118 valueFn : function() {
119 return this.get(HOST).get(PARENT_NODE);
124 * Time between flick animation frames.
135 * The custom duration to apply to the flick animation. By default,
136 * the animation duration is controlled by the deceleration factor.
138 * @attribute duration
147 * The custom transition easing to use for the flick animation. If not
148 * provided defaults to internally to Flick.EASING, or Flick.SNAP_EASING based
149 * on whether or not we're animating the flick or bounce step.
161 * The NAME of the Flick class. Used to prefix events generated
167 * @default "pluginFlick"
169 Flick.NAME = "pluginFlick";
172 * The namespace for the plugin. This will be the property on the node, which will
173 * reference the plugin instance, when it's plugged in.
182 Y.extend(Flick, Y.Plugin.Base, {
185 * The initializer lifecycle implementation.
187 * @method initializer
188 * @param {Object} config The user configuration for the plugin
190 initializer : function(config) {
191 this._node = this.get(HOST);
193 this._renderClasses();
196 this._node.on(FLICK, Y.bind(this._onFlick, this), {
197 minDistance : this.get(MIN_DISTANCE),
198 minVelocity : this.get(MIN_VELOCITY)
203 * Sets the min/max boundaries for the flick animation,
204 * based on the boundingBox dimensions.
208 setBounds : function () {
209 var box = this.get(BOUNDING_BOX),
212 boxHeight = box.get(OFFSET_HEIGHT),
213 boxWidth = box.get(OFFSET_WIDTH),
215 contentHeight = node.get(SCROLL_HEIGHT),
216 contentWidth = node.get(SCROLL_WIDTH);
218 if (contentHeight > boxHeight) {
219 this._maxY = contentHeight - boxHeight;
221 this._scrollY = true;
224 if (contentWidth > boxWidth) {
225 this._maxX = contentWidth - boxWidth;
227 this._scrollX = true;
230 this._x = this._y = 0;
232 node.set("top", this._y + "px");
233 node.set("left", this._x + "px");
237 * Adds the CSS classes, necessary to set up overflow/position properties on the
238 * node and boundingBox.
240 * @method _renderClasses
243 _renderClasses : function() {
244 this.get(BOUNDING_BOX).addClass(Flick.CLASS_NAMES.box);
245 this._node.addClass(Flick.CLASS_NAMES.content);
249 * The flick event listener. Kicks off the flick animation.
252 * @param e {EventFacade} The flick event facade, containing e.flick.distance, e.flick.velocity etc.
255 _onFlick: function(e) {
256 this._v = e.flick.velocity;
262 * Executes a single frame in the flick animation
264 * @method _flickFrame
267 _flickAnim: function() {
278 step = this.get(STEP),
279 deceleration = this.get(DECELERATION),
280 bounce = this.get(BOUNCE);
282 this._v = (velocity * deceleration);
284 this._snapToEdge = false;
287 x = x - (velocity * step);
291 y = y - (velocity * step);
294 if (Math.abs(velocity).toFixed(4) <= Flick.VELOCITY_THRESHOLD) {
298 this._killTimer(!(this._exceededYBoundary || this._exceededXBoundary));
302 this._snapToEdge = true;
304 } else if (x > maxX) {
305 this._snapToEdge = true;
312 this._snapToEdge = true;
314 } else if (y > maxY) {
315 this._snapToEdge = true;
322 if (this._scrollX && (x < minX || x > maxX)) {
323 this._exceededXBoundary = true;
327 if (this._scrollY && (y < minY || y > maxY)) {
328 this._exceededYBoundary = true;
340 this._flickTimer = Y.later(step, this, this._flickAnim);
345 * Internal utility method to set the X offset position
348 * @param {Number} val
351 _setX : function(val) {
352 this._move(val, null, this.get(DURATION), this.get(EASING));
356 * Internal utility method to set the Y offset position
359 * @param {Number} val
362 _setY : function(val) {
363 this._move(null, val, this.get(DURATION), this.get(EASING));
367 * Internal utility method to move the node to a given XY position,
368 * using transitions, if specified.
371 * @param {Number} x The X offset position
372 * @param {Number} y The Y offset position
373 * @param {Number} duration The duration to use for the transition animation
374 * @param {String} easing The easing to use for the transition animation.
378 _move: function(x, y, duration, easing) {
392 duration = duration || this._snapToEdge ? Flick.SNAP_DURATION : 0;
393 easing = easing || this._snapToEdge ? Flick.SNAP_EASING : Flick.EASING;
398 this._anim(x, y, duration, easing);
402 * Internal utility method to perform the transition step
405 * @param {Number} x The X offset position
406 * @param {Number} y The Y offset position
407 * @param {Number} duration The duration to use for the transition animation
408 * @param {String} easing The easing to use for the transition animation.
412 _anim : function(x, y, duration, easing) {
417 duration : duration / 1000,
421 Y.log("Transition: duration, easing:" + transition.duration, transition.easing, "node-flick");
423 if (Y.Transition.useNative) {
424 transition.transform = 'translate('+ (xn) + 'px,' + (yn) +'px)';
426 transition.left = xn + 'px';
427 transition.top = yn + 'px';
430 this._node.transition(transition);
434 * Internal utility method to constrain the offset value
435 * based on the bounce criteria.
438 * @param {Number} x The offset value to constrain.
439 * @param {Number} max The max offset value.
443 _bounce : function(val, max) {
444 var bounce = this.get(BOUNCE),
445 dist = this.get(BOUNCE_DISTANCE),
446 min = bounce ? -dist : 0;
448 max = bounce ? max + dist : max;
453 } else if(val > max) {
461 * Stop the animation timer
466 _killTimer: function() {
467 if(this._flickTimer) {
468 this._flickTimer.cancel();
475 * The threshold used to determine when the decelerated velocity of the node
478 * @property VELOCITY_THRESHOLD
483 VELOCITY_THRESHOLD : 0.015,
486 * The duration to use for the bounce snap-back transition
488 * @property SNAP_DURATION
496 * The default easing to use for the main flick movement transition
501 * @default 'cubic-bezier(0, 0.1, 0, 1.0)'
503 EASING : 'cubic-bezier(0, 0.1, 0, 1.0)',
506 * The default easing to use for the bounce snap-back transition
508 * @property SNAP_EASING
511 * @default 'ease-out'
513 SNAP_EASING : 'ease-out',
516 * The default CSS class names used by the plugin
518 * @property CLASS_NAMES
523 box: getClassName(Flick.NS),
524 content: getClassName(Flick.NS, "content")
528 Y.Plugin.Flick = Flick;
531 }, '3.13.0', {"requires": ["classnamemanager", "transition", "event-flick", "plugin"], "skinnable": true});