blob: e881d0d676bc3d1612af0677e3d1206e750cab81 [file] [log] [blame]
<!DOCTYPE html>
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-at-rule">
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#scroll-timeline-offset-section">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/web-animations/testcommon.js"></script>
<style>
#scroller {
overflow: scroll;
width: 100px;
height: 100px;
}
#contents {
height: 300px;
}
@keyframes expand {
from { width: 100px; }
to { width: 200px; }
}
@scroll-timeline timeline_0px_100px {
source: selector(#scroller);
time-range: 10s;
start: 0px;
end: 100px;
}
@scroll-timeline timeline_50px_100px {
source: selector(#scroller);
time-range: 10s;
start: 50px;
end: 100px;
}
@scroll-timeline timeline_auto_auto {
source: selector(#scroller);
time-range: 10s;
start: auto;
end: auto;
}
@scroll-timeline timeline_auto_auto_implicit {
source: selector(#scroller);
time-range: 10s;
}
@scroll-timeline timeline_50px_auto {
source: selector(#scroller);
time-range: 10s;
start: 50px;
end: auto;
}
@scroll-timeline timeline_auto_100px {
source: selector(#scroller);
time-range: 10s;
start: auto;
end: 100px;
}
@scroll-timeline timeline_25p_75p {
source: selector(#scroller);
time-range: 10s;
start: 25%;
end: 75%;
}
@scroll-timeline timeline_calc_calc {
source: selector(#scroller);
time-range: 10s;
start: calc(25% + 10px);
end: calc(75% + 10px);
}
#container > div {
width: 0px;
animation-name: expand;
animation-duration: 10s;
animation-timing-function: linear;
}
/* Ensure stable expectations if feature is not supported */
@supports not (animation-timeline:foo) {
#container > div { animation-play-state: paused; }
}
#element_0px_100px { animation-timeline: timeline_0px_100px; }
#element_50px_100px { animation-timeline: timeline_50px_100px; }
#element_auto_auto { animation-timeline: timeline_auto_auto; }
#element_auto_auto_implicit { animation-timeline: timeline_auto_auto_implicit; }
#element_50px_auto { animation-timeline: timeline_50px_auto; }
#element_auto_100px { animation-timeline: timeline_auto_100px; }
#element_25p_75p { animation-timeline: timeline_25p_75p; }
#element_calc_calc { animation-timeline: timeline_calc_calc; }
</style>
<div id=scroller>
<div id=contents></div>
</div>
<div id=container>
<div id=element_0px_100px></div>
<div id=element_50px_100px></div>
<div id=element_auto_auto></div>
<div id=element_auto_auto_implicit></div>
<div id=element_50px_auto></div>
<div id=element_auto_100px></div>
<div id=element_25p_75p></div>
<div id=element_calc_calc></div>
</div>
<script>
// Scrolls top to 'offset', waits for a frame, then call the provided
// assertions function.
function test_scroll(element, offset, assertions, description) {
promise_test(async (t) => {
scroller.scrollTop = offset;
await waitForNextFrame();
assertions();
}, `${description} [${element.id}]`);
}
// Tests that the computed value of 'width' on element is the expected value
// after scrolling top to the specifed offset.
function test_width_at_scroll_top(element, offset, expected) {
test_scroll(element, offset, () => {
assert_equals(getComputedStyle(element).width, expected);
}, `Scroll at offset ${offset} updates animation correctly`);
}
// Tests that the computed value of 'width' on element is (approximately)
// the expected value after scrolling top to the offset specified by
// 'fraction'. The 'fraction' parameter is either a number which maps [0, 1]
// to the full scroll range, or a function which accepts the maximum scroll,
// and returns a specific offset.
function test_approximate_width_at_fraction(element, fraction, expected) {
const max = scroller.scrollHeight - scroller.clientHeight;
const offsetFunction = (typeof fraction == 'function') ? fraction : () => max * fraction;
const resolvedOffset = Math.floor(offsetFunction(max));
test_scroll(element, resolvedOffset, () => {
assert_approx_equals(parseInt(getComputedStyle(element).width), parseInt(expected), 5);
}, `Scroll at offset ${resolvedOffset} updates animation correctly`);
}
// [0px, 100px]
test_width_at_scroll_top(element_0px_100px, 0, '100px');
test_width_at_scroll_top(element_0px_100px, 1, '101px');
test_width_at_scroll_top(element_0px_100px, 50, '150px');
test_width_at_scroll_top(element_0px_100px, 99, '199px');
test_width_at_scroll_top(element_0px_100px, 100, '0px');
test_width_at_scroll_top(element_0px_100px, 101, '0px');
// [50px, 100px]
test_width_at_scroll_top(element_50px_100px, 0, '0px');
test_width_at_scroll_top(element_50px_100px, 1, '0px');
test_width_at_scroll_top(element_50px_100px, 49, '0px');
test_width_at_scroll_top(element_50px_100px, 50, '100px');
test_width_at_scroll_top(element_50px_100px, 51, '102px');
test_width_at_scroll_top(element_50px_100px, 99, '198px');
test_width_at_scroll_top(element_50px_100px, 100, '0px');
test_width_at_scroll_top(element_50px_100px, 101, '0px');
// [auto, auto]
test_approximate_width_at_fraction(element_auto_auto, 0, '100px');
test_approximate_width_at_fraction(element_auto_auto, 0.1, '110px');
test_approximate_width_at_fraction(element_auto_auto, 0.5, '150px');
test_approximate_width_at_fraction(element_auto_auto, 0.9, '190px');
// TODO: Effects for scroll linked animations are not inclusive at max scroll
// https://github.com/w3c/csswg-drafts/issues/5223
test_approximate_width_at_fraction(element_auto_auto, 1, '0px');
// [auto, auto] (implicit)
test_approximate_width_at_fraction(element_auto_auto_implicit, 0, '100px');
test_approximate_width_at_fraction(element_auto_auto_implicit, 0.1, '110px');
test_approximate_width_at_fraction(element_auto_auto_implicit, 0.5, '150px');
test_approximate_width_at_fraction(element_auto_auto_implicit, 0.9, '190px');
// TODO: Effects for scroll linked animations are not inclusive at max scroll
// https://github.com/w3c/csswg-drafts/issues/5223
test_approximate_width_at_fraction(element_auto_auto_implicit, 1, '0px');
// [50px, auto]
{
let offset = (t) => (max => 50 * (1 - t) + max * t);
test_width_at_scroll_top(element_50px_auto, 0, '0px');
test_width_at_scroll_top(element_50px_auto, 49, '0px');
test_width_at_scroll_top(element_50px_auto, 50, '100px');
test_approximate_width_at_fraction(element_50px_auto, offset(0.5), '150px');
test_approximate_width_at_fraction(element_50px_auto, offset(0.9), '190px');
test_approximate_width_at_fraction(element_50px_auto, 1, '0px');
}
// [auto, 100px]
test_width_at_scroll_top(element_auto_100px, 0, '100px');
test_width_at_scroll_top(element_auto_100px, 1, '101px');
test_width_at_scroll_top(element_auto_100px, 50, '150px');
test_width_at_scroll_top(element_auto_100px, 99, '199px');
test_width_at_scroll_top(element_auto_100px, 100, '0px');
// [25%, 75%]
test_approximate_width_at_fraction(element_25p_75p, 0, '0px');
test_approximate_width_at_fraction(element_25p_75p, 0.1, '0px');
test_approximate_width_at_fraction(element_25p_75p, 0.2, '0px');
test_approximate_width_at_fraction(element_25p_75p, 0.35, '120px');
test_approximate_width_at_fraction(element_25p_75p, 0.55, '160px');
test_approximate_width_at_fraction(element_25p_75p, 0.8, '0px');
test_approximate_width_at_fraction(element_25p_75p, 1, '0px');
// [calc(25% + 10px), calc(75% + 10px)]
{
let offset = (t) => (max => (max * 0.25 + 10) * (1 - t) + (max * 0.75 + 10) * t);
test_approximate_width_at_fraction(element_calc_calc, offset(0), '0px');
test_approximate_width_at_fraction(element_calc_calc, offset(0.5), '150px');
test_approximate_width_at_fraction(element_calc_calc, offset(1.1), '0px');
}
</script>