2
Copyright (c) 2010, Yahoo! Inc. All rights reserved.
3
Code licensed under the BSD License:
4
http://developer.yahoo.com/yui/license.html
8
YUI.add('node-flick', function(Y) {
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
* The constraining box relative to which the flick animation and bounds should be calculated.
126
* @attribute boundingBox
128
* @default parentNode
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
164
* @property Flick.NAME
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
347
* @param {Number} val
350
_setX : function(val) {
351
this._move(val, null, this.get(DURATION), this.get(EASING));
355
* Internal utility method to set the Y offset position
357
* @param {Number} val
360
_setY : function(val) {
361
this._move(null, val, this.get(DURATION), this.get(EASING));
365
* Internal utility method to move the node to a given XY position,
366
* using transitions, if specified.
368
* @param {Number} x The X offset position
369
* @param {Number} y The Y offset position
370
* @param {Number} duration The duration to use for the transition animation
371
* @param {String} easing The easing to use for the transition animation.
375
_move: function(x, y, duration, easing) {
389
duration = duration || this._snapToEdge ? Flick.SNAP_DURATION : 0;
390
easing = easing || this._snapToEdge ? Flick.SNAP_EASING : Flick.EASING;
395
this._anim(x, y, duration, easing);
399
* Internal utility method to perform the transition step
401
* @param {Number} x The X offset position
402
* @param {Number} y The Y offset position
403
* @param {Number} duration The duration to use for the transition animation
404
* @param {String} easing The easing to use for the transition animation.
408
_anim : function(x, y, duration, easing) {
413
duration : duration / 1000,
418
if (Y.Transition.useNative) {
419
transition.transform = 'translate('+ (xn) + 'px,' + (yn) +'px)';
421
transition.left = xn + 'px';
422
transition.top = yn + 'px';
425
this._node.transition(transition);
429
* Internal utility method to constrain the offset value
430
* based on the bounce criteria.
432
* @param {Number} x The offset value to constrain.
433
* @param {Number} max The max offset value.
437
_bounce : function(val, max) {
438
var bounce = this.get(BOUNCE),
439
dist = this.get(BOUNCE_DISTANCE),
440
min = bounce ? -dist : 0;
442
max = bounce ? max + dist : max;
447
} else if(val > max) {
455
* Stop the animation timer
460
_killTimer: function() {
461
if(this._flickTimer) {
462
this._flickTimer.cancel();
469
* The threshold used to determine when the decelerated velocity of the node
472
* @property Flick.VELOCITY_THRESHOLD
477
VELOCITY_THRESHOLD : 0.015,
480
* The duration to use for the bounce snap-back transition
482
* @property Flick.SNAP_DURATION
490
* The default easing to use for the main flick movement transition
492
* @property Flick.EASING
495
* @default 'cubic-bezier(0, 0.1, 0, 1.0)'
497
EASING : 'cubic-bezier(0, 0.1, 0, 1.0)',
500
* The default easing to use for the bounce snap-back transition
502
* @property Flick.SNAP_EASING
505
* @default 'ease-out'
507
SNAP_EASING : 'ease-out',
510
* The default CSS class names used by the plugin
512
* @property Flick.CLASS_NAMES
517
box: getClassName(Flick.NS),
518
content: getClassName(Flick.NS, "content")
522
Y.Plugin.Flick = Flick;
525
}, '3.2.0' ,{requires:['classnamemanager', 'transition', 'event-flick', 'plugin']});