Skip to content

Commit

Permalink
Added hold_phase to Animation
Browse files Browse the repository at this point in the history
Prior to this change, effects are always given the current phase of the
timeline associated with them. This fails in certain situations such as
pausing and then scrolling, setting current time with an inactive
timeline, and many other scenarios where hold_time is set.

In order to fix this, we have introduced hold_phase. Anytime hold_time
is updated, hold_phase will also be updated with the appropriate phase.
Sometimes the phase is set explicitly and other times we pull from the
timeline current time.

Animation effects will be given the animation hold_phase if it is
populated, otherwise it will calculate its current phase (similar to how
current time is calculated).

The WebAnimations spec is being updated to reflect this change.

Bug: 1046833
Change-Id: I4bf1e42eaab684c18829a79acc1ab8911ec893af
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2135336
Commit-Queue: Jordan Taylor <jortaylo@microsoft.com>
Reviewed-by: Kevin Ellis <kevers@chromium.org>
Reviewed-by: Majid Valipour <majidvp@chromium.org>
Cr-Commit-Position: refs/heads/master@{#797835}
  • Loading branch information
Jordan Taylor authored and chromium-wpt-export-bot committed Aug 13, 2020
1 parent fdfb0a5 commit 4d71665
Showing 1 changed file with 163 additions and 0 deletions.
163 changes: 163 additions & 0 deletions scroll-animations/scroll-animation-effect-phases.tentative.html
Expand Up @@ -151,4 +151,167 @@
);
}
}

function createKeyframeEffectOpacity(test){
return new KeyframeEffect(
createDiv(test),
{
opacity: [0.3, 0.7]
},
{
duration: 1000
}
);
}

function verifyTimelineBeforePhase(animation){
assert_equals(animation.timeline.phase, "before");
assert_equals(animation.timeline.currentTime, 0);
assert_equals(animation.currentTime, 0);
assert_equals(
animation.effect.getComputedTiming().localTime,
0,
"effect local time in timeline before phase");
}

function verifyEffectBeforePhase(animation){
// progress == null AND opacity == 1 implies we are in the effect before
// phase
assert_equals(
animation.effect.getComputedTiming().progress,
null
);
assert_equals(
window.getComputedStyle(animation.effect.target).getPropertyValue("opacity"),
"1"
);
}

promise_test(async t => {
const animation = new Animation(
createKeyframeEffectOpacity(t),
createScrollTimelineWithOffsets(t, "20%", "80%")
);

const scroller = animation.timeline.scrollSource;
const maxScroll = scroller.scrollHeight - scroller.clientHeight;

animation.play();
await animation.ready;

verifyTimelineBeforePhase(animation);
verifyEffectBeforePhase(animation);

animation.pause();
await waitForNextFrame();

verifyTimelineBeforePhase(animation);
verifyEffectBeforePhase(animation);

animation.play();
await waitForNextFrame();

verifyTimelineBeforePhase(animation);
verifyEffectBeforePhase(animation);
}, 'Verify that (play -> pause -> play) doesn\'t change phase/progress.');

promise_test(async t => {
const animation = new Animation(
createKeyframeEffectOpacity(t),
createScrollTimelineWithOffsets(t, "20%", "80%")
);

const scroller = animation.timeline.scrollSource;
const maxScroll = scroller.scrollHeight - scroller.clientHeight;

animation.play();
await animation.ready;

verifyTimelineBeforePhase(animation);
verifyEffectBeforePhase(animation);

animation.pause();
await waitForNextFrame();

verifyTimelineBeforePhase(animation);
verifyEffectBeforePhase(animation);

// Scrolling should not cause the animation effect to change.
scroller.scrollTop = 0.5 * maxScroll;
await waitForNextFrame();

// Check timeline phase
assert_equals(animation.timeline.phase, "active");
assert_equals(animation.timeline.currentTime, 500);
assert_equals(animation.currentTime, 0);
assert_equals(
animation.effect.getComputedTiming().localTime,
0,
"effect local time"
);

// Make sure the effect is still in the before phase even though the
// timeline is not.
verifyEffectBeforePhase(animation);
}, 'Pause in before phase, scroll timeline into active phase, animation ' +
'should remain in the before phase');

promise_test(async t => {
const animation = new Animation(
createKeyframeEffectOpacity(t),
createScrollTimelineWithOffsets(t, "20%", "80%")
);
const scroller = animation.timeline.scrollSource;
const maxScroll = scroller.scrollHeight - scroller.clientHeight;

animation.play();
await animation.ready;

// Causes the timeline to be inactive
scroller.style.overflow = "visible";
await waitForNextFrame();
await waitForNextFrame();

// Check timeline phase
assert_equals(animation.timeline.phase, "inactive");
assert_equals(animation.timeline.currentTime, null);
assert_equals(animation.currentTime, null);
assert_equals(
animation.effect.getComputedTiming().localTime,
null,
"effect local time with inactive timeline"
);

verifyEffectBeforePhase(animation);

// Setting the current time while timeline is inactive should cause hold phase
// and hold time to be populated
animation.currentTime = 500;
await waitForNextFrame();
await waitForNextFrame();

// Check timeline phase
assert_equals(animation.timeline.phase, "inactive");
assert_equals(animation.timeline.currentTime, null);
assert_equals(animation.currentTime, 500);
assert_equals(
animation.effect.getComputedTiming().localTime,
500,
"effect local time after setting animation current time"
);

// Check effect phase
// progress == 0.5 AND opacity == 0.5 shows we are in the effect active phase
assert_equals(
animation.effect.getComputedTiming().progress,
0.5,
"effect progress"
);
assert_equals(
window.getComputedStyle(animation.effect.target).getPropertyValue("opacity"),
"0.5",
"effect opacity after setting animation current time"
);
}, 'Make scroller inactive, then set current time to an in range time');

</script>

0 comments on commit 4d71665

Please sign in to comment.