blob: 915aaa1b36b060e0d2fe30fa48705d46138009fd [file] [log] [blame]
<!DOCTYPE html>
<title>The animation-timeline: scroll-timeline-name</title>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1">
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/rewrite#scroll-timelines-named">
<link rel="help" src="https://github.com/w3c/csswg-drafts/issues/6674">
<script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script>
<script src="/web-animations/testcommon.js"></script>
<style>
@keyframes anim {
from { translate: 50px; }
to { translate: 150px; }
}
#target {
width: 100px;
height: 100px;
}
.square {
width: 100px;
height: 100px;
}
.square-container {
width: 300px;
height: 300px;
}
.scroller {
overflow: scroll;
}
.content {
inline-size: 100%;
block-size: 100%;
padding-inline-end: 100px;
padding-block-end: 100px;
}
</style>
<body>
<div id="log"></div>
<script>
"use strict";
function createScrollerAndTarget(t, scrollerSizeClass) {
let scroller = document.createElement('div');
let className = scrollerSizeClass || 'square';
scroller.className = `scroller ${className}`;
let content = document.createElement('div');
content.className = 'content';
scroller.appendChild(content);
let target = document.createElement('div');
target.id = 'target';
t.add_cleanup(function() {
content.remove();
scroller.remove();
target.remove();
});
return [scroller, target];
}
// -------------------------
// Test scroll-timeline-name
// -------------------------
promise_test(async t => {
let target = document.createElement('div');
target.id = 'target';
target.className = 'scroller';
let content = document.createElement('div');
content.className = 'content';
// <div id='target' class='scroller'>
// <div id='content'></div>
// </div>
document.body.appendChild(target);
target.appendChild(content);
target.style.scrollTimelineName = 'timeline';
target.style.animation = "anim 10s linear timeline";
target.scrollTop = 50; // 50%, in [0, 100].
await waitForNextFrame();
assert_equals(getComputedStyle(target).translate, '100px');
content.remove();
target.remove();
}, 'scroll-timeline-name is referenceable in animation-timeline on the ' +
'declaring element itself');
promise_test(async t => {
let [parent, target] = createScrollerAndTarget(t, 'square-container');
// <div id='parent' class='scroller'>
// <div id='target'></div>
// <div id='content'></div>
// </div>
document.body.appendChild(parent);
parent.insertBefore(target, parent.firstElementChild);
parent.style.scrollTimelineName = 'timeline';
target.style.animation = "anim 10s linear timeline";
parent.scrollTop = 100; // 50%, in [0, 200].
await waitForNextFrame();
assert_equals(getComputedStyle(target).translate, '100px');
}, "scroll-timeline-name is referenceable in animation-timeline on that " +
"element's descendants");
promise_test(async t => {
let [sibling, target] = createScrollerAndTarget(t);
// <div id='sibling' class='scroller'> ... </div>
// <div id='target'></div>
document.body.appendChild(sibling);
document.body.appendChild(target);
sibling.style.scrollTimelineName = 'timeline';
target.style.animation = "anim 10s linear timeline";
sibling.scrollTop = 50; // 50%, in [0, 100].
await waitForNextFrame();
assert_equals(getComputedStyle(target).translate, '100px');
}, "scroll-timeline-name is referenceable in animation-timeline on that " +
"element's following siblings");
promise_test(async t => {
let [sibling, target] = createScrollerAndTarget(t);
let parent = document.createElement('div');
// <div id='sibling' class='scroller'> ... </div>
// <div id='parent'>
// <div id='target'></div>
// </div>
document.body.appendChild(sibling);
document.body.appendChild(parent);
parent.appendChild(target);
sibling.style.scrollTimelineName = 'timeline';
target.style.animation = "anim 10s linear timeline";
sibling.scrollTop = 50; // 50%, in [0, 100].
await waitForNextFrame();
assert_equals(getComputedStyle(target).translate, '100px');
parent.remove();
}, "scroll-timeline-name is referenceable in animation-timeline on that " +
"element's following siblings' descendants");
// FIXME: We may use global scope for scroll-timeline-name.
// See https://github.com/w3c/csswg-drafts/issues/7047
promise_test(async t => {
let [sibling, target] = createScrollerAndTarget(t);
// <div id='target'></div>
// <div id='sibling' class='scroller'> ... </div>
document.body.appendChild(target);
document.body.appendChild(sibling);
sibling.style.scrollTimelineName = 'timeline';
target.style.animation = "anim 10s linear timeline";
sibling.scrollTop = 50; // 50%, in [0, 100].
await waitForNextFrame();
assert_equals(getComputedStyle(target).translate, '50px',
'Animation with unknown timeline name holds current time at zero');
}, "scroll-timeline-name is not referenceable in animation-timeline on that " +
"element's previous siblings");
promise_test(async t => {
let [sibling, target] = createScrollerAndTarget(t);
let parent = document.createElement('div');
parent.className = 'scroller square-container';
let content = document.createElement('div');
content.className = 'content';
// <div id='parent' class='scroller'>
// <div id='sibling' class='scroller'> ... </div>
// <div id='target'></div>
// <div id='content'></div>
// </div>
document.body.appendChild(parent);
parent.appendChild(sibling);
parent.appendChild(target);
parent.appendChild(content);
parent.style.scrollTimelineName = 'timeline';
parent.style.scrollTimelineAxis = 'inline';
sibling.style.scrollTimelineName = 'timeline';
target.style.animation = "anim 10s linear timeline";
parent.scrollTop = 50; // 25%, in [0, 200].
sibling.scrollTop = 50; // 50%, in [0, 100].
await waitForNextFrame();
assert_equals(getComputedStyle(target).translate, '100px');
content.remove();
parent.remove();
}, 'scroll-timeline-name is matched based on tree order, which considers ' +
'siblings closer than parents');
promise_test(async t => {
let sibling = document.createElement('div');
sibling.className = 'square';
sibling.style.overflowX = 'clip'; // This makes overflow-y be clip as well.
let target = document.createElement('div');
target.id = 'target';
// <div id='sibling' style='overflow-x: clip'></div>
// <div id='target'></div>
document.body.appendChild(sibling);
document.body.appendChild(target);
sibling.style.scrollTimelineName = 'timeline';
target.style.animation = "anim 10s linear timeline";
sibling.scrollTop = 50; // 50%, in [0, 100].
await waitForNextFrame();
assert_equals(getComputedStyle(target).translate, 'none',
'Animation with an unresolved current time');
target.remove();
sibling.remove();
}, 'scroll-timeline-name on an element which is not a scroll-container');
promise_test(async t => {
let [sibling, target] = createScrollerAndTarget(t);
let main = document.createElement('div');
main.id = 'name';
// <div id='main'>
// <div id='sibling' class='scroller'> ... </div>
// <div id='target'></div>
// </div>
document.body.appendChild(main);
main.appendChild(sibling);
main.appendChild(target);
target.style.animation = 'anim 10s linear timeline';
sibling.scrollTop = 50; // 50%, in [50, 150].
await waitForNextFrame();
// Unknown animation-timeline, current time held at zero.
assert_equals(getComputedStyle(target).translate, '50px');
// Ensure that #main (an ancestor of the scroller) needs style recalc.
main.style.background = 'lightgray';
sibling.style.scrollTimelineName = 'timeline';
assert_equals(getComputedStyle(target).translate, '100px');
main.remove();
}, 'scroll-timeline-name affects subsequent siblings when changed');
// TODO: Add more tests which change scroll-timeline-name property.
// Those animations which use this timeline should be restyled propertly.
// -------------------------
// Test scroll-timeline-axis
// -------------------------
promise_test(async t => {
let [scroller, target] = createScrollerAndTarget(t);
scroller.style.writingMode = 'vertical-lr';
document.body.appendChild(scroller);
document.body.appendChild(target);
scroller.style.scrollTimeline = 'timeline block';
target.style.animation = "anim 10s linear timeline";
scroller.scrollLeft = 50;
await waitForNextFrame();
assert_equals(getComputedStyle(target).translate, '100px');
}, 'scroll-timeline-axis is block');
promise_test(async t => {
let [scroller, target] = createScrollerAndTarget(t);
scroller.style.writingMode = 'vertical-lr';
document.body.appendChild(scroller);
document.body.appendChild(target);
scroller.style.scrollTimeline = 'timeline inline';
target.style.animation = "anim 10s linear timeline";
scroller.scrollTop = 50;
await waitForNextFrame();
assert_equals(getComputedStyle(target).translate, '100px');
}, 'scroll-timeline-axis is inline');
promise_test(async t => {
let [scroller, target] = createScrollerAndTarget(t);
scroller.style.writingMode = 'vertical-lr';
document.body.appendChild(scroller);
document.body.appendChild(target);
scroller.style.scrollTimeline = 'timeline horizontal';
target.style.animation = "anim 10s linear timeline";
scroller.scrollLeft = 50;
await waitForNextFrame();
assert_equals(getComputedStyle(target).translate, '100px');
}, 'scroll-timeline-axis is horizontal');
promise_test(async t => {
let [scroller, target] = createScrollerAndTarget(t);
scroller.style.writingMode = 'vertical-lr';
document.body.appendChild(scroller);
document.body.appendChild(target);
scroller.style.scrollTimeline = 'timeline vertical';
target.style.animation = "anim 10s linear timeline";
scroller.scrollTop = 50;
await waitForNextFrame();
assert_equals(getComputedStyle(target).translate, '100px');
}, 'scroll-timeline-axis is vertical');
// TODO: Add more tests which change scroll-timeline-axis property.
// Those animations which use this timeline should be restyled properly.
</script>
</body>