Skip to content

Commit

Permalink
Merge pull request #485 from alancutter/optimiseAnimationMutations
Browse files Browse the repository at this point in the history
Avoid updating all animation effects on single animation mutation
  • Loading branch information
suzyh authored Aug 3, 2016
2 parents 5b96c1e + 24f7a24 commit fbb1a15
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 20 deletions.
33 changes: 27 additions & 6 deletions src/animation.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
this._paused = true;
}
this._tickCurrentTime(newTime, true);
scope.invalidateEffects();
scope.applyDirtiedAnimation(this);
},
get startTime() {
return this._startTime;
Expand All @@ -107,7 +107,7 @@
return;
this._startTime = newTime;
this._tickCurrentTime((this._timeline.currentTime - this._startTime) * this.playbackRate);
scope.invalidateEffects();
scope.applyDirtiedAnimation(this);
},
get playbackRate() {
return this._playbackRate;
Expand All @@ -123,7 +123,7 @@
this._finishedFlag = false;
this._idle = false;
this._ensureAlive();
scope.invalidateEffects();
scope.applyDirtiedAnimation(this);
}
if (oldCurrentTime != null) {
this.currentTime = oldCurrentTime;
Expand Down Expand Up @@ -165,7 +165,7 @@
this._finishedFlag = false;
this._idle = false;
this._ensureAlive();
scope.invalidateEffects();
scope.applyDirtiedAnimation(this);
},
pause: function() {
if (!this._isFinished && !this._paused && !this._idle) {
Expand All @@ -183,21 +183,22 @@
this.currentTime = this._playbackRate > 0 ? this._totalDuration : 0;
this._startTime = this._totalDuration - this.currentTime;
this._currentTimePending = false;
scope.invalidateEffects();
scope.applyDirtiedAnimation(this);
},
cancel: function() {
if (!this._inEffect)
return;
this._inEffect = false;
this._idle = true;
this._paused = false;
this._isFinished = true;
this._finishedFlag = true;
this._currentTime = 0;
this._startTime = null;
this._effect._update(null);
// effects are invalid after cancellation as the animation state
// needs to un-apply.
scope.invalidateEffects();
scope.applyDirtiedAnimation(this);
},
reverse: function() {
this.playbackRate *= -1;
Expand Down Expand Up @@ -249,6 +250,26 @@
get _needsTick() {
return (this.playState in {'pending': 1, 'running': 1}) || !this._finishedFlag;
},
_targetAnimations: function() {
var target = this._effect._target;
if (!target._activeAnimations) {
target._activeAnimations = [];
}
return target._activeAnimations;
},
_markTarget: function() {
var animations = this._targetAnimations();
if (animations.indexOf(this) === -1) {
animations.push(this);
}
},
_unmarkTarget: function() {
var animations = this._targetAnimations();
var index = animations.indexOf(this);
if (index !== -1) {
animations.splice(index, 1);
}
},
};

if (WEB_ANIMATIONS_TESTING) {
Expand Down
1 change: 1 addition & 0 deletions src/keyframe-effect.js
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
keyframeEffect._hasSameTarget = function(otherTarget) {
return target === otherTarget;
};
keyframeEffect._target = target;
keyframeEffect._totalDuration = effectTime._totalDuration;
keyframeEffect._id = id;
return keyframeEffect;
Expand Down
55 changes: 41 additions & 14 deletions src/tick.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
rafCallbacks = [];
if (t < timeline.currentTime)
t = timeline.currentTime;
tick(t, true);
timeline._animations.sort(compareAnimations);
timeline._animations = tick(t, true, timeline._animations)[0];
processing.forEach(function(entry) { entry[1](t); });
applyPendingEffects();
_now = undefined;
Expand All @@ -63,7 +64,7 @@
animation._timeline = this;
this._animations.push(animation);
scope.restart();
scope.invalidateEffects();
scope.applyDirtiedAnimation(animation);
return animation;
}
};
Expand Down Expand Up @@ -92,8 +93,24 @@
return hasRestartedThisFrame;
};

scope.invalidateEffects = function() {
tick(scope.timeline.currentTime, false);
// RAF is supposed to be the last script to occur before frame rendering but not
// all browsers behave like this. This function is for synchonously updating an
// animation's effects whenever its state is mutated by script to work around
// incorrect script execution ordering by the browser.
scope.applyDirtiedAnimation = function(animation) {
if (inTick) {
return;
}
animation._markTarget();
var animations = animation._targetAnimations();
animations.sort(compareAnimations);
var inactiveAnimations = tick(scope.timeline.currentTime, false, animations.slice())[1];
inactiveAnimations.forEach(function(animation) {
var index = timeline._animations.indexOf(animation);
if (index !== -1) {
timeline._animations.splice(index, 1);
}
});
applyPendingEffects();
};

Expand All @@ -105,41 +122,51 @@

var t60hz = 1000 / 60;

function tick(t, isAnimationFrame) {
var inTick = false;
function tick(t, isAnimationFrame, updatingAnimations) {
inTick = true;
hasRestartedThisFrame = false;
var timeline = scope.timeline;

timeline.currentTime = t;
timeline._animations.sort(compareAnimations);
ticking = false;
var updatingAnimations = timeline._animations;
timeline._animations = [];

var newPendingClears = [];
var newPendingEffects = [];
updatingAnimations = updatingAnimations.filter(function(animation) {
var activeAnimations = [];
var inactiveAnimations = [];
updatingAnimations.forEach(function(animation) {
animation._tick(t, isAnimationFrame);

if (!animation._inEffect)
if (!animation._inEffect) {
newPendingClears.push(animation._effect);
else
animation._unmarkTarget();
} else {
newPendingEffects.push(animation._effect);
animation._markTarget();
}

if (animation._needsTick)
ticking = true;

var alive = animation._inEffect || animation._needsTick;
animation._inTimeline = alive;
return alive;
if (alive) {
activeAnimations.push(animation);
} else {
inactiveAnimations.push(animation);
}
});

// FIXME: Should remove dupliactes from pendingEffects.
pendingEffects.push.apply(pendingEffects, newPendingClears);
pendingEffects.push.apply(pendingEffects, newPendingEffects);

timeline._animations.push.apply(timeline._animations, updatingAnimations);

if (ticking)
requestAnimationFrame(function() {});

inTick = false;
return [activeAnimations, inactiveAnimations];
};

if (WEB_ANIMATIONS_TESTING) {
Expand Down

0 comments on commit fbb1a15

Please sign in to comment.