Skip to content

Commit dd39ac0

Browse files
committed
Merge remote-tracking branch 'web-animations-next/master' into HEAD
2 parents 4ba62d1 + be6e1a1 commit dd39ac0

14 files changed

+175
-102
lines changed

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,14 +35,14 @@ effect.
3535
<div class="pulse" style="width:150px;">Hello world!</div>
3636
<script>
3737
var elem = document.querySelector('.pulse');
38-
var player = document.timeline.play(new Animation(elem, [
38+
var player = elem.animate([
3939
{opacity: 0.5, transform: "scale(0.5)"},
4040
{opacity: 1.0, transform: "scale(1)"}
4141
], {
42-
direction: "alternate",
42+
direction: 'alternate',
4343
duration: 500,
4444
iterations: Infinity
45-
}));
45+
});
4646
</script>
4747

4848
Web Animations supports off-main-thread animations, and also allows procedural

src/animation-constructor.js

Lines changed: 24 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
getFrames: function() { return this._frames; }
2727
};
2828

29-
window.Animation = function(target, effect, timingInput) {
29+
scope.Animation = function(target, effect, timingInput) {
3030
this.target = target;
3131
// TODO: Make modifications to specified update the underlying player
3232
this._timing = shared.normalizeTimingInput(timingInput);
@@ -38,11 +38,31 @@
3838
else
3939
this.effect = new KeyframeEffect(effect);
4040
this._effect = effect;
41-
this._internalPlayer = null;
4241
this.activeDuration = shared.calculateActiveDuration(this._timing);
4342
return this;
4443
};
4544

45+
var originalElementAnimate = Element.prototype.animate;
46+
Element.prototype.animate = function(effect, timing) {
47+
return scope.timeline.play(new scope.Animation(this, effect, timing));
48+
};
49+
50+
var nullTarget = document.createElement('div');
51+
scope.newUnderlyingPlayerForAnimation = function(animation) {
52+
var target = animation.target || nullTarget;
53+
var effect = animation._effect;
54+
if (typeof effect == 'function') {
55+
effect = [];
56+
}
57+
return originalElementAnimate.apply(target, [effect, animation.timing]);
58+
};
59+
60+
scope.bindPlayerForAnimation = function(player) {
61+
if (player.source && typeof player.source.effect == 'function') {
62+
scope.bindPlayerForCustomEffect(player);
63+
}
64+
};
65+
4666
var pendingGroups = [];
4767
scope.awaitStartTime = function(groupPlayer) {
4868
if (groupPlayer.startTime !== null || !groupPlayer._isGroup)
@@ -103,44 +123,10 @@
103123
}
104124
};
105125

106-
var nullTarget = document.createElement('div');
107-
108-
window.document.timeline.play = function(source) {
109-
// TODO: Handle effect callback.
110-
if (source instanceof window.Animation) {
111-
var target = source.target ? source.target : nullTarget;
112-
var player = target.animate(source._effect, source.timing);
113-
player.source = source;
114-
source.player = player;
115-
return player;
116-
}
117-
// FIXME: Move this code out of this module
118-
if (source instanceof window.AnimationSequence || source instanceof window.AnimationGroup) {
119-
var ticker = function(tf) {
120-
if (!player.source)
121-
return;
122-
if (tf == null) {
123-
player._removePlayers();
124-
return;
125-
}
126-
if (player.startTime === null)
127-
return;
128-
129-
player._updateChildren();
130-
};
131-
132-
var player = nullTarget.animate(ticker, source._timing);
133-
player.source = source;
134-
player._isGroup = true;
135-
source.player = player;
136-
scope.awaitStartTime(player);
137-
return player;
138-
}
139-
};
140-
126+
window.Animation = scope.Animation;
141127
window.Element.prototype.getAnimationPlayers = function() {
142128
return document.timeline.getAnimationPlayers().filter(function(player) {
143-
return player._player.source !== null && player._player.source.target == this;
129+
return player.source !== null && player.source.target == this;
144130
}.bind(this));
145131
};
146132

src/effect-callback.js

Lines changed: 7 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,26 +13,13 @@
1313
// limitations under the License.
1414
(function(shared, scope, testing) {
1515

16-
var element = document.createElement('div');
17-
var originalAnimate = Element.prototype.animate;
18-
19-
Element.prototype.animate = function(effect, timing) {
20-
var player;
21-
if (typeof effect == 'function') {
22-
player = new scope.Player(originalAnimate.apply(element, [[], timing]));
23-
bind(player, this, effect, timing);
24-
} else {
25-
player = new scope.Player(originalAnimate.apply(this, [effect, timing]));
26-
}
27-
// FIXME: See if we can just use the maxifill player source and remove this all together.
28-
player._player.source = {target: this};
29-
window.document.timeline._addPlayer(player);
30-
return player;
31-
};
16+
var nullTarget = document.createElement('div');
3217

3318
var sequenceNumber = 0;
34-
function bind(player, target, effect, timing) {
35-
var animation = 'fixme';
19+
scope.bindPlayerForCustomEffect = function(player) {
20+
var target = player.source.target;
21+
var effect = player.source.effect;
22+
var timing = player.source.timing;
3623
var last = undefined;
3724
timing = shared.normalizeTimingInput(timing);
3825
var callback = function() {
@@ -45,7 +32,7 @@
4532
// FIXME: There are actually more conditions under which the effect
4633
// should be called.
4734
if (t !== last)
48-
effect(t, target, animation);
35+
effect(t, target, player.source);
4936
last = t;
5037
};
5138

@@ -54,7 +41,7 @@
5441
callback._sequenceNumber = sequenceNumber++;
5542
player._callback = callback;
5643
register(callback);
57-
}
44+
};
5845

5946
var callbacks = [];
6047
var ticking = false;

src/group-constructors.js

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@
2121

2222
if (this._timing.duration === 'auto')
2323
this._timing.duration = this.activeDuration;
24-
this._internalPlayer = null;
2524
}
2625

2726
window.AnimationSequence = function() {
@@ -52,4 +51,31 @@
5251
}
5352
};
5453

54+
scope.newUnderlyingPlayerForGroup = function(group) {
55+
var underlyingPlayer;
56+
var ticker = function(tf) {
57+
var player = underlyingPlayer._wrapper;
58+
if (!player.source)
59+
return;
60+
if (tf == null) {
61+
player._removePlayers();
62+
return;
63+
}
64+
if (player.startTime === null)
65+
return;
66+
67+
player._updateChildren();
68+
};
69+
70+
underlyingPlayer = scope.timeline.play(new scope.Animation(null, ticker, group._timing));
71+
return underlyingPlayer;
72+
};
73+
74+
scope.bindPlayerForGroup = function(player) {
75+
player._player._wrapper = player;
76+
player._isGroup = true;
77+
scope.awaitStartTime(player);
78+
};
79+
80+
5581
})(webAnimationsShared, webAnimationsMaxifill, webAnimationsTesting);

src/maxifill-player.js

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,40 @@
1313
// limitations under the License.
1414

1515
(function(shared, scope, testing) {
16-
scope.Player = function(player) {
17-
this.source = null;
16+
scope.Player = function(source) {
17+
this.source = source;
18+
if (source) {
19+
// FIXME: detach existing player.
20+
source.player = this;
21+
}
1822
this._isGroup = false;
19-
this._player = player;
23+
this._player = null;
2024
this._childPlayers = [];
2125
this._callback = null;
26+
this._rebuildUnderlyingPlayer();
27+
// Players are constructed in the idle state.
28+
this._player.cancel();
2229
};
2330

2431
// TODO: add a source getter/setter
2532
scope.Player.prototype = {
33+
_rebuildUnderlyingPlayer: function() {
34+
if (this._player) {
35+
this._player.cancel();
36+
this._player = null;
37+
}
38+
39+
if (!this.source || this.source instanceof window.Animation) {
40+
this._player = scope.newUnderlyingPlayerForAnimation(this.source);
41+
scope.bindPlayerForAnimation(this);
42+
}
43+
if (this.source instanceof window.AnimationSequence || this.source instanceof window.AnimationGroup) {
44+
this._player = scope.newUnderlyingPlayerForGroup(this.source);
45+
scope.bindPlayerForGroup(this);
46+
}
47+
48+
// FIXME: move existing currentTime/startTime/playState to new player
49+
},
2650
get paused() {
2751
return this._player.paused;
2852
},
@@ -94,11 +118,7 @@
94118
},
95119
cancel: function() {
96120
this._player.cancel();
97-
if (this._callback) {
98-
this._register();
99-
this._callback._player = null;
100-
}
101-
this.source = null;
121+
this._register();
102122
this._removePlayers();
103123
},
104124
reverse: function() {

src/player.js

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -71,8 +71,7 @@
7171
newTime = +newTime;
7272
if (isNaN(newTime))
7373
return;
74-
if (scope.restart())
75-
this._startTime = null;
74+
scope.restart();
7675
if (!this.paused && this._startTime != null) {
7776
this._startTime = this._timeline.currentTime - newTime / this._playbackRate;
7877
}
@@ -116,14 +115,11 @@
116115
this.paused = false;
117116
if (this.finished || this._idle) {
118117
this._currentTime = this._playbackRate > 0 ? 0 : this._totalDuration;
118+
this._startTime = null;
119119
scope.invalidateEffects();
120120
}
121121
this._finishedFlag = false;
122-
if (!scope.restart()) {
123-
this._startTime = this._timeline.currentTime - this._currentTime / this._playbackRate;
124-
}
125-
else
126-
this._startTime = null;
122+
scope.restart();
127123
this._idle = false;
128124
this._ensureAlive();
129125
},
@@ -149,6 +145,7 @@
149145
},
150146
reverse: function() {
151147
this._playbackRate *= -1;
148+
this._startTime = null;
152149
this.play();
153150
},
154151
addEventListener: function(type, handler) {

src/timeline.js

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -14,28 +14,32 @@
1414

1515

1616
(function(shared, scope, testing) {
17+
1718
scope.AnimationTimeline = function() {
1819
this._players = [];
1920
this.currentTime = undefined;
2021
};
2122

2223
scope.AnimationTimeline.prototype = {
23-
_addPlayer: function(player) {
24-
this._players.push(player);
25-
scope.restartMaxifillTick();
26-
},
2724
// FIXME: This needs to return the wrapped players in maxifill
2825
// TODO: Does this need to be sorted?
2926
// TODO: Do we need to consider needsRetick?
3027
getAnimationPlayers: function() {
31-
this.filterPlayers();
28+
this._discardPlayers();
3229
return this._players.slice();
3330
},
34-
filterPlayers: function() {
31+
_discardPlayers: function() {
3532
this._players = this._players.filter(function(player) {
3633
return player.playState != 'finished' && player.playState != 'idle';
3734
});
38-
}
35+
},
36+
play: function(source) {
37+
var player = new scope.Player(source);
38+
this._players.push(player);
39+
scope.restartMaxifillTick();
40+
player.play();
41+
return player;
42+
},
3943
};
4044

4145
var ticking = false;
@@ -50,7 +54,7 @@
5054
function maxifillTick(t) {
5155
var timeline = window.document.timeline;
5256
timeline.currentTime = t;
53-
timeline.filterPlayers();
57+
timeline._discardPlayers();
5458
if (timeline._players.length == 0)
5559
ticking = false;
5660
else

test/js/animation-constructor.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
suite('animation-constructor', function() {
22
setup(function() {
3-
document.timeline._players = [];
3+
document.timeline.getAnimationPlayers().forEach(function(player) {
4+
player.cancel();
5+
});
46
});
57

68
test('Playing an Animation makes a Player', function() {

test/js/effect-callback.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,4 +49,28 @@ suite('effect-callback', function() {
4949
tick(501);
5050
assert.deepEqual(fractions, [0, 0.5, null]);
5151
});
52+
53+
test('element.animate is given animation', function() {
54+
var callbackAnim;
55+
var player = document.body.animate(function(t, target, a) {
56+
callbackAnim = a;
57+
}, 100);
58+
tick(50);
59+
tick(150);
60+
assert.equal(isTicking(), false);
61+
assert(callbackAnim, 'callback should be set');
62+
assert.equal(callbackAnim.target, document.body);
63+
});
64+
65+
test('effect callback on animation is given source animation', function() {
66+
var callbackAnim;
67+
var anim = new Animation(document.body, function(t, target, a) {
68+
callbackAnim = a;
69+
}, 1000);
70+
var player = document.timeline.play(anim);
71+
tick(50);
72+
tick(550);
73+
assert.equal(player.currentTime, 500);
74+
assert.equal(callbackAnim, anim);
75+
});
5276
});

test/js/group-constructors.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ suite('group-constructors', function() {
1515

1616
test('player getter for children in groups, and __internalPlayer, work as expected', function() {
1717
var p = document.timeline.play(simpleAnimationGroup());
18-
tick(100);
18+
tick(0);
1919
assert.equal(p.source.player, p);
2020
assert.equal(p._childPlayers[0].source.player, p);
2121
assert.equal(p._childPlayers[1].source.player, p);

0 commit comments

Comments
 (0)