<!DOCTYPE html>
<title>Scroll timelines and animation attachment ranges</title>
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#named-timeline-range">
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#animation-range">
<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>
#scroller {
overflow-y: scroll;
width: 200px;
height: 200px;
scroll-timeline: --t1;
}
.spacer {
height: 1100px;
}
#target {
height: 100px;
width: 0;
font-size: 10px;
background-color: green;
}
@keyframes grow {
to { width: 200px }
}
.anim-1 {
animation: auto grow linear;
animation-timeline: --t1;
animation-range-start: 25%;
animation-range-end: 75%;
}
.anim-2 {
animation: auto grow linear;
animation-timeline: --t1;
animation-range-start: 40px;
animation-range-end: 700px;
}
.anim-3 {
animation: auto grow linear;
animation-timeline: --t1;
animation-range-start: calc(30% + 40px);
animation-range-end: calc(70% - 20px);
}
.anim-4 {
animation: auto grow linear;
animation-timeline: --t1;
animation-range-start: 5em;
animation-range-end: calc(100% - 5em);
}
</style>
<div id=scroller>
<div id=target></div>
<div class=spacer></div>
</div>
<script>
// Test via web-animation API.
async function test_range_waapi(t, options) {
const timeline = new ScrollTimeline({source: scroller, axis: 'block'});
const anim =
target.animate([{ width: "200px" }],
{
timeline: timeline,
rangeStart: options.rangeStart,
rangeEnd: options.rangeEnd
});
t.add_cleanup(() => {
anim.cancel();
});
await anim.ready;
scroller.scrollTop = 600;
await waitForNextFrame();
const expectedProgress =
(600 - options.startOffset) / (options.endOffset - options.startOffset);
assert_approx_equals(anim.effect.getComputedTiming().progress,
expectedProgress, 0.001);
}
promise_test(async t => {
await test_range_waapi(t, {
rangeStart: "25%",
rangeEnd: "75%",
startOffset: 250,
endOffset: 750
});
}, 'Scroll timeline with percentage range [JavaScript API]');
promise_test(async t => {
await test_range_waapi(t, {
rangeStart: "40px",
rangeEnd: "700px",
startOffset: 40,
endOffset: 700
});
}, 'Scroll timeline with px range [JavaScript API]');
promise_test(async t => {
await test_range_waapi(t, {
rangeStart: "calc(30% + 40px)",
rangeEnd: "calc(70% - 20px)",
startOffset: 340,
endOffset: 680
});
}, 'Scroll timeline with calculated range [JavaScript API]');
promise_test(async t => {
t.add_cleanup(() => {
target.style.fontSize = '';
});
await test_range_waapi(t, {
rangeStart: "5em",
rangeEnd: "calc(100% - 5em)",
startOffset: 50,
endOffset: 950
});
target.style.fontSize = '20px';
await waitForNextFrame();
const anim = target.getAnimations()[0];
const expectedProgress = (600 - 100) / (900 - 100);
assert_approx_equals(anim.effect.getComputedTiming().progress,
expectedProgress, 0.001);
}, 'Scroll timeline with EM range [JavaScript API]');
// Test via CSS.
async function test_range_css(t, options) {
t.add_cleanup(() => {
target.classList.remove(options.animation);
});
target.classList.add(options.animation);
const anim = target.getAnimations()[0];
await anim.ready;
scroller.scrollTop = 600;
await waitForNextFrame();
const expectedProgress =
(600 - options.startOffset) / (options.endOffset - options.startOffset);
assert_approx_equals(anim.effect.getComputedTiming().progress,
expectedProgress, 0.001);
}
promise_test(async t => {
await test_range_css(t, {
animation: "anim-1",
startOffset: 250,
endOffset: 750
});
}, 'Scroll timeline with percentage range [CSS]');
promise_test(async t => {
await test_range_css(t, {
animation: "anim-2",
startOffset: 40,
endOffset: 700
});
}, 'Scroll timeline with px range [CSS]');
promise_test(async t => {
await test_range_css(t, {
animation: "anim-3",
startOffset: 340,
endOffset: 680
});
}, 'Scroll timeline with calculated range [CSS]');
promise_test(async t => {
t.add_cleanup(() => {
target.style.fontSize = '';
});
await test_range_css(t, {
animation: "anim-4",
startOffset: 50,
endOffset: 950
});
target.style.fontSize = '20px';
await waitForNextFrame();
const anim = target.getAnimations()[0];
const expectedProgress = (600 - 100) / (900 - 100);
assert_approx_equals(anim.effect.getComputedTiming().progress,
expectedProgress, 0.001);
}, 'Scroll timeline with EM range [CSS]');
</script>