testing/web-platform/tests/scroll-animations/css/animation-timeline-scroll-functional-notation.tentative.html
author Florian Quèze <florian@queze.net>
Tue, 08 Jul 2025 20:48:53 +0000 (10 hours ago)
changeset 795809 b01190f141a12f4b506acb1f4476561c4b0f2407
parent 667122 bad4d3d66de56f4f974fe096eddd0f099a89874c
permissions -rw-r--r--
Bug 1960567 - remove the last C++ and scriptable APIs to accumulate data to legacy telemetry histograms, r=chutten. Differential Revision: https://phabricator.services.mozilla.com/D255582
<!DOCTYPE html>
<title>The animation-timeline: scroll() notation</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-notation">
<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>
<script src="support/testcommon.js"></script>
<style>
  @keyframes anim {
    from { translate: 50px; }
    to { translate: 150px; }
  }
  html {
    min-height: 100vh;
    /* This makes the max scrollable ragne be 100px in root element */
    padding-bottom: 100px;
  }
  #container {
    width: 300px;
    height: 300px;
    overflow: scroll;
  }
  #target {
    width: 100px;
    /* This makes the max scrollable ragne be 100px in the block direction */
    height: 100px;
  }
  /* large block content */
  .block-content {
    block-size: 100%;
  }
  /* large inline content */
  .inline-content {
    inline-size: 100%;
    block-size: 5px;
    /* This makes the max scrollable ragne be 100px in the inline direction */
    padding-inline-end: 100px;
  }
</style>
<body>
<div id="log"></div>
<script>
"use strict";

setup(assert_implements_animation_timeline);

const root = document.scrollingElement;
const createTargetWithStuff = function(t, contentClass) {
  let container = document.createElement('div');
  container.id = 'container';
  let target = document.createElement('div');
  target.id = 'target';
  let content = document.createElement('div');
  content.className = contentClass;

  // <div id='container'>
  //   <div id='target'></div>
  //   <div class=contentClass></div>
  // </div>
  document.body.appendChild(container);
  container.appendChild(target);
  container.appendChild(content);

  if (t && typeof t.add_cleanup === 'function') {
    t.add_cleanup(() => {
      content.remove();
      target.remove();
      container.remove();
    });
  }

  return [container, target];
};

async function scrollLeft(element, value) {
  element.scrollLeft = value;
  await waitForNextFrame();
}

async function scrollTop(element, value) {
  element.scrollTop = value;
  await waitForNextFrame();
}

promise_test(async t => {
  let [container, div] = createTargetWithStuff(t, 'block-content');
  await runAndWaitForFrameUpdate(() => {
    div.style.animation = "anim 10s linear";
    div.style.animationTimeline = "scroll(nearest)";
  });

  await scrollTop(root, 50);
  assert_equals(getComputedStyle(div).translate, '50px');

  await scrollTop(container, 50);
  assert_equals(getComputedStyle(div).translate, '100px');

  await scrollTop(root, 0);
}, 'animation-timeline: scroll(nearest)');

promise_test(async t => {
  let [container, div] = createTargetWithStuff(t, 'block-content');
  await runAndWaitForFrameUpdate(() => {
    div.style.animation = "anim 10s linear";
    div.style.animationTimeline = "scroll(root)";
  });

  await scrollTop(container, 50);
  assert_equals(getComputedStyle(div).translate, '50px');

  await scrollTop(root, 50);
  assert_equals(getComputedStyle(div).translate, '100px');

  await scrollTop(root, 0);
}, 'animation-timeline: scroll(root)');

promise_test(async t => {
  let [container, div] = createTargetWithStuff(t, 'block-content');
  await runAndWaitForFrameUpdate(() => {
    container.style.animation = "anim 10s linear";
    container.style.animationTimeline = "scroll(self)";
  });
  await scrollTop(container, 50);
  assert_equals(getComputedStyle(container).translate, '100px');
}, 'animation-timeline: scroll(self)');

promise_test(async t => {
  let [container, div] = createTargetWithStuff(t, 'block-content');
  await runAndWaitForFrameUpdate(() => {
    div.style.animation = "anim 10s linear";
    div.style.animationTimeline = "scroll(self)";
  });
  await scrollTop(container, 50);
  assert_equals(getComputedStyle(div).translate, 'none');
}, 'animation-timeline: scroll(self), on non-scroller');

promise_test(async t => {
  let [container, div] = createTargetWithStuff(t, 'inline-content');
  await runAndWaitForFrameUpdate(() => {
    div.style.animation = "anim 10s linear";
    div.style.animationTimeline = "scroll(inline)";
  });
  await scrollLeft(container, 50);
  assert_equals(getComputedStyle(div).translate, '100px');
}, 'animation-timeline: scroll(inline)');

promise_test(async t => {
  let [container, div] = createTargetWithStuff(t, 'block-content');
  await runAndWaitForFrameUpdate(() => {
    container.style.writingMode = 'vertical-lr';
    div.style.animation = "anim 10s linear";
    div.style.animationTimeline = "scroll(x)";
  });

  await scrollLeft(container, 50);
  assert_equals(getComputedStyle(div).translate, '100px');
}, 'animation-timeline: scroll(x)');

promise_test(async t => {
  let [container, div] = createTargetWithStuff(t, 'inline-content');
  await runAndWaitForFrameUpdate(() => {
    container.style.writingMode = 'vertical-lr';
    div.style.animation = "anim 10s linear";
    div.style.animationTimeline = "scroll(y)";
  });

  await scrollTop(container, 50);
  assert_equals(getComputedStyle(div).translate, '100px');
}, 'animation-timeline: scroll(y)');

// TODO: Add more tests which change the overflow property of the container for
// scroll(nearest)

</script>
</body>