268 lines
8.6 KiB
HTML
268 lines
8.6 KiB
HTML
<!DOCTYPE html>
|
|
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1">
|
|
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#scroll-timelines">
|
|
<script src="/resources/testharness.js"></script>
|
|
<script src="/resources/testharnessreport.js"></script>
|
|
<script src="/web-animations/testcommon.js"></script>
|
|
<script src="support/testcommon.js"></script>
|
|
<style>
|
|
main {
|
|
timeline-scope: --timeline;
|
|
}
|
|
|
|
main > div {
|
|
overflow: hidden;
|
|
width: 100px;
|
|
height: 100px;
|
|
}
|
|
main > div > div {
|
|
height: 200px;
|
|
}
|
|
@keyframes expand {
|
|
from { width: 100px; }
|
|
to { width: 200px; }
|
|
}
|
|
#element {
|
|
width: 0px;
|
|
height: 20px;
|
|
animation-name: expand;
|
|
/* Some of the tests in this file assume animations attached to the
|
|
DocumentTimeline are "stopped" without actually being paused.
|
|
Using 600s + steps(10, end) achieves this for one minute.*/
|
|
animation-duration: 600s;
|
|
animation-timing-function: steps(10, end);
|
|
}
|
|
</style>
|
|
<main id=main>
|
|
<div id=scroller1 class=scroller>
|
|
<div></div>
|
|
</div>
|
|
<div id=scroller2 class=scroller>
|
|
<div></div>
|
|
</div>
|
|
<div id=container></div>
|
|
</main>
|
|
<script>
|
|
// Force layout of scrollers.
|
|
scroller1.offsetTop;
|
|
scroller2.offsetTop;
|
|
|
|
// Note the steps(10, end) timing function and height:100px. (10px scroll
|
|
// resolution).
|
|
scroller1.scrollTop = 20;
|
|
scroller2.scrollTop = 40;
|
|
|
|
function insertElement() {
|
|
let element = document.createElement('div');
|
|
element.id = 'element';
|
|
container.append(element);
|
|
return element;
|
|
}
|
|
|
|
// Runs a test with dynamically added/removed elements or CSS rules.
|
|
// Each test is instantiated twice: once for the initial style resolve where
|
|
// the DOM change was effectuated, and once after scrolling.
|
|
function dynamic_rule_test(func, description) {
|
|
// assert_width is an async function which verifies that the computed value
|
|
// of 'width' is as expected.
|
|
const instantiate = (assert_width, description) => {
|
|
promise_test(async (t) => {
|
|
try {
|
|
await func(t, assert_width);
|
|
} finally {
|
|
while (container.firstChild)
|
|
container.firstChild.remove();
|
|
main.style = '';
|
|
scroller1.style = '';
|
|
scroller2.style = '';
|
|
}
|
|
}, description);
|
|
};
|
|
|
|
// Verify that the computed style is as expected after a full frame update
|
|
// following the rule change took place.
|
|
instantiate(async (element, expected) => {
|
|
await waitForCSSScrollTimelineStyle();
|
|
assert_equals(getComputedStyle(element).width, expected);
|
|
}, description + ' [immediate]');
|
|
|
|
// Verify the computed style after scrolling a bit.
|
|
instantiate(async (element, expected) => {
|
|
await waitForNextFrame();
|
|
scroller1.scrollTop = scroller1.scrollTop + 10;
|
|
scroller2.scrollTop = scroller2.scrollTop + 10;
|
|
await waitForNextFrame();
|
|
scroller1.scrollTop = scroller1.scrollTop - 10;
|
|
scroller2.scrollTop = scroller2.scrollTop - 10;
|
|
await waitForNextFrame();
|
|
assert_equals(getComputedStyle(element).width, expected);
|
|
}, description + ' [scroll]');
|
|
}
|
|
|
|
dynamic_rule_test(async (t, assert_width) => {
|
|
let element = insertElement();
|
|
|
|
// This element initially has a DocumentTimeline.
|
|
await assert_width(element, '100px');
|
|
|
|
// Switch to scroll timeline.
|
|
scroller1.style.scrollTimelineName = '--timeline';
|
|
element.style.animationTimeline = '--timeline';
|
|
await assert_width(element, '120px');
|
|
|
|
// Switching from ScrollTimeline -> DocumentTimeline should preserve
|
|
// current time.
|
|
scroller1.style = '';
|
|
element.style = '';
|
|
await assert_width(element, '120px');
|
|
}, 'Switching between document and scroll timelines');
|
|
|
|
dynamic_rule_test(async (t, assert_width) => {
|
|
let element = insertElement();
|
|
|
|
// Flush style and create the animation with play pending.
|
|
getComputedStyle(element).animation;
|
|
|
|
let anim = element.getAnimations()[0];
|
|
assert_true(anim.pending, "The animation is in play pending");
|
|
|
|
// Switch to scroll timeline for a pending animation.
|
|
scroller1.style.scrollTimelineName = '--timeline';
|
|
element.style.animationTimeline = '--timeline';
|
|
|
|
await anim.ready;
|
|
assert_false(anim.pending, "The animation is not pending");
|
|
|
|
await assert_width(element, '120px');
|
|
}, 'Switching pending animation from document to scroll timelines');
|
|
|
|
dynamic_rule_test(async (t, assert_width) => {
|
|
let element = insertElement();
|
|
|
|
// Note: #scroller1 is at 20%, and #scroller2 is at 40%.
|
|
scroller1.style.scrollTimelineName = '--timeline1';
|
|
scroller2.style.scrollTimelineName = '--timeline2';
|
|
main.style.timelineScope = "--timeline1, --timeline2";
|
|
|
|
await assert_width(element, '100px');
|
|
|
|
element.style.animationTimeline = '--timeline1';
|
|
await assert_width(element, '120px');
|
|
|
|
element.style.animationTimeline = '--timeline2';
|
|
await assert_width(element, '140px');
|
|
|
|
element.style.animationTimeline = '--timeline1';
|
|
await assert_width(element, '120px');
|
|
|
|
// Switching from ScrollTimeline -> DocumentTimeline should preserve
|
|
// current time.
|
|
element.style.animationTimeline = '';
|
|
await assert_width(element, '120px');
|
|
|
|
}, 'Changing computed value of animation-timeline changes effective timeline');
|
|
|
|
dynamic_rule_test(async (t, assert_width) => {
|
|
let element = insertElement();
|
|
|
|
scroller1.style.scrollTimelineName = '--timeline';
|
|
|
|
// DocumentTimeline applies by default.
|
|
await assert_width(element, '100px');
|
|
|
|
// Wait for the animation to be ready so that we a start time and no hold
|
|
// time.
|
|
await element.getAnimations()[0].ready;
|
|
|
|
// DocumentTimeline -> none
|
|
element.style.animationTimeline = '--none';
|
|
await assert_width(element, '0px');
|
|
|
|
// none -> DocumentTimeline
|
|
element.style.animationTimeline = '';
|
|
await assert_width(element, '100px');
|
|
|
|
// DocumentTimeline -> ScrollTimeline
|
|
element.style.animationTimeline = '--timeline';
|
|
await assert_width(element, '120px');
|
|
|
|
// ScrollTimeline -> none
|
|
element.style.animationTimeline = '--none';
|
|
await assert_width(element, '120px');
|
|
|
|
// none -> ScrollTimeline
|
|
element.style.animationTimeline = '--timeline';
|
|
await assert_width(element, '120px');
|
|
}, 'Changing to/from animation-timeline:none');
|
|
|
|
|
|
dynamic_rule_test(async (t, assert_width) => {
|
|
let element = insertElement();
|
|
|
|
element.style.animationDirection = 'reverse';
|
|
element.style.animationTimeline = '--timeline';
|
|
|
|
// Inactive animation-timeline. Animation is inactive.
|
|
await assert_width(element, '0px');
|
|
|
|
// Note: #scroller1 is at 20%.
|
|
scroller1.style.scrollTimelineName = '--timeline';
|
|
await assert_width(element, '180px');
|
|
|
|
// Note: #scroller2 is at 40%.
|
|
scroller1.style.scrollTimelineName = '';
|
|
scroller2.style.scrollTimelineName = '--timeline';
|
|
await assert_width(element, '160px');
|
|
|
|
element.style.animationDirection = '';
|
|
await assert_width(element, '140px');
|
|
}, 'Reverse animation direction');
|
|
|
|
dynamic_rule_test(async (t, assert_width) => {
|
|
let element = insertElement();
|
|
element.style.animationTimeline = '--timeline';
|
|
|
|
// Inactive animation-timeline. Animation effect is inactive.
|
|
await assert_width(element, '0px');
|
|
|
|
// Note: #scroller1 is at 20%.
|
|
scroller1.style.scrollTimelineName = '--timeline';
|
|
await assert_width(element, '120px');
|
|
|
|
element.style.animationPlayState = 'paused';
|
|
|
|
// We should still be at the same position after pausing.
|
|
await assert_width(element, '120px');
|
|
|
|
// Note: #scroller2 is at 40%.
|
|
scroller1.style.scrollTimelineName = '';
|
|
scroller2.style.scrollTimelineName = '--timeline';
|
|
|
|
// Should be at the same position until we unpause.
|
|
await assert_width(element, '120px');
|
|
|
|
// Unpausing should synchronize to the scroll position.
|
|
element.style.animationPlayState = '';
|
|
await assert_width(element, '140px');
|
|
}, 'Change to timeline attachment while paused');
|
|
|
|
dynamic_rule_test(async (t, assert_width) => {
|
|
let element = insertElement();
|
|
|
|
// Note: #scroller1 is at 20%.
|
|
scroller1.style.scrollTimelineName = '--timeline';
|
|
|
|
await assert_width(element, '100px');
|
|
|
|
element.style.animationTimeline = '--timeline';
|
|
element.style.animationPlayState = 'paused';
|
|
|
|
// Pausing should happen after the timeline was modified.
|
|
// https://github.com/w3c/csswg-drafts/issues/5653
|
|
await assert_width(element, '120px');
|
|
|
|
// Changing the play state should not change the animation current time.
|
|
element.style.animationPlayState = 'running';
|
|
await assert_width(element, '120px');
|
|
}, 'Switching timelines and pausing at the same time');
|
|
</script>
|