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