Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simple iteration progress works #482

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/effect-callback.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
var callback = function() {
var t = callback._animation ? callback._animation.currentTime : null;
if (t !== null) {
t = shared.calculateTimeFraction(shared.calculateActiveDuration(timing), t, timing);
t = shared.calculateIterationProgress(shared.calculateActiveDuration(timing), t, timing);
if (isNaN(t))
t = null;
}
Expand Down
4 changes: 2 additions & 2 deletions src/group-constructors.js
Original file line number Diff line number Diff line change
Expand Up @@ -168,13 +168,13 @@

// If the group has a negative playback rate and is not fill backwards/both, then it should go
// out of effect when it reaches the start of its active interval (tf == 0). If it is fill
// backwards/both then it should stay in effect. calculateTimeFraction will return 0 in the
// backwards/both then it should stay in effect. calculateIterationProgress will return 0 in the
// backwards-filling case, and null otherwise.
if (tf == 0 && animation.playbackRate < 0) {
if (!timing) {
timing = shared.normalizeTimingInput(animation.effect.timing);
}
tf = shared.calculateTimeFraction(shared.calculateActiveDuration(timing), -1, timing);
tf = shared.calculateIterationProgress(shared.calculateActiveDuration(timing), -1, timing);
if (isNaN(tf) || tf == null) {
animation._forEachChild(function(child) {
child.currentTime = -1;
Expand Down
2 changes: 1 addition & 1 deletion src/keyframe-effect.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
var timeFraction = 0;
var activeDuration = shared.calculateActiveDuration(timing);
var effectTime = function(localTime) {
return shared.calculateTimeFraction(activeDuration, localTime, timing);
return shared.calculateIterationProgress(activeDuration, localTime, timing);
};
effectTime._totalDuration = timing.delay + activeDuration + timing.endDelay;
return effectTime;
Expand Down
100 changes: 65 additions & 35 deletions src/timing-utilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,10 @@
}

function repeatedDuration(timing) {
// https://w3c.github.io/web-animations/#calculating-the-active-duration
if (timing.duration === 0 || timing.iterations === 0) {
return 0;
}
return timing.duration * timing.iterations;
}

Expand All @@ -273,19 +277,24 @@
var PhaseActive = 3;

function calculatePhase(activeDuration, localTime, timing) {
// https://w3c.github.io/web-animations/#animation-effect-phases-and-states
if (localTime == null) {
return PhaseNone;
}
if (localTime < timing.delay) {

var endTime = timing.delay + activeDuration + timing.endDelay;
if (localTime < Math.min(timing.delay, endTime)) {
return PhaseBefore;
}
if (localTime >= timing.delay + activeDuration) {
if (localTime >= Math.min(timing.delay + activeDuration, endTime)) {
return PhaseAfter;
}

return PhaseActive;
}

function calculateActiveTime(activeDuration, fillMode, localTime, phase, delay) {
// https://w3c.github.io/web-animations/#calculating-the-active-time
switch (phase) {
case PhaseBefore:
if (fillMode == 'backwards' || fillMode == 'both')
Expand All @@ -302,56 +311,82 @@
}
}

function calculateScaledActiveTime(activeDuration, activeTime, startOffset, timing) {
return (timing.playbackRate < 0 ? activeTime - activeDuration : activeTime) * timing.playbackRate + startOffset;
function calculateOverallProgress(iterationDuration, phase, iterations, activeTime, iterationStart) {
// https://w3c.github.io/web-animations/#calculating-the-overall-progress
var overallProgress = iterationStart;
if (iterationDuration === 0) {
if (phase !== PhaseBefore) {
overallProgress += iterations;
}
} else {
overallProgress += activeTime / iterationDuration;
}
return overallProgress;
}

function calculateIterationTime(iterationDuration, repeatedDuration, scaledActiveTime, startOffset, timing) {
if (scaledActiveTime === Infinity || scaledActiveTime === -Infinity || (scaledActiveTime - startOffset == repeatedDuration && timing.iterations && ((timing.iterations + timing.iterationStart) % 1 == 0))) {
return iterationDuration;
}
function calculateSimpleIterationProgress(overallProgress, iterationStart, phase, iterations, activeTime, iterationDuration) {
// https://w3c.github.io/web-animations/#calculating-the-simple-iteration-progress

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent newline.

return scaledActiveTime % iterationDuration;
var simpleIterationProgress = (overallProgress === Infinity) ? iterationStart % 1 : overallProgress % 1;
if (simpleIterationProgress === 0 && phase === PhaseAfter && iterations !== 0 &&
(activeTime !== 0 || iterationDuration === 0)) {
simpleIterationProgress = 1;
}
return simpleIterationProgress;
}

function calculateCurrentIteration(iterationDuration, iterationTime, scaledActiveTime, timing) {
if (scaledActiveTime === 0) {
return 0;
function calculateCurrentIteration(phase, iterations, simpleIterationProgress, overallProgress) {
// https://w3c.github.io/web-animations/#calculating-the-current-iteration
if (phase === PhaseAfter && iterations === Infinity) {
return Infinity;
}
if (iterationTime == iterationDuration) {
return timing.iterationStart + timing.iterations - 1;
if (simpleIterationProgress === 1) {
return Math.floor(overallProgress) - 1;
}
return Math.floor(scaledActiveTime / iterationDuration);
return Math.floor(overallProgress);
}

function calculateTransformedTime(currentIteration, iterationDuration, iterationTime, timing) {
var currentIterationIsOdd = currentIteration % 2 >= 1;
var currentDirectionIsForwards = timing.direction == 'normal' || timing.direction == (currentIterationIsOdd ? 'alternate-reverse' : 'alternate');
var directedTime = currentDirectionIsForwards ? iterationTime : iterationDuration - iterationTime;
var timeFraction = directedTime / iterationDuration;
return iterationDuration * timing._easingFunction(timeFraction);
function calculateDirectedProgress(playbackDirection, currentIteration, simpleIterationProgress) {
// https://w3c.github.io/web-animations/#calculating-the-directed-progress
var currentDirection = playbackDirection;
if (playbackDirection !== 'normal' && playbackDirection !== 'reverse') {
var d = currentIteration;
if (playbackDirection === 'alternate-reverse') {
d += 1;
}
currentDirection = 'normal';
if (d !== Infinity && d % 2 !== 0) {
currentDirection = 'reverse';
}
}
if (currentDirection === 'normal') {
return simpleIterationProgress;
}
return 1 - simpleIterationProgress;
}

function calculateTimeFraction(activeDuration, localTime, timing) {
function calculateIterationProgress(activeDuration, localTime, timing) {
var phase = calculatePhase(activeDuration, localTime, timing);
var activeTime = calculateActiveTime(activeDuration, timing.fill, localTime, phase, timing.delay);
if (activeTime === null)
return null;
if (activeDuration === 0)
return phase === PhaseBefore ? 0 : 1;
var startOffset = timing.iterationStart * timing.duration;
var scaledActiveTime = calculateScaledActiveTime(activeDuration, activeTime, startOffset, timing);
var iterationTime = calculateIterationTime(timing.duration, repeatedDuration(timing), scaledActiveTime, startOffset, timing);
var currentIteration = calculateCurrentIteration(timing.duration, iterationTime, scaledActiveTime, timing);
return calculateTransformedTime(currentIteration, timing.duration, iterationTime, timing) / timing.duration;

var overallProgress = calculateOverallProgress(timing.duration, phase, timing.iterations, activeTime, timing.iterationStart);
var simpleIterationProgress = calculateSimpleIterationProgress(overallProgress, timing.iterationStart, phase, timing.iterations, activeTime, timing.duration);
var currentIteration = calculateCurrentIteration(phase, timing.iterations, simpleIterationProgress, overallProgress);
var directedProgress = calculateDirectedProgress(timing.direction, currentIteration, simpleIterationProgress);

// https://w3c.github.io/web-animations/#calculating-the-transformed-progress
// https://w3c.github.io/web-animations/#calculating-the-iteration-progress
return timing._easingFunction(directedProgress);
}

shared.cloneTimingInput = cloneTimingInput;
shared.makeTiming = makeTiming;
shared.numericTimingToObject = numericTimingToObject;
shared.normalizeTimingInput = normalizeTimingInput;
shared.calculateActiveDuration = calculateActiveDuration;
shared.calculateTimeFraction = calculateTimeFraction;
shared.calculateIterationProgress = calculateIterationProgress;
shared.calculatePhase = calculatePhase;
shared.normalizeEasing = normalizeEasing;
shared.parseEasingFunction = parseEasingFunction;
Expand All @@ -366,11 +401,6 @@
testing.PhaseBefore = PhaseBefore;
testing.PhaseActive = PhaseActive;
testing.PhaseAfter = PhaseAfter;
testing.calculateActiveTime = calculateActiveTime;
testing.calculateScaledActiveTime = calculateScaledActiveTime;
testing.calculateIterationTime = calculateIterationTime;
testing.calculateCurrentIteration = calculateCurrentIteration;
testing.calculateTransformedTime = calculateTransformedTime;
}

})(webAnimationsShared, webAnimationsTesting);
2 changes: 1 addition & 1 deletion src/web-animations-next-animation.js
Original file line number Diff line number Diff line change
Expand Up @@ -368,7 +368,7 @@
var timing = this.effect._timing;
var t = this.currentTime;
if (t !== null)
t = shared.calculateTimeFraction(shared.calculateActiveDuration(timing), t, timing);
t = shared.calculateIterationProgress(shared.calculateActiveDuration(timing), t, timing);
if (t == null || isNaN(t))
this._removeChildAnimations();
},
Expand Down
2 changes: 1 addition & 1 deletion test/js/group-animation.js
Original file line number Diff line number Diff line change
Expand Up @@ -1093,7 +1093,7 @@ suite('group-animation', function() {
tick(102);
assert.equal(getComputedStyle(this.target).marginLeft, '2px');
tick(103);
assert.equal(getComputedStyle(this.target).marginLeft, '3px');
assert.equal(getComputedStyle(this.target).marginLeft, '0px');
tick(104);
});

Expand Down
65 changes: 10 additions & 55 deletions test/js/timing-utilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,72 +56,27 @@ suite('timing-utilities', function() {
assert.equal(f(0.1), 0.1);
assert.equal(f(0.25), 0.2);
});
test('calculating phase', function() {
// calculatePhase(activeDuration, localTime, timing);
assert.equal(calculatePhase(1000, 100, {delay: 0}), PhaseActive);
assert.equal(calculatePhase(1000, 100, {delay: 200}), PhaseBefore);
assert.equal(calculatePhase(1000, 2000, {delay: 200}), PhaseAfter);
assert.equal(calculatePhase(1000, null, {delay: 200}), PhaseNone);
});
test('calculating active time', function() {
// calculateActiveTime(activeDuration, fillMode, localTime, phase, delay);
assert.equal(calculateActiveTime(1000, 'forwards', 100, PhaseActive, 0), 100);
assert.equal(calculateActiveTime(1000, 'forwards', 100, PhaseBefore, 200), null);
assert.equal(calculateActiveTime(1000, 'both', 100, PhaseBefore, 200), 0);
assert.equal(calculateActiveTime(1000, 'forwards', 500, PhaseActive, 200), 300);
assert.equal(calculateActiveTime(1000, 'forwards', 1100, PhaseAfter, 200), 1000);
assert.equal(calculateActiveTime(1000, 'none', 1100, PhaseAfter, 200), null);
assert.equal(calculateActiveTime(Infinity, 'both', 5000000, PhaseActive, 2000000), 3000000);
assert.equal(calculateActiveTime(Infinity, 'both', 50000, PhaseBefore, 2000000), 0);
});
test('calculating scaled active time', function() {
// calculateScaledActiveTime(activeDuration, activeTime, startOffset, timingInput);
assert.equal(calculateScaledActiveTime(1000, 200, 300, {playbackRate: 1.5}), 600);
assert.equal(calculateScaledActiveTime(1000, 200, 300, {playbackRate: -4}), 3500);
assert.equal(calculateScaledActiveTime(Infinity, 400, 200, {playbackRate: 1}), 600);
assert.equal(calculateScaledActiveTime(Infinity, 400, 200, {playbackRate: -4}), Infinity);
});
test('calculating iteration time', function() {
// calculateIterationTime(iterationDuration, repeatedDuration, scaledActiveTime, startOffset, timingInput);
assert.equal(calculateIterationTime(500, 5000, 600, 100, {iterations: 10, iterationStart: 0}), 100);
assert.equal(calculateIterationTime(500, 5000, Infinity, 100, {iterations: 10, iterationStart: 0}), 500);
assert.equal(calculateIterationTime(500, 5000, 5100, 100, {iterations: 3.2, iterationStart: 0.8}), 500);
});
test('calculating current iteration', function() {
// calculateCurrentIteration(iterationDuration, iterationTime, scaledActiveTime, timingInput);
assert.equal(calculateCurrentIteration(1000, 400, 4400, {iterations: 50, iterationStart: 0.8}), 4);
assert.equal(calculateCurrentIteration(1000, 1000, 4400, {iterations: 50.2, iterationStart: 0.8}), 50);
});
test('calculating transformed time', function() {
// calculateTransformedTime(currentIteration, iterationDuration, iterationTime, timingInput);
assert.equal(calculateTransformedTime(4, 1000, 200, {_easingFunction: function(x) { return x; }, direction: 'normal'}), 200);
assert.equal(calculateTransformedTime(4, 1000, 200, {_easingFunction: function(x) { return x; }, direction: 'reverse'}), 800);
assert.closeTo(calculateTransformedTime(4, 1000, 200, {_easingFunction: function(x) { return x * x; }, direction: 'reverse'}), 640, 0.0001);
assert.closeTo(calculateTransformedTime(4, 1000, 600, {_easingFunction: function(x) { return x * x; }, direction: 'alternate'}), 360, 0.0001);
assert.closeTo(calculateTransformedTime(3, 1000, 600, {_easingFunction: function(x) { return x * x; }, direction: 'alternate'}), 160, 0.0001);
assert.closeTo(calculateTransformedTime(4, 1000, 600, {_easingFunction: function(x) { return x * x; }, direction: 'alternate-reverse'}), 160, 0.0001);
assert.closeTo(calculateTransformedTime(3, 1000, 600, {_easingFunction: function(x) { return x * x; }, direction: 'alternate-reverse'}), 360, 0.0001);
});
test('EffectTime', function() {
var timing = normalizeTimingInput({duration: 1000, iterations: 4, iterationStart: 0.5, easing: 'linear', direction: 'alternate', delay: 100, fill: 'forwards'});
var timing2 = normalizeTimingInput({duration: 1000, iterations: 4, iterationStart: 0.5, easing: 'ease', direction: 'alternate', delay: 100, fill: 'forwards'});
var effectTF = effectTime(timing);
var effectTF2 = effectTime(timing2);
var epsilon = 0.005;
assert.equal(effectTF(0), null);
assert.equal(effectTF(100), 0.5);
assert.closeTo(effectTF2(100), 0.8, 0.005);
assert.closeTo(effectTF2(100), 0.8, epsilon);
assert.equal(effectTF(600), 1);
assert.closeTo(effectTF2(600), 1, 0.005);
assert.equal(effectTF(700), 0.9);
assert.closeTo(effectTF2(700), 0.99, 0.005);
assert.closeTo(effectTF2(600), 1, epsilon);
assert.closeTo(effectTF(700), 0.9, epsilon);
assert.closeTo(effectTF2(700), 0.99, epsilon);
assert.equal(effectTF(1600), 0);
assert.closeTo(effectTF2(1600), 0, 0.005);
assert.equal(effectTF(4000), 0.4);
assert.closeTo(effectTF2(4000), 0.68, 0.005);
assert.closeTo(effectTF2(1600), 0, epsilon);
assert.closeTo(effectTF(4000), 0.4, epsilon);
assert.closeTo(effectTF2(4000), 0.68, epsilon);
assert.equal(effectTF(4100), 0.5);
assert.closeTo(effectTF2(4100), 0.8, 0.005);
assert.closeTo(effectTF2(4100), 0.8, epsilon);
assert.equal(effectTF(6000), 0.5);
assert.closeTo(effectTF2(6000), 0.8, 0.005);
assert.closeTo(effectTF2(6000), 0.8, epsilon);
});
test('TypeErrors', function() {
var timing = normalizeTimingInput({
Expand Down
Loading