Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
seleniumhq
GitHub Repository: seleniumhq/selenium
Path: blob/trunk/third_party/closure/goog/fx/animation.js
2868 views
1
// Copyright 2006 The Closure Library Authors. All Rights Reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
// http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS-IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
/**
16
* @fileoverview Classes for doing animations and visual effects.
17
*
18
* (Based loosly on my animation code for 13thparallel.org, with extra
19
* inspiration from the DojoToolkit's modifications to my code)
20
* @author [email protected] (Erik Arvidsson)
21
*/
22
23
goog.provide('goog.fx.Animation');
24
goog.provide('goog.fx.Animation.EventType');
25
goog.provide('goog.fx.Animation.State');
26
goog.provide('goog.fx.AnimationEvent');
27
28
goog.require('goog.array');
29
goog.require('goog.asserts');
30
goog.require('goog.events.Event');
31
goog.require('goog.fx.Transition');
32
goog.require('goog.fx.TransitionBase');
33
goog.require('goog.fx.anim');
34
goog.require('goog.fx.anim.Animated');
35
36
37
38
/**
39
* Constructor for an animation object.
40
* @param {Array<number>} start Array for start coordinates.
41
* @param {Array<number>} end Array for end coordinates.
42
* @param {number} duration Length of animation in milliseconds.
43
* @param {Function=} opt_acc Acceleration function, returns 0-1 for inputs 0-1.
44
* @constructor
45
* @struct
46
* @implements {goog.fx.anim.Animated}
47
* @implements {goog.fx.Transition}
48
* @extends {goog.fx.TransitionBase}
49
*/
50
goog.fx.Animation = function(start, end, duration, opt_acc) {
51
goog.fx.Animation.base(this, 'constructor');
52
53
if (!goog.isArray(start) || !goog.isArray(end)) {
54
throw Error('Start and end parameters must be arrays');
55
}
56
57
if (start.length != end.length) {
58
throw Error('Start and end points must be the same length');
59
}
60
61
/**
62
* Start point.
63
* @type {Array<number>}
64
* @protected
65
*/
66
this.startPoint = start;
67
68
/**
69
* End point.
70
* @type {Array<number>}
71
* @protected
72
*/
73
this.endPoint = end;
74
75
/**
76
* Duration of animation in milliseconds.
77
* @type {number}
78
* @protected
79
*/
80
this.duration = duration;
81
82
/**
83
* Acceleration function, which must return a number between 0 and 1 for
84
* inputs between 0 and 1.
85
* @type {Function|undefined}
86
* @private
87
*/
88
this.accel_ = opt_acc;
89
90
/**
91
* Current coordinate for animation.
92
* @type {Array<number>}
93
* @protected
94
*/
95
this.coords = [];
96
97
/**
98
* Whether the animation should use "right" rather than "left" to position
99
* elements in RTL. This is a temporary flag to allow clients to transition
100
* to the new behavior at their convenience. At some point it will be the
101
* default.
102
* @type {boolean}
103
* @private
104
*/
105
this.useRightPositioningForRtl_ = false;
106
107
/**
108
* Current frame rate.
109
* @private {number}
110
*/
111
this.fps_ = 0;
112
113
/**
114
* Percent of the way through the animation.
115
* @protected {number}
116
*/
117
this.progress = 0;
118
119
/**
120
* Timestamp for when last frame was run.
121
* @protected {?number}
122
*/
123
this.lastFrame = null;
124
};
125
goog.inherits(goog.fx.Animation, goog.fx.TransitionBase);
126
127
128
/**
129
* Sets whether the animation should use "right" rather than "left" to position
130
* elements. This is a temporary flag to allow clients to transition
131
* to the new component at their convenience. At some point "right" will be
132
* used for RTL elements by default.
133
* @param {boolean} useRightPositioningForRtl True if "right" should be used for
134
* positioning, false if "left" should be used for positioning.
135
*/
136
goog.fx.Animation.prototype.enableRightPositioningForRtl = function(
137
useRightPositioningForRtl) {
138
this.useRightPositioningForRtl_ = useRightPositioningForRtl;
139
};
140
141
142
/**
143
* Whether the animation should use "right" rather than "left" to position
144
* elements. This is a temporary flag to allow clients to transition
145
* to the new component at their convenience. At some point "right" will be
146
* used for RTL elements by default.
147
* @return {boolean} True if "right" should be used for positioning, false if
148
* "left" should be used for positioning.
149
*/
150
goog.fx.Animation.prototype.isRightPositioningForRtlEnabled = function() {
151
return this.useRightPositioningForRtl_;
152
};
153
154
155
/**
156
* Events fired by the animation.
157
* @enum {string}
158
*/
159
goog.fx.Animation.EventType = {
160
/**
161
* Dispatched when played for the first time OR when it is resumed.
162
* @deprecated Use goog.fx.Transition.EventType.PLAY.
163
*/
164
PLAY: goog.fx.Transition.EventType.PLAY,
165
166
/**
167
* Dispatched only when the animation starts from the beginning.
168
* @deprecated Use goog.fx.Transition.EventType.BEGIN.
169
*/
170
BEGIN: goog.fx.Transition.EventType.BEGIN,
171
172
/**
173
* Dispatched only when animation is restarted after a pause.
174
* @deprecated Use goog.fx.Transition.EventType.RESUME.
175
*/
176
RESUME: goog.fx.Transition.EventType.RESUME,
177
178
/**
179
* Dispatched when animation comes to the end of its duration OR stop
180
* is called.
181
* @deprecated Use goog.fx.Transition.EventType.END.
182
*/
183
END: goog.fx.Transition.EventType.END,
184
185
/**
186
* Dispatched only when stop is called.
187
* @deprecated Use goog.fx.Transition.EventType.STOP.
188
*/
189
STOP: goog.fx.Transition.EventType.STOP,
190
191
/**
192
* Dispatched only when animation comes to its end naturally.
193
* @deprecated Use goog.fx.Transition.EventType.FINISH.
194
*/
195
FINISH: goog.fx.Transition.EventType.FINISH,
196
197
/**
198
* Dispatched when an animation is paused.
199
* @deprecated Use goog.fx.Transition.EventType.PAUSE.
200
*/
201
PAUSE: goog.fx.Transition.EventType.PAUSE,
202
203
/**
204
* Dispatched each frame of the animation. This is where the actual animator
205
* will listen.
206
*/
207
ANIMATE: 'animate',
208
209
/**
210
* Dispatched when the animation is destroyed.
211
*/
212
DESTROY: 'destroy'
213
};
214
215
216
/**
217
* @deprecated Use goog.fx.anim.TIMEOUT.
218
*/
219
goog.fx.Animation.TIMEOUT = goog.fx.anim.TIMEOUT;
220
221
222
/**
223
* Enum for the possible states of an animation.
224
* @deprecated Use goog.fx.Transition.State instead.
225
* @enum {number}
226
*/
227
goog.fx.Animation.State = goog.fx.TransitionBase.State;
228
229
230
/**
231
* @deprecated Use goog.fx.anim.setAnimationWindow.
232
* @param {Window} animationWindow The window in which to animate elements.
233
*/
234
goog.fx.Animation.setAnimationWindow = function(animationWindow) {
235
goog.fx.anim.setAnimationWindow(animationWindow);
236
};
237
238
239
/**
240
* Starts or resumes an animation.
241
* @param {boolean=} opt_restart Whether to restart the
242
* animation from the beginning if it has been paused.
243
* @return {boolean} Whether animation was started.
244
* @override
245
*/
246
goog.fx.Animation.prototype.play = function(opt_restart) {
247
if (opt_restart || this.isStopped()) {
248
this.progress = 0;
249
this.coords = this.startPoint;
250
} else if (this.isPlaying()) {
251
return false;
252
}
253
254
goog.fx.anim.unregisterAnimation(this);
255
256
var now = /** @type {number} */ (goog.now());
257
258
this.startTime = now;
259
if (this.isPaused()) {
260
this.startTime -= this.duration * this.progress;
261
}
262
263
this.endTime = this.startTime + this.duration;
264
this.lastFrame = this.startTime;
265
266
if (!this.progress) {
267
this.onBegin();
268
}
269
270
this.onPlay();
271
272
if (this.isPaused()) {
273
this.onResume();
274
}
275
276
this.setStatePlaying();
277
278
goog.fx.anim.registerAnimation(this);
279
this.cycle(now);
280
281
return true;
282
};
283
284
285
/**
286
* Stops the animation.
287
* @param {boolean=} opt_gotoEnd If true the animation will move to the
288
* end coords.
289
* @override
290
*/
291
goog.fx.Animation.prototype.stop = function(opt_gotoEnd) {
292
goog.fx.anim.unregisterAnimation(this);
293
this.setStateStopped();
294
295
if (opt_gotoEnd) {
296
this.progress = 1;
297
}
298
299
this.updateCoords_(this.progress);
300
301
this.onStop();
302
this.onEnd();
303
};
304
305
306
/**
307
* Pauses the animation (iff it's playing).
308
* @override
309
*/
310
goog.fx.Animation.prototype.pause = function() {
311
if (this.isPlaying()) {
312
goog.fx.anim.unregisterAnimation(this);
313
this.setStatePaused();
314
this.onPause();
315
}
316
};
317
318
319
/**
320
* @return {number} The current progress of the animation, the number
321
* is between 0 and 1 inclusive.
322
*/
323
goog.fx.Animation.prototype.getProgress = function() {
324
return this.progress;
325
};
326
327
328
/**
329
* Sets the progress of the animation.
330
* @param {number} progress The new progress of the animation.
331
*/
332
goog.fx.Animation.prototype.setProgress = function(progress) {
333
this.progress = progress;
334
if (this.isPlaying()) {
335
var now = goog.now();
336
// If the animation is already playing, we recompute startTime and endTime
337
// such that the animation plays consistently, that is:
338
// now = startTime + progress * duration.
339
this.startTime = now - this.duration * this.progress;
340
this.endTime = this.startTime + this.duration;
341
}
342
};
343
344
345
/**
346
* Disposes of the animation. Stops an animation, fires a 'destroy' event and
347
* then removes all the event handlers to clean up memory.
348
* @override
349
* @protected
350
*/
351
goog.fx.Animation.prototype.disposeInternal = function() {
352
if (!this.isStopped()) {
353
this.stop(false);
354
}
355
this.onDestroy();
356
goog.fx.Animation.base(this, 'disposeInternal');
357
};
358
359
360
/**
361
* Stops an animation, fires a 'destroy' event and then removes all the event
362
* handlers to clean up memory.
363
* @deprecated Use dispose() instead.
364
*/
365
goog.fx.Animation.prototype.destroy = function() {
366
this.dispose();
367
};
368
369
370
/** @override */
371
goog.fx.Animation.prototype.onAnimationFrame = function(now) {
372
this.cycle(now);
373
};
374
375
376
/**
377
* Handles the actual iteration of the animation in a timeout
378
* @param {number} now The current time.
379
*/
380
goog.fx.Animation.prototype.cycle = function(now) {
381
goog.asserts.assertNumber(this.startTime);
382
goog.asserts.assertNumber(this.endTime);
383
goog.asserts.assertNumber(this.lastFrame);
384
// Happens in rare system clock reset.
385
if (now < this.startTime) {
386
this.endTime = now + this.endTime - this.startTime;
387
this.startTime = now;
388
}
389
this.progress = (now - this.startTime) / (this.endTime - this.startTime);
390
391
if (this.progress > 1) {
392
this.progress = 1;
393
}
394
395
this.fps_ = 1000 / (now - this.lastFrame);
396
this.lastFrame = now;
397
398
this.updateCoords_(this.progress);
399
400
// Animation has finished.
401
if (this.progress == 1) {
402
this.setStateStopped();
403
goog.fx.anim.unregisterAnimation(this);
404
405
this.onFinish();
406
this.onEnd();
407
408
// Animation is still under way.
409
} else if (this.isPlaying()) {
410
this.onAnimate();
411
}
412
};
413
414
415
/**
416
* Calculates current coordinates, based on the current state. Applies
417
* the acceleration function if it exists.
418
* @param {number} t Percentage of the way through the animation as a decimal.
419
* @private
420
*/
421
goog.fx.Animation.prototype.updateCoords_ = function(t) {
422
if (goog.isFunction(this.accel_)) {
423
t = this.accel_(t);
424
}
425
this.coords = new Array(this.startPoint.length);
426
for (var i = 0; i < this.startPoint.length; i++) {
427
this.coords[i] =
428
(this.endPoint[i] - this.startPoint[i]) * t + this.startPoint[i];
429
}
430
};
431
432
433
/**
434
* Dispatches the ANIMATE event. Sub classes should override this instead
435
* of listening to the event.
436
* @protected
437
*/
438
goog.fx.Animation.prototype.onAnimate = function() {
439
this.dispatchAnimationEvent(goog.fx.Animation.EventType.ANIMATE);
440
};
441
442
443
/**
444
* Dispatches the DESTROY event. Sub classes should override this instead
445
* of listening to the event.
446
* @protected
447
*/
448
goog.fx.Animation.prototype.onDestroy = function() {
449
this.dispatchAnimationEvent(goog.fx.Animation.EventType.DESTROY);
450
};
451
452
453
/** @override */
454
goog.fx.Animation.prototype.dispatchAnimationEvent = function(type) {
455
this.dispatchEvent(new goog.fx.AnimationEvent(type, this));
456
};
457
458
459
460
/**
461
* Class for an animation event object.
462
* @param {string} type Event type.
463
* @param {goog.fx.Animation} anim An animation object.
464
* @constructor
465
* @struct
466
* @extends {goog.events.Event}
467
*/
468
goog.fx.AnimationEvent = function(type, anim) {
469
goog.fx.AnimationEvent.base(this, 'constructor', type);
470
471
/**
472
* The current coordinates.
473
* @type {Array<number>}
474
*/
475
this.coords = anim.coords;
476
477
/**
478
* The x coordinate.
479
* @type {number}
480
*/
481
this.x = anim.coords[0];
482
483
/**
484
* The y coordinate.
485
* @type {number}
486
*/
487
this.y = anim.coords[1];
488
489
/**
490
* The z coordinate.
491
* @type {number}
492
*/
493
this.z = anim.coords[2];
494
495
/**
496
* The current duration.
497
* @type {number}
498
*/
499
this.duration = anim.duration;
500
501
/**
502
* The current progress.
503
* @type {number}
504
*/
505
this.progress = anim.getProgress();
506
507
/**
508
* Frames per second so far.
509
*/
510
this.fps = anim.fps_;
511
512
/**
513
* The state of the animation.
514
* @type {number}
515
*/
516
this.state = anim.getStateInternal();
517
518
/**
519
* The animation object.
520
* @type {goog.fx.Animation}
521
*/
522
// TODO(arv): This can be removed as this is the same as the target
523
this.anim = anim;
524
};
525
goog.inherits(goog.fx.AnimationEvent, goog.events.Event);
526
527
528
/**
529
* Returns the coordinates as integers (rounded to nearest integer).
530
* @return {!Array<number>} An array of the coordinates rounded to
531
* the nearest integer.
532
*/
533
goog.fx.AnimationEvent.prototype.coordsAsInts = function() {
534
return goog.array.map(this.coords, Math.round);
535
};
536
537