UNPKG

6.4 kBJavaScriptView Raw
1// Copyright 2014 Google Inc. 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(function(shared, scope, testing) {
16
17 var disassociate = function(effect) {
18 effect._animation = undefined;
19 if (effect instanceof window.SequenceEffect || effect instanceof window.GroupEffect) {
20 for (var i = 0; i < effect.children.length; i++) {
21 disassociate(effect.children[i]);
22 }
23 }
24 };
25
26 scope.removeMulti = function(effects) {
27 var oldParents = [];
28 for (var i = 0; i < effects.length; i++) {
29 var effect = effects[i];
30 if (effect._parent) {
31 if (oldParents.indexOf(effect._parent) == -1) {
32 oldParents.push(effect._parent);
33 }
34 effect._parent.children.splice(effect._parent.children.indexOf(effect), 1);
35 effect._parent = null;
36 disassociate(effect);
37 } else if (effect._animation && (effect._animation.effect == effect)) {
38 effect._animation.cancel();
39 effect._animation.effect = new KeyframeEffect(null, []);
40 if (effect._animation._callback) {
41 effect._animation._callback._animation = null;
42 }
43 effect._animation._rebuildUnderlyingAnimation();
44 disassociate(effect);
45 }
46 }
47 for (i = 0; i < oldParents.length; i++) {
48 oldParents[i]._rebuild();
49 }
50 };
51
52 function KeyframeList(effectInput) {
53 this._frames = shared.normalizeKeyframes(effectInput);
54 }
55
56 scope.KeyframeEffect = function(target, effectInput, timingInput, id) {
57 this.target = target;
58 this._parent = null;
59
60 timingInput = shared.numericTimingToObject(timingInput);
61 this._timingInput = shared.cloneTimingInput(timingInput);
62 this._timing = shared.normalizeTimingInput(timingInput);
63
64 this.timing = shared.makeTiming(timingInput, false, this);
65 this.timing._effect = this;
66 if (typeof effectInput == 'function') {
67 shared.deprecated('Custom KeyframeEffect', '2015-06-22', 'Use KeyframeEffect.onsample instead.');
68 this._normalizedKeyframes = effectInput;
69 } else {
70 this._normalizedKeyframes = new KeyframeList(effectInput);
71 }
72 this._keyframes = effectInput;
73 this.activeDuration = shared.calculateActiveDuration(this._timing);
74 this._id = id;
75 return this;
76 };
77
78 scope.KeyframeEffect.prototype = {
79 getFrames: function() {
80 if (typeof this._normalizedKeyframes == 'function')
81 return this._normalizedKeyframes;
82 return this._normalizedKeyframes._frames;
83 },
84 set onsample(callback) {
85 if (typeof this.getFrames() == 'function') {
86 throw new Error('Setting onsample on custom effect KeyframeEffect is not supported.');
87 }
88 this._onsample = callback;
89 if (this._animation) {
90 this._animation._rebuildUnderlyingAnimation();
91 }
92 },
93 get parent() {
94 return this._parent;
95 },
96 clone: function() {
97 if (typeof this.getFrames() == 'function') {
98 throw new Error('Cloning custom effects is not supported.');
99 }
100 var clone = new KeyframeEffect(this.target, [], shared.cloneTimingInput(this._timingInput), this._id);
101 clone._normalizedKeyframes = this._normalizedKeyframes;
102 clone._keyframes = this._keyframes;
103 return clone;
104 },
105 remove: function() {
106 scope.removeMulti([this]);
107 }
108 };
109
110 var originalElementAnimate = Element.prototype.animate;
111 Element.prototype.animate = function(effectInput, options) {
112 var id = '';
113 if (options && options.id) {
114 id = options.id;
115 }
116 return scope.timeline._play(new scope.KeyframeEffect(this, effectInput, options, id));
117 };
118
119 var nullTarget = document.createElementNS('http://www.w3.org/1999/xhtml', 'div');
120 scope.newUnderlyingAnimationForKeyframeEffect = function(keyframeEffect) {
121 if (keyframeEffect) {
122 var target = keyframeEffect.target || nullTarget;
123 var keyframes = keyframeEffect._keyframes;
124 if (typeof keyframes == 'function') {
125 keyframes = [];
126 }
127 var options = keyframeEffect._timingInput;
128 options.id = keyframeEffect._id;
129 } else {
130 var target = nullTarget;
131 var keyframes = [];
132 var options = 0;
133 }
134 return originalElementAnimate.apply(target, [keyframes, options]);
135 };
136
137 // TODO: Remove this once we remove support for custom KeyframeEffects.
138 scope.bindAnimationForKeyframeEffect = function(animation) {
139 if (animation.effect && typeof animation.effect._normalizedKeyframes == 'function') {
140 scope.bindAnimationForCustomEffect(animation);
141 }
142 };
143
144 var pendingGroups = [];
145 scope.awaitStartTime = function(groupAnimation) {
146 if (groupAnimation.startTime !== null || !groupAnimation._isGroup)
147 return;
148 if (pendingGroups.length == 0) {
149 requestAnimationFrame(updatePendingGroups);
150 }
151 pendingGroups.push(groupAnimation);
152 };
153 function updatePendingGroups() {
154 var updated = false;
155 while (pendingGroups.length) {
156 var group = pendingGroups.shift();
157 group._updateChildren();
158 updated = true;
159 }
160 return updated;
161 }
162 var originalGetComputedStyle = window.getComputedStyle;
163 Object.defineProperty(window, 'getComputedStyle', {
164 configurable: true,
165 enumerable: true,
166 value: function() {
167 scope.timeline._updateAnimationsPromises();
168 var result = originalGetComputedStyle.apply(this, arguments);
169 if (updatePendingGroups())
170 result = originalGetComputedStyle.apply(this, arguments);
171 scope.timeline._updateAnimationsPromises();
172 return result;
173 },
174 });
175
176 window.KeyframeEffect = scope.KeyframeEffect;
177 window.Element.prototype.getAnimations = function() {
178 return document.timeline.getAnimations().filter(function(animation) {
179 return animation.effect !== null && animation.effect.target == this;
180 }.bind(this));
181 };
182
183}(webAnimationsShared, webAnimationsNext, webAnimationsTesting));