Skip to content

Commit 551e131

Browse files
committed
Merge pull request #306 from rjwright/GroupPlaybackRateRebased
Fix group playback rates
2 parents 29943a1 + 764069f commit 551e131

File tree

5 files changed

+577
-25
lines changed

5 files changed

+577
-25
lines changed

src/animation.js

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,14 @@
4848

4949
scope.Animation.prototype = {
5050
_ensureAlive: function() {
51-
this._inEffect = this._effect._update(this.currentTime);
51+
// If an animation is playing backwards and is not fill backwards/both
52+
// then it should go out of effect when it reaches the start of its
53+
// active interval (currentTime == 0).
54+
if (this.playbackRate < 0 && this.currentTime === 0) {
55+
this._inEffect = this._effect._update(-1);
56+
} else {
57+
this._inEffect = this._effect._update(this.currentTime);
58+
}
5259
if (!this._inTimeline && (this._inEffect || !this._finishedFlag)) {
5360
this._inTimeline = true;
5461
scope.timeline._animations.push(this);
@@ -98,8 +105,15 @@
98105
return this._playbackRate;
99106
},
100107
set playbackRate(value) {
108+
if (value == this._playbackRate) {
109+
return;
110+
}
101111
var oldCurrentTime = this.currentTime;
102112
this._playbackRate = value;
113+
this._startTime = null;
114+
if (this.playState != 'paused' && this.playState != 'idle') {
115+
this.play();
116+
}
103117
if (oldCurrentTime != null) {
104118
this.currentTime = oldCurrentTime;
105119
}
@@ -153,8 +167,7 @@
153167
this._startTime = null;
154168
},
155169
reverse: function() {
156-
this._playbackRate *= -1;
157-
this._startTime = null;
170+
this.playbackRate *= -1;
158171
this.play();
159172
},
160173
addEventListener: function(type, handler) {

src/group-constructors.js

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,16 +57,37 @@
5757

5858
scope.newUnderlyingAnimationForGroup = function(group) {
5959
var underlyingAnimation;
60+
var timing = null;
6061
var ticker = function(tf) {
6162
var animation = underlyingAnimation._wrapper;
62-
if (animation.playState == 'pending') return;
63+
if (animation.playState == 'pending')
64+
return;
6365

6466
if (!animation.effect)
6567
return;
68+
6669
if (tf == null) {
6770
animation._removeChildren();
6871
return;
6972
}
73+
74+
// If the group has a negative playback rate and is not fill backwards/both, then it should go
75+
// out of effect when it reaches the start of its active interval (tf == 0). If it is fill
76+
// backwards/both then it should stay in effect. calculateTimeFraction will return 0 in the
77+
// backwards-filling case, and null otherwise.
78+
if (tf == 0 && animation.playbackRate < 0) {
79+
if (!timing) {
80+
timing = shared.normalizeTimingInput(animation.effect.timing);
81+
}
82+
tf = shared.calculateTimeFraction(shared.calculateActiveDuration(timing), -1, timing);
83+
if (isNaN(tf) || tf == null) {
84+
animation._forEachChild(function(child) {
85+
child.currentTime = -1;
86+
});
87+
animation._removeChildren();
88+
return;
89+
}
90+
}
7091
};
7192

7293
underlyingAnimation = scope.timeline.play(new scope.KeyframeEffect(null, ticker, group._timing));

src/web-animations-next-animation.js

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,10 @@
8686
},
8787
_arrangeChildren: function(childAnimation, offset) {
8888
if (this.startTime === null) {
89-
childAnimation.currentTime = this.currentTime - offset;
89+
childAnimation.currentTime = this.currentTime - offset / this.playbackRate;
9090
childAnimation._startTime = null;
91-
} else if (childAnimation.startTime !== this.startTime + offset) {
92-
childAnimation.startTime = this.startTime + offset;
91+
} else if (childAnimation.startTime !== this.startTime + offset / this.playbackRate) {
92+
childAnimation.startTime = this.startTime + offset / this.playbackRate;
9393
}
9494
},
9595
get paused() {
@@ -137,10 +137,17 @@
137137
return this._animation.playbackRate;
138138
},
139139
set playbackRate(value) {
140+
var oldCurrentTime = this.currentTime;
140141
this._animation.playbackRate = value;
141142
this._forEachChild(function(childAnimation) {
142143
childAnimation.playbackRate = value;
143144
});
145+
if (this.playState != 'paused' && this.playState != 'idle') {
146+
this.play();
147+
}
148+
if (oldCurrentTime !== null) {
149+
this.currentTime = oldCurrentTime;
150+
}
144151
},
145152
get finished() {
146153
return this._animation.finished;
@@ -177,14 +184,14 @@
177184
this._removeChildren();
178185
},
179186
reverse: function() {
187+
var oldCurrentTime = this.currentTime;
180188
this._animation.reverse();
181-
scope.awaitStartTime(this);
182-
this._register();
183-
this._forEachChild(function(child, offset) {
184-
child.reverse();
185-
child.startTime = this.startTime + offset * this.playbackRate;
186-
child.currentTime = this.currentTime + offset * this.playbackRate;
189+
this._forEachChild(function(childAnimation) {
190+
childAnimation.reverse();
187191
});
192+
if (oldCurrentTime !== null) {
193+
this.currentTime = oldCurrentTime;
194+
}
188195
},
189196
addEventListener: function(type, handler) {
190197
var wrapped = handler;

test/js/animation.js

Lines changed: 73 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -239,14 +239,80 @@ suite('animation', function() {
239239
assert.equal(a.startTime, null);
240240
assert.equal(a.currentTime, null);
241241
});
242-
test('setting playbackRate does preserves the current time', function() {
243-
tick(900);
242+
test('setting playbackRate sets startTime to null unless the playbackRate is not changing, ' +
243+
'preserves the current time', function() {
244+
tick(0);
245+
var a = document.body.animate([], 1000);
246+
tick(1);
247+
tick(11);
248+
assert.equal(a.currentTime, 10);
249+
250+
a.playbackRate = 2;
251+
assert.equal(a.playbackRate, 2);
252+
assert.equal(a.currentTime, 10);
253+
assert.equal(a.startTime, null);
254+
tick(12);
255+
assert.equal(a.currentTime, 10);
256+
assert.equal(a.startTime, 7);
257+
tick(22);
258+
assert.equal(a.currentTime, 30);
259+
assert.equal(a.startTime, 7);
260+
261+
a.playbackRate = -1;
262+
assert.equal(a.playbackRate, -1);
263+
assert.equal(a.currentTime, 30);
264+
assert.equal(a.startTime, null);
265+
tick(23);
266+
assert.equal(a.currentTime, 30);
267+
assert.equal(a.startTime, 53);
268+
tick(33);
269+
assert.equal(a.currentTime, 20);
270+
assert.equal(a.startTime, 53);
271+
272+
a.playbackRate = -1;
273+
assert.equal(a.playbackRate, -1);
274+
assert.equal(a.currentTime, 20);
275+
assert.equal(a.startTime, 53);
276+
tick(43);
277+
assert.equal(a.currentTime, 10);
278+
assert.equal(a.startTime, 53);
279+
}
280+
);
281+
test('setting playbackRate puts animation back into effect if it is not finished', function() {
282+
tick(0);
283+
var a = document.body.animate([], 1000);
284+
assert.equal(a.playbackRate, 1);
285+
tick(1);
286+
tick(1002);
287+
assert.equal(a.currentTime, 1000);
288+
289+
a.playbackRate = -1;
290+
assert.equal(a.playbackRate, -1);
291+
assert.equal(a.currentTime, 1000);
292+
tick(1003);
293+
assert.equal(a.currentTime, 1000);
294+
tick(1503);
295+
assert.equal(a.currentTime, 500);
296+
});
297+
test('setting playbackRate does not put animation back into effect if it is finished', function() {
298+
tick(0);
244299
var a = document.body.animate([], 1000);
245-
tick(1100);
246-
var oldCurrentTime = a.currentTime;
247-
a.playbackRate = 2;
248-
assert.equal(a.playbackRate, 2);
249-
assert.equal(a.currentTime, oldCurrentTime);
300+
assert.equal(a.playbackRate, 1);
301+
tick(1);
302+
tick(1002);
303+
assert.equal(a.currentTime, 1000);
304+
assert.equal(a.startTime, 1);
305+
306+
a.playbackRate = 0.5;
307+
assert.equal(a.playbackRate, 0.5);
308+
assert.equal(a.currentTime, 1000);
309+
assert.equal(a.startTime, null);
310+
tick(1003);
311+
assert.equal(a.currentTime, 1000);
312+
assert.equal(a.startTime, -997);
313+
tick(1503);
314+
assert.equal(a.currentTime, 1000);
315+
assert.equal(a.startTime, -997);
250316
});
251317
test('finishing works as expected', function() {
252318
tick(1000);

0 commit comments

Comments
 (0)