1 |
|
2 |
|
3 |
|
4 |
|
5 |
|
6 |
|
7 |
|
8 |
|
9 |
|
10 |
|
11 |
|
12 |
|
13 |
|
14 |
|
15 | (function(shared, scope, testing) {
|
16 |
|
17 | function groupChildDuration(node) {
|
18 | return node._timing.delay + node.activeDuration + node._timing.endDelay;
|
19 | }
|
20 |
|
21 | function constructor(children, timingInput, id) {
|
22 | this._id = id;
|
23 | this._parent = null;
|
24 | this.children = children || [];
|
25 | this._reparent(this.children);
|
26 | timingInput = shared.numericTimingToObject(timingInput);
|
27 | this._timingInput = shared.cloneTimingInput(timingInput);
|
28 | this._timing = shared.normalizeTimingInput(timingInput, true);
|
29 | this.timing = shared.makeTiming(timingInput, true, this);
|
30 | this.timing._effect = this;
|
31 |
|
32 | if (this._timing.duration === 'auto') {
|
33 | this._timing.duration = this.activeDuration;
|
34 | }
|
35 | }
|
36 |
|
37 | window.SequenceEffect = function() {
|
38 | constructor.apply(this, arguments);
|
39 | };
|
40 |
|
41 | window.GroupEffect = function() {
|
42 | constructor.apply(this, arguments);
|
43 | };
|
44 |
|
45 | constructor.prototype = {
|
46 | _isAncestor: function(effect) {
|
47 | var a = this;
|
48 | while (a !== null) {
|
49 | if (a == effect)
|
50 | return true;
|
51 | a = a._parent;
|
52 | }
|
53 | return false;
|
54 | },
|
55 | _rebuild: function() {
|
56 |
|
57 | var node = this;
|
58 | while (node) {
|
59 | if (node.timing.duration === 'auto') {
|
60 | node._timing.duration = node.activeDuration;
|
61 | }
|
62 | node = node._parent;
|
63 | }
|
64 | if (this._animation) {
|
65 | this._animation._rebuildUnderlyingAnimation();
|
66 | }
|
67 | },
|
68 | _reparent: function(newChildren) {
|
69 | scope.removeMulti(newChildren);
|
70 | for (var i = 0; i < newChildren.length; i++) {
|
71 | newChildren[i]._parent = this;
|
72 | }
|
73 | },
|
74 | _putChild: function(args, isAppend) {
|
75 | var message = isAppend ? 'Cannot append an ancestor or self' : 'Cannot prepend an ancestor or self';
|
76 | for (var i = 0; i < args.length; i++) {
|
77 | if (this._isAncestor(args[i])) {
|
78 | throw {
|
79 | type: DOMException.HIERARCHY_REQUEST_ERR,
|
80 | name: 'HierarchyRequestError',
|
81 | message: message
|
82 | };
|
83 | }
|
84 | }
|
85 | var oldParents = [];
|
86 | for (var i = 0; i < args.length; i++) {
|
87 | isAppend ? this.children.push(args[i]) : this.children.unshift(args[i]);
|
88 | }
|
89 | this._reparent(args);
|
90 | this._rebuild();
|
91 | },
|
92 | append: function() {
|
93 | this._putChild(arguments, true);
|
94 | },
|
95 | prepend: function() {
|
96 | this._putChild(arguments, false);
|
97 | },
|
98 | get parent() {
|
99 | return this._parent;
|
100 | },
|
101 | get firstChild() {
|
102 | return this.children.length ? this.children[0] : null;
|
103 | },
|
104 | get lastChild() {
|
105 | return this.children.length ? this.children[this.children.length - 1] : null;
|
106 | },
|
107 | clone: function() {
|
108 | var clonedTiming = shared.cloneTimingInput(this._timingInput);
|
109 | var clonedChildren = [];
|
110 | for (var i = 0; i < this.children.length; i++) {
|
111 | clonedChildren.push(this.children[i].clone());
|
112 | }
|
113 | return (this instanceof GroupEffect) ?
|
114 | new GroupEffect(clonedChildren, clonedTiming) :
|
115 | new SequenceEffect(clonedChildren, clonedTiming);
|
116 | },
|
117 | remove: function() {
|
118 | scope.removeMulti([this]);
|
119 | }
|
120 | };
|
121 |
|
122 | window.SequenceEffect.prototype = Object.create(constructor.prototype);
|
123 | Object.defineProperty(
|
124 | window.SequenceEffect.prototype,
|
125 | 'activeDuration',
|
126 | {
|
127 | get: function() {
|
128 | var total = 0;
|
129 | this.children.forEach(function(child) {
|
130 | total += groupChildDuration(child);
|
131 | });
|
132 | return Math.max(total, 0);
|
133 | }
|
134 | });
|
135 |
|
136 | window.GroupEffect.prototype = Object.create(constructor.prototype);
|
137 | Object.defineProperty(
|
138 | window.GroupEffect.prototype,
|
139 | 'activeDuration',
|
140 | {
|
141 | get: function() {
|
142 | var max = 0;
|
143 | this.children.forEach(function(child) {
|
144 | max = Math.max(max, groupChildDuration(child));
|
145 | });
|
146 | return max;
|
147 | }
|
148 | });
|
149 |
|
150 | scope.newUnderlyingAnimationForGroup = function(group) {
|
151 | var underlyingAnimation;
|
152 | var timing = null;
|
153 | var ticker = function(tf) {
|
154 | var animation = underlyingAnimation._wrapper;
|
155 | if (!animation) {
|
156 | return;
|
157 | }
|
158 | if (animation.playState == 'pending') {
|
159 | return;
|
160 | }
|
161 | if (!animation.effect) {
|
162 | return;
|
163 | }
|
164 | if (tf == null) {
|
165 | animation._removeChildAnimations();
|
166 | return;
|
167 | }
|
168 |
|
169 |
|
170 |
|
171 |
|
172 |
|
173 | if (tf == 0 && animation.playbackRate < 0) {
|
174 | if (!timing) {
|
175 | timing = shared.normalizeTimingInput(animation.effect.timing);
|
176 | }
|
177 | tf = shared.calculateIterationProgress(shared.calculateActiveDuration(timing), -1, timing);
|
178 | if (isNaN(tf) || tf == null) {
|
179 | animation._forEachChild(function(child) {
|
180 | child.currentTime = -1;
|
181 | });
|
182 | animation._removeChildAnimations();
|
183 | return;
|
184 | }
|
185 | }
|
186 | };
|
187 |
|
188 | var underlyingEffect = new KeyframeEffect(null, [], group._timing, group._id);
|
189 | underlyingEffect.onsample = ticker;
|
190 | underlyingAnimation = scope.timeline._play(underlyingEffect);
|
191 | return underlyingAnimation;
|
192 | };
|
193 |
|
194 | scope.bindAnimationForGroup = function(animation) {
|
195 | animation._animation._wrapper = animation;
|
196 | animation._isGroup = true;
|
197 | scope.awaitStartTime(animation);
|
198 | animation._constructChildAnimations();
|
199 | animation._setExternalAnimation(animation);
|
200 | };
|
201 |
|
202 | scope.groupChildDuration = groupChildDuration;
|
203 |
|
204 | })(webAnimationsShared, webAnimationsNext, webAnimationsTesting);
|