[scroll-animations] Implement insets in ViewTimeline (minus auto)
This CL implements the functionality behind view-timeline-inset, except
that 'auto' just returns zero for now. It will be implemented in a future
CL.
Bug: 1344151
Change-Id: I500ba8724df729ecfcb2edaabfc60fba73ad2da6
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/3891062
Reviewed-by: Kevin Ellis <kevers@chromium.org>
Commit-Queue: Anders Hartvoll Ruud <andruud@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1048023}
diff --git a/scroll-animations/css/view-timeline-inset-animation.html b/scroll-animations/css/view-timeline-inset-animation.html
new file mode 100644
index 0000000..82bfb93
--- /dev/null
+++ b/scroll-animations/css/view-timeline-inset-animation.html
@@ -0,0 +1,236 @@
+<!DOCTYPE html>
+<title>Animations using view-timeline-inset</title>
+<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#propdef-view-timeline-inset">
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="/web-animations/testcommon.js"></script>
+<style>
+ @keyframes anim {
+ from { z-index: 0; }
+ to { z-index: 100; }
+ }
+ #scroller {
+ overflow: hidden;
+ width: 80px;
+ height: 100px;
+ }
+ #target {
+ margin: 150px;
+ width: 50px;
+ height: 50px;
+ z-index: -1;
+ }
+</style>
+<main id=main></main>
+<script>
+ function inflate(t, template) {
+ t.add_cleanup(() => main.replaceChildren());
+ main.append(template.content.cloneNode(true));
+ }
+ async function scrollTop(e, value) {
+ e.scrollTop = value;
+ await waitForNextFrame();
+ }
+ async function scrollLeft(e, value) {
+ e.scrollLeft = value;
+ await waitForNextFrame();
+ }
+ async function assertValueAt(scroller, target, args) {
+ if (args.scrollTop !== undefined)
+ await scrollTop(scroller, args.scrollTop);
+ if (args.scrollLeft !== undefined)
+ await scrollLeft(scroller, args.scrollLeft);
+ assert_equals(getComputedStyle(target).zIndex, args.expected.toString());
+ }
+</script>
+
+<!--
+ The scroller is 80x100px.
+ The target is 50x50px with a 150px margin.
+-->
+
+<template id=test_one_value>
+ <style>
+ #target {
+ view-timeline: t1;
+ view-timeline-inset: 10px;
+ animation: anim 1s linear t1;
+ }
+ </style>
+ <div id=scroller class=vertical>
+ <div id=target></div>
+ </div>
+</template>
+<script>
+ promise_test(async (t) => {
+ inflate(t, test_one_value);
+ await assertValueAt(scroller, target, { scrollTop:50, expected:-1 });
+ await assertValueAt(scroller, target, { scrollTop:50 + 10, expected:0 }); // 0%
+ await assertValueAt(scroller, target, { scrollTop:125, expected:50 }); // 50%
+ await assertValueAt(scroller, target, { scrollTop:200 - 10, expected:100 }); // 100%
+ await assertValueAt(scroller, target, { scrollTop:200, expected:-1 });
+ }, 'view-timeline-inset with one value');
+</script>
+
+<template id=test_two_values>
+ <style>
+ #target {
+ view-timeline: t1;
+ view-timeline-inset: 10px 20px;
+ animation: anim 1s linear t1;
+ }
+ </style>
+ <div id=scroller class=vertical>
+ <div id=target></div>
+ </div>
+</template>
+<script>
+ promise_test(async (t) => {
+ inflate(t, test_two_values);
+ await assertValueAt(scroller, target, { scrollTop:50, expected:-1 });
+ await assertValueAt(scroller, target, { scrollTop:50 + 20, expected:0 }); // 0%
+ await assertValueAt(scroller, target, { scrollTop:130, expected:50 }); // 50%
+ await assertValueAt(scroller, target, { scrollTop:200 - 10, expected:100 }); // 100%
+ await assertValueAt(scroller, target, { scrollTop:200, expected:-1 });
+ }, 'view-timeline-inset with two values');
+</script>
+
+<template id=test_em_values>
+ <style>
+ #target {
+ font-size: 10px;
+ view-timeline: t1;
+ view-timeline-inset: 10px 2em;
+ animation: anim 1s linear t1;
+ }
+ </style>
+ <div id=scroller class=vertical>
+ <div id=target></div>
+ </div>
+</template>
+<script>
+ promise_test(async (t) => {
+ inflate(t, test_em_values);
+ await assertValueAt(scroller, target, { scrollTop:50, expected:-1 });
+ await assertValueAt(scroller, target, { scrollTop:50 + 20, expected:0 }); // 0%
+ await assertValueAt(scroller, target, { scrollTop:130, expected:50 }); // 50%
+ await assertValueAt(scroller, target, { scrollTop:200 - 10, expected:100 }); // 100%
+ await assertValueAt(scroller, target, { scrollTop:200, expected:-1 });
+ }, 'view-timeline-inset with em values');
+</script>
+
+<template id=test_percentage_values>
+ <style>
+ #target {
+ font-size: 10px;
+ view-timeline: t1;
+ view-timeline-inset: calc(5px + max(1%, 5%)) 20%;
+ animation: anim 1s linear t1;
+ }
+ </style>
+ <div id=scroller class=vertical>
+ <div id=target></div>
+ </div>
+</template>
+<script>
+ promise_test(async (t) => {
+ inflate(t, test_percentage_values);
+ await assertValueAt(scroller, target, { scrollTop:50, expected:-1 });
+ await assertValueAt(scroller, target, { scrollTop:50 + 20, expected:0 }); // 0%
+ await assertValueAt(scroller, target, { scrollTop:130, expected:50 }); // 50%
+ await assertValueAt(scroller, target, { scrollTop:200 - 10, expected:100 }); // 100%
+ await assertValueAt(scroller, target, { scrollTop:200, expected:-1 });
+ }, 'view-timeline-inset with percentage values');
+</script>
+
+<template id=test_outset>
+ <style>
+ #target {
+ view-timeline: t1;
+ view-timeline-inset: -10px -20px;
+ animation: anim 1s linear t1;
+ }
+ </style>
+ <div id=scroller class=vertical>
+ <div id=target></div>
+ </div>
+</template>
+<script>
+ promise_test(async (t) => {
+ inflate(t, test_outset);
+ await assertValueAt(scroller, target, { scrollTop:20, expected:-1 });
+ await assertValueAt(scroller, target, { scrollTop:50 - 20, expected:0 }); // 0%
+ await assertValueAt(scroller, target, { scrollTop:120, expected:50 }); // 50%
+ await assertValueAt(scroller, target, { scrollTop:200 + 10, expected:100 }); // 100%
+ await assertValueAt(scroller, target, { scrollTop:220, expected:-1 });
+ }, 'view-timeline-inset with negative values');
+</script>
+
+<template id=test_horizontal>
+ <style>
+ #target {
+ view-timeline: t1 horizontal;
+ view-timeline-inset: 10px 20px;
+ animation: anim 1s linear t1;
+ }
+ </style>
+ <div id=scroller>
+ <div id=target></div>
+ </div>
+</template>
+<script>
+ promise_test(async (t) => {
+ inflate(t, test_horizontal);
+ await assertValueAt(scroller, target, { scrollLeft:20, expected:-1 });
+ await assertValueAt(scroller, target, { scrollLeft:70 + 20, expected:0 }); // 0%
+ await assertValueAt(scroller, target, { scrollLeft:140, expected:50 }); // 50%
+ await assertValueAt(scroller, target, { scrollLeft:200 - 10, expected:100 }); // 100%
+ await assertValueAt(scroller, target, { scrollLeft:200, expected:-1 });
+ }, 'view-timeline-inset with horizontal scroller');
+</script>
+
+<template id=test_block>
+ <style>
+ #target {
+ view-timeline: t1 block;
+ view-timeline-inset: 10px 20px;
+ animation: anim 1s linear t1;
+ }
+ </style>
+ <div id=scroller>
+ <div id=target></div>
+ </div>
+</template>
+<script>
+ promise_test(async (t) => {
+ inflate(t, test_block);
+ await assertValueAt(scroller, target, { scrollTop:50, expected:-1 });
+ await assertValueAt(scroller, target, { scrollTop:50 + 20, expected:0 }); // 0%
+ await assertValueAt(scroller, target, { scrollTop:130, expected:50 }); // 50%
+ await assertValueAt(scroller, target, { scrollTop:200 - 10, expected:100 }); // 100%
+ await assertValueAt(scroller, target, { scrollTop:200, expected:-1 });
+ }, 'view-timeline-inset with block scroller');
+</script>
+
+<template id=test_inline>
+ <style>
+ #target {
+ view-timeline: t1 inline;
+ view-timeline-inset: 10px 20px;
+ animation: anim 1s linear t1;
+ }
+ </style>
+ <div id=scroller>
+ <div id=target></div>
+ </div>
+</template>
+<script>
+ promise_test(async (t) => {
+ inflate(t, test_inline);
+ await assertValueAt(scroller, target, { scrollLeft:20, expected:-1 });
+ await assertValueAt(scroller, target, { scrollLeft:70 + 20, expected:0 }); // 0%
+ await assertValueAt(scroller, target, { scrollLeft:140, expected:50 }); // 50%
+ await assertValueAt(scroller, target, { scrollLeft:200 - 10, expected:100 }); // 100%
+ await assertValueAt(scroller, target, { scrollLeft:200, expected:-1 });
+ }, 'view-timeline-inset with inline scroller');
+</script>
diff --git a/scroll-animations/css/view-timeline-inset-computed.html b/scroll-animations/css/view-timeline-inset-computed.html
index 9cc272a..77683a4 100644
--- a/scroll-animations/css/view-timeline-inset-computed.html
+++ b/scroll-animations/css/view-timeline-inset-computed.html
@@ -18,6 +18,8 @@
test_computed_value('view-timeline-inset', 'unset', '0px');
test_computed_value('view-timeline-inset', 'revert', '0px');
test_computed_value('view-timeline-inset', '1px');
+test_computed_value('view-timeline-inset', '1%');
+test_computed_value('view-timeline-inset', 'calc(1% + 1px)');
test_computed_value('view-timeline-inset', '1px 2px');
test_computed_value('view-timeline-inset', '1px 2em', '1px 20px');
test_computed_value('view-timeline-inset', 'calc(1px + 1em) 2px', '11px 2px');
diff --git a/scroll-animations/css/view-timeline-used-values.html b/scroll-animations/css/view-timeline-used-values.html
index 883c062..cf32777 100644
--- a/scroll-animations/css/view-timeline-used-values.html
+++ b/scroll-animations/css/view-timeline-used-values.html
@@ -68,3 +68,32 @@
assert_equals(getComputedStyle(target).zIndex, '35');
}, 'Use the last value from view-timeline-axis if omitted');
</script>
+
+<template id=omitted_inset>
+ <style>
+ #target {
+ view-timeline-name: t1, t2; /* Two items */
+ view-timeline-inset: 100px; /* One item */
+ animation: anim 1s linear t2;
+ }
+ </style>
+ <div id=scroller class=scroller>
+ <div id=target></div>
+ </div>
+</template>
+<script>
+ promise_test(async (t) => {
+ inflate(t, omitted_inset);
+ assert_equals(getComputedStyle(target).zIndex, '-1');
+
+ // 0% is normally at at scrollTop = -100
+ // 100% is normally at scrollTop/Left = 300
+ // However, we have a 100px inset in both ends, which makes the
+ // range [0, 200].
+
+ await scrollTop(scroller, 0);
+ assert_equals(getComputedStyle(target).zIndex, '0');
+ await scrollTop(scroller, 100); // 50%
+ assert_equals(getComputedStyle(target).zIndex, '50');
+ }, 'Use the last value from view-timeline-inset if omitted');
+</script>