Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

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