264 lines
8.9 KiB
HTML
264 lines
8.9 KiB
HTML
<!DOCTYPE html>
|
|
<html>
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#named-timeline-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>
|
|
<title>Animation range and delay</title>
|
|
</head>
|
|
<style type="text/css">
|
|
#scroller {
|
|
border: 10px solid lightgray;
|
|
overflow-y: scroll;
|
|
overflow-x: hidden;
|
|
width: 300px;
|
|
height: 200px;
|
|
}
|
|
#target {
|
|
margin: 800px 10px;
|
|
width: 100px;
|
|
height: 100px;
|
|
z-index: -1;
|
|
background-color: green;
|
|
}
|
|
</style>
|
|
<body>
|
|
<div id=scroller>
|
|
<div id=target></div>
|
|
</div>
|
|
</body>
|
|
<script type="text/javascript">
|
|
async function runTest() {
|
|
function assert_progress_equals(anim, expected, errorMessage) {
|
|
assert_approx_equals(
|
|
anim.effect.getComputedTiming().progress,
|
|
expected, 1e-6, errorMessage);
|
|
}
|
|
|
|
function assert_opacity_equals(expected, errorMessage) {
|
|
assert_approx_equals(
|
|
parseFloat(getComputedStyle(target).opacity), expected, 1e-6,
|
|
errorMessage);
|
|
}
|
|
|
|
async function runTimelineOffsetsInKeyframesTest(keyframes) {
|
|
const testcase = JSON.stringify(keyframes);
|
|
const anim = target.animate(keyframes, {
|
|
timeline: new ViewTimeline( { subject: target }),
|
|
rangeStart: { rangeName: 'contain', offset: CSS.percent(0) },
|
|
rangeEnd: { rangeName: 'contain', offset: CSS.percent(100) },
|
|
duration: 'auto', fill: 'both'
|
|
});
|
|
await anim.ready;
|
|
await waitForNextFrame();
|
|
|
|
// @ contain 0%
|
|
scroller.scrollTop = 700;
|
|
await waitForNextFrame();
|
|
|
|
assert_progress_equals(
|
|
anim, 0, `Testcase '${testcase}': progress at contain 0%`);
|
|
assert_opacity_equals(
|
|
1/3, `Testcase '${testcase}': opacity at contain 0%`);
|
|
|
|
// @ contain 50%
|
|
scroller.scrollTop = 750;
|
|
await waitForNextFrame();
|
|
assert_progress_equals(
|
|
anim, 0.5, `Testcase '${testcase}': progress at contain 50%`);
|
|
assert_opacity_equals(
|
|
0.5, `Testcase '${testcase}': opacity at contain 50%`);
|
|
|
|
// @ contain 100%
|
|
scroller.scrollTop = 800;
|
|
await waitForNextFrame();
|
|
assert_progress_equals(
|
|
anim, 1, `Testcase '${testcase}': progress at contain 100%`);
|
|
assert_opacity_equals(
|
|
2/3, `Testcase '${testcase}': opacity at contain 100%`);
|
|
anim.cancel();
|
|
}
|
|
|
|
async function runParseNumberOrPercentInKeyframesTest(keyframes) {
|
|
const anim = target.animate(keyframes, {
|
|
timeline: new ViewTimeline( { subject: target }),
|
|
rangeStart: { rangeName: 'contain', offset: CSS.percent(0) },
|
|
rangeEnd: { rangeName: 'contain', offset: CSS.percent(100) },
|
|
duration: 'auto', fill: 'both'
|
|
});
|
|
await anim.ready;
|
|
await waitForNextFrame();
|
|
|
|
const maxScroll = scroller.scrollHeight - scroller.clientHeight;
|
|
scroller.scrollTop = maxScroll / 2;
|
|
await waitForNextFrame();
|
|
|
|
const testcase = JSON.stringify(keyframes);
|
|
assert_progress_equals(anim, 0.5, testcase);
|
|
assert_opacity_equals(0.5, testcase);
|
|
anim.cancel();
|
|
}
|
|
|
|
async function runInvalidKeyframesTest(keyframes) {
|
|
assert_throws_js(TypeError, () => {
|
|
target.animate(keyframes, {
|
|
timeline: new ViewTimeline( { subject: target }),
|
|
});
|
|
}, `Invalid keyframes test case "${JSON.stringify(keyframes)}"`);
|
|
}
|
|
|
|
promise_test(async t => {
|
|
// Test equivalent typed-OM and CSS representations of timeline offsets.
|
|
// Test array and object form for keyframes.
|
|
const keyframeTests = [
|
|
// BaseKeyframe form with offsets expressed as typed-OM.
|
|
[
|
|
{
|
|
offset: { rangeName: 'cover', offset: CSS.percent(0) },
|
|
opacity: 0
|
|
},
|
|
{
|
|
offset: { rangeName: 'cover', offset: CSS.percent(100) },
|
|
opacity: 1
|
|
}
|
|
],
|
|
// BaseKeyframe form with offsets expressed as CSS text.
|
|
[
|
|
{ offset: "cover 0%", opacity: 0 },
|
|
{ offset: "cover 100%", opacity: 1 }
|
|
],
|
|
// BasePropertyIndexedKeyframe form with offsets expressed as typed-OM.
|
|
{
|
|
opacity: [0, 1],
|
|
offset: [
|
|
{ rangeName: 'cover', offset: CSS.percent(0) },
|
|
{ rangeName: 'cover', offset: CSS.percent(100) }
|
|
]
|
|
},
|
|
// BasePropertyIndexedKeyframe form with offsets expressed as CSS text.
|
|
{ opacity: [0, 1], offset: [ "cover 0%", "cover 100%" ]}
|
|
];
|
|
|
|
for (let i = 0; i < keyframeTests.length; i++) {
|
|
await runTimelineOffsetsInKeyframesTest(keyframeTests[i]);
|
|
}
|
|
|
|
}, 'Timeline offsets in programmatic keyframes');
|
|
|
|
promise_test(async t => {
|
|
const keyframeTests = [
|
|
[{offset: "0.5", opacity: 0.5 }],
|
|
[{offset: "50%", opacity: 0.5 }],
|
|
[{offset: "calc(20% + 30%)", opacity: 0.5 }]
|
|
];
|
|
|
|
for (let i = 0; i < keyframeTests.length; i++) {
|
|
await runParseNumberOrPercentInKeyframesTest(keyframeTests[i]);
|
|
}
|
|
|
|
}, 'String offsets in programmatic keyframes');
|
|
|
|
promise_test(async t => {
|
|
const invalidKeyframeTests = [
|
|
// BasePropertyKefyrame:
|
|
[{ offset: { rangeName: 'somewhere', offset: CSS.percent(0) }}],
|
|
[{ offset: { rangeName: 'entry', offset: CSS.px(0) }}],
|
|
[{ offset: "here 0%" }],
|
|
[{ offset: "entry 3px" }],
|
|
// BasePropertyIndexedKeyframe with sequence:
|
|
{ offset: [{ rangeName: 'somewhere', offset: CSS.percent(0) }]},
|
|
{ offset: [{ rangeName: 'entry', offset: CSS.px(0) }]},
|
|
{ offset: ["here 0%"] },
|
|
{ offset: ["entry 3px" ]},
|
|
// BasePropertyIndexedKeyframe without sequence:
|
|
{ offset: { rangeName: 'somewhere', offset: CSS.percent(0) }},
|
|
{ offset: { rangeName: 'entry', offset: CSS.px(0) }},
|
|
{ offset: "here 0%" },
|
|
{ offset: "entry 3px" },
|
|
// <number> or <percent> as string:
|
|
[{ offset: "-1" }],
|
|
[{ offset: "2" }],
|
|
[{ offset: "-10%" }],
|
|
[{ offset: "110%" }],
|
|
{ offset: ["-1"], opacity: [0.5] },
|
|
{ offset: ["2"], opacity: [0.5] },
|
|
{ offset: "-1", opacity: 0.5 },
|
|
{ offset: "2", opacity: 0.5 },
|
|
// Extra stuff at the end.
|
|
[{ offset: "0.5 trailing nonsense" }],
|
|
[{ offset: "cover 50% eureka" }]
|
|
];
|
|
for( let i = 0; i < invalidKeyframeTests.length; i++) {
|
|
await runInvalidKeyframesTest(invalidKeyframeTests[i]);
|
|
}
|
|
}, 'Invalid timeline offset in programmatic keyframe throws');
|
|
|
|
|
|
promise_test(async t => {
|
|
const anim = target.animate([
|
|
{ offset: "cover 0%", opacity: 0 },
|
|
{ offset: "cover 100%", opacity: 1 }
|
|
], {
|
|
rangeStart: { rangeName: 'contain', offset: CSS.percent(0) },
|
|
rangeEnd: { rangeName: 'contain', offset: CSS.percent(100) },
|
|
duration: 10000, fill: 'both'
|
|
});
|
|
|
|
scroller.scrollTop = 750;
|
|
|
|
await anim.ready;
|
|
assert_opacity_equals(1, `Opacity with document timeline`);
|
|
|
|
anim.timeline = new ViewTimeline( { subject: target });
|
|
await anim.ready;
|
|
|
|
assert_progress_equals(anim, 0.5, `Progress at contain 50%`);
|
|
assert_opacity_equals(0.5, `Opacity at contain 50%`);
|
|
|
|
anim.timeline = document.timeline;
|
|
assert_false(anim.pending);
|
|
await waitForNextFrame();
|
|
assert_opacity_equals(1, `Opacity after resetting timeline`);
|
|
|
|
anim.cancel();
|
|
}, 'Timeline offsets in programmatic keyframes adjust for change in ' +
|
|
'timeline');
|
|
|
|
promise_test(async t => {
|
|
const anim = target.animate([], {
|
|
timeline: new ViewTimeline( { subject: target }),
|
|
rangeStart: { rangeName: 'contain', offset: CSS.percent(0) },
|
|
rangeEnd: { rangeName: 'contain', offset: CSS.percent(100) },
|
|
duration: 'auto', fill: 'both'
|
|
});
|
|
|
|
await anim.ready;
|
|
await waitForNextFrame();
|
|
|
|
scroller.scrollTop = 750;
|
|
await waitForNextFrame();
|
|
assert_progress_equals(
|
|
anim, 0.5, `Progress at contain 50% before effect change`);
|
|
assert_opacity_equals(1, `Opacity at contain 50% before effect change`);
|
|
|
|
anim.effect = new KeyframeEffect(target, [
|
|
{ offset: "cover 0%", opacity: 0 },
|
|
{ offset: "cover 100%", opacity: 1 }
|
|
], { duration: 'auto', fill: 'both' });
|
|
await waitForNextFrame();
|
|
assert_progress_equals(
|
|
anim, 0.5, `Progress at contain 50% after effect change`);
|
|
assert_opacity_equals(0.5, `Opacity at contain 50% after effect change`);
|
|
}, 'Timeline offsets in programmatic keyframes resolved when updating ' +
|
|
'the animation effect');
|
|
}
|
|
|
|
// TODO(kevers): Add tests for getKeyframes once
|
|
// https://github.com/w3c/csswg-drafts/issues/8507 is resolved.
|
|
|
|
window.onload = runTest;
|
|
</script>
|
|
</html>
|