tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

animation-timeline-named-scroll-progress-timeline.tentative.html (13910B)


      1 <!DOCTYPE html>
      2 <title>The animation-timeline: scroll-timeline-name</title>
      3 <meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1">
      4 <link rel="help" src="https://drafts.csswg.org/scroll-animations-1/rewrite#scroll-timelines-named">
      5 <link rel="help" src="https://github.com/w3c/csswg-drafts/issues/6674">
      6 <script src="/resources/testharness.js"></script>
      7 <script src="/resources/testharnessreport.js"></script>
      8 <script src="/web-animations/testcommon.js"></script>
      9 <script src="support/testcommon.js"></script>
     10 <script src="/scroll-animations/scroll-timelines/testcommon.js"></script>
     11 <style>
     12  @keyframes anim {
     13    from { translate: 50px; }
     14    to { translate: 150px; }
     15  }
     16  @keyframes anim-2 {
     17    from { z-index: 0; }
     18    to { z-index: 100; }
     19  }
     20 
     21  #target {
     22    width: 100px;
     23    height: 100px;
     24  }
     25  .square {
     26    width: 100px;
     27    height: 100px;
     28  }
     29  .square-container {
     30    width: 300px;
     31    height: 300px;
     32  }
     33  .scroller {
     34    overflow: scroll;
     35  }
     36  .content {
     37    inline-size: 100%;
     38    block-size: 100%;
     39    padding-inline-end: 100px;
     40    padding-block-end: 100px;
     41  }
     42 </style>
     43 <body>
     44 <div id="log"></div>
     45 <script>
     46 "use strict";
     47 
     48 setup(assert_implements_animation_timeline);
     49 
     50 function createScroller(t, scrollerSizeClass) {
     51  let scroller = document.createElement('div');
     52  let className = scrollerSizeClass || 'square';
     53  scroller.className = `scroller ${className}`;
     54  let content = document.createElement('div');
     55  content.className = 'content';
     56 
     57  scroller.appendChild(content);
     58 
     59  t.add_cleanup(function() {
     60    content.remove();
     61    scroller.remove();
     62  });
     63 
     64  return scroller;
     65 }
     66 
     67 function createTarget(t) {
     68  let target = document.createElement('div');
     69  target.id = 'target';
     70 
     71  t.add_cleanup(function() {
     72    target.remove();
     73  });
     74 
     75  return target;
     76 }
     77 
     78 function createScrollerAndTarget(t, scrollerSizeClass) {
     79  return [createScroller(t, scrollerSizeClass), createTarget(t)];
     80 }
     81 
     82 async function waitForScrollTop(scroller, percentage) {
     83  const maxScroll = scroller.scrollHeight - scroller.clientHeight;
     84  scroller.scrollTop = maxScroll * percentage / 100;
     85  return waitForNextFrame();
     86 }
     87 
     88 async function waitForScrollLeft(scroller, percentage) {
     89  const maxScroll = scroller.scrollWidth - scroller.clientWidth;
     90  scroller.scrollLeft = maxScroll * percentage / 100;
     91  return waitForNextFrame();
     92 }
     93 
     94 // -------------------------
     95 // Test scroll-timeline-name
     96 // -------------------------
     97 
     98 promise_test(async t => {
     99  let target = document.createElement('div');
    100  target.id = 'target';
    101  target.className = 'scroller';
    102  let content = document.createElement('div');
    103  content.className = 'content';
    104 
    105  await runAndWaitForFrameUpdate(() => {
    106    // <div id='target' class='scroller'>
    107    //   <div id='content'></div>
    108    // </div>
    109    document.body.appendChild(target);
    110    target.appendChild(content);
    111 
    112    target.style.scrollTimelineName = '--timeline';
    113    target.style.animation = "anim 10s linear";
    114    target.style.animationTimeline = '--timeline';
    115    target.scrollTop = 50; // 50%
    116  });
    117 
    118  assert_equals(getComputedStyle(target).translate, '100px');
    119 
    120  content.remove();
    121  target.remove();
    122 }, 'scroll-timeline-name is referenceable in animation-timeline on the ' +
    123   'declaring element itself');
    124 
    125 promise_test(async t => {
    126  let [parent, target] = createScrollerAndTarget(t, 'square-container');
    127 
    128  await runAndWaitForFrameUpdate(() => {
    129    // <div id='parent' class='scroller'>
    130    //   <div id='target'></div>
    131    //   <div id='content'></div>
    132    // </div>
    133    document.body.appendChild(parent);
    134    parent.insertBefore(target, parent.firstElementChild);
    135 
    136    parent.style.scrollTimelineName = '--timeline';
    137    target.style.animation = "anim 10s linear";
    138    target.style.animationTimeline = '--timeline';
    139 
    140    parent.scrollTop = 100; // 50%
    141  });
    142 
    143  assert_equals(getComputedStyle(target).translate, '100px');
    144 }, "scroll-timeline-name is referenceable in animation-timeline on that " +
    145   "element's descendants");
    146 
    147 // See https://github.com/w3c/csswg-drafts/issues/7047
    148 promise_test(async t => {
    149  let [sibling, target] = createScrollerAndTarget(t);
    150 
    151  await runAndWaitForFrameUpdate(() => {
    152    // <div id='sibling' class='scroller'> ... </div>
    153    // <div id='target'></div>
    154    document.body.appendChild(sibling);
    155    document.body.appendChild(target);
    156 
    157    // Resolvable if using a deferred timeline, but otherwise can only resolve
    158    // if an ancestor container of the target element.
    159    sibling.style.scrollTimelineName = '--timeline';
    160    target.style.animation = "anim 10s linear";
    161    target.style.animationTimeline = '--timeline';
    162 
    163    sibling.scrollTop = 50; // 50%
    164  });
    165 
    166  assert_equals(getComputedStyle(target).translate, '50px',
    167    'Animation with unknown timeline name holds current time at zero');
    168 }, "scroll-timeline-name is not referenceable in animation-timeline on that " +
    169   "element's siblings");
    170 
    171 promise_test(async t => {
    172  let parent = document.createElement('div');
    173  parent.className = 'square';
    174  parent.style.overflowX = 'clip'; // This makes overflow-y be clip as well.
    175  let target = document.createElement('div');
    176  target.id = 'target';
    177 
    178  await runAndWaitForFrameUpdate(() => {
    179    // <div id='parent' style='overflow-x: clip'>...
    180    //   <div id='target'></div>
    181    // </div>
    182    document.body.appendChild(parent);
    183    parent.appendChild(target);
    184 
    185    parent.style.scrollTimelineName = '--timeline';
    186    target.style.animation = "anim 10s linear";
    187    target.style.animationTimeline = '--timeline';
    188  });
    189 
    190  assert_equals(getComputedStyle(target).translate, 'none',
    191    'Animation with an unresolved current time');
    192 
    193  target.remove();
    194  parent.remove();
    195 }, 'scroll-timeline-name on an element which is not a scroll-container');
    196 
    197 promise_test(async t => {
    198  let [scroller, target] = createScrollerAndTarget(t);
    199 
    200  await runAndWaitForFrameUpdate(() => {
    201    // <div id='scroller' class='scroller'> ...
    202    //   <div id='target'></div>
    203    // </div>
    204 
    205    document.body.appendChild(scroller);
    206    scroller.appendChild(target);
    207 
    208    scroller.style.scrollTimelineName = '--timeline-A';
    209    scroller.scrollTop = 50; // 25%
    210    target.style.animation = "anim 10s linear";
    211    target.style.animationTimeline = '--timeline-B';
    212  });
    213 
    214  const anim = target.getAnimations()[0];
    215  assert_true(!!anim, 'Failed to create animation');
    216  assert_equals(anim.timeline, null);
    217  // Hold time of animation is zero.
    218  assert_equals(getComputedStyle(target).translate, '50px');
    219 
    220  scroller.style.scrollTimelineName = '--timeline-B';
    221  await waitForNextFrame();
    222 
    223  assert_true(!!anim.timeline, 'Failed to create timeline');
    224  assert_equals(getComputedStyle(target).translate, '75px');
    225 }, 'Change in scroll-timeline-name to match animation timeline updates animation.');
    226 
    227 promise_test(async t => {
    228  let [scroller, target] = createScrollerAndTarget(t);
    229 
    230  await runAndWaitForFrameUpdate(() => {
    231    // <div id='scroller' class='scroller'> ...
    232    //   <div id='target'></div>
    233    // </div>
    234 
    235    document.body.appendChild(scroller);
    236    scroller.appendChild(target);
    237 
    238    scroller.style.scrollTimelineName = '--timeline-A';
    239    scroller.scrollTop = 50; // 25%
    240    target.style.animation = "anim 10s linear";
    241    target.style.animationTimeline = '--timeline-A';
    242  });
    243 
    244  const anim = target.getAnimations()[0];
    245  assert_true(!!anim, 'Failed to create animation');
    246  assert_true(!!anim.timeline, 'Failed to create timeline');
    247  assert_equals(getComputedStyle(target).translate, '75px');
    248  assert_percents_equal(anim.startTime, 0);
    249  assert_percents_equal(anim.currentTime, 25);
    250 
    251  scroller.style.scrollTimelineName = '--timeline-B';
    252  await waitForNextFrame();
    253 
    254  // Switching to a null timeline pauses the animation.
    255  assert_equals(anim.timeline, null, 'Failed to remove timeline');
    256  assert_equals(getComputedStyle(target).translate, '75px');
    257  assert_equals(anim.startTime, null);
    258  assert_times_equal(anim.currentTime, 2500);
    259 }, 'Change in scroll-timeline-name to no longer match animation timeline updates animation.');
    260 
    261 promise_test(async t => {
    262  let target = createTarget(t);
    263  let scroller1 = createScroller(t);
    264  let scroller2 = createScroller(t);
    265 
    266  target.style.animation = 'anim 10s linear';
    267  target.style.animationTimeline = '--timeline';
    268  scroller1.style.scrollTimelineName = '--timeline';
    269  scroller1.id = 'A';
    270  scroller2.id = 'B';
    271 
    272  await runAndWaitForFrameUpdate(() => {
    273    // <div class='scroller' id='A'> ...
    274    //   <div class='scroller' id='B'> ...
    275    //     <div id='target'></div>
    276    //   </div>
    277    // </div>
    278    document.body.appendChild(scroller1);
    279    scroller1.appendChild(scroller2);
    280    scroller2.appendChild(target);
    281 
    282    scroller1.style.scrollTimelineName = '--timeline';
    283    scroller1.scrollTop = 50; // 25%
    284    scroller2.scrollTop = 100; // 50%
    285  });
    286 
    287  const anim = target.getAnimations()[0];
    288  assert_true(!!anim.timeline, 'Failed to retrieve animation');
    289  assert_equals(anim.timeline.source.id, 'A');
    290  assert_equals(getComputedStyle(target).translate, '75px');
    291 
    292  scroller2.style.scrollTimelineName = '--timeline';
    293  await waitForNextFrame();
    294 
    295  // The timeline should be updated to scroller2.
    296  assert_true(!!anim.timeline, 'Animation no longer has a timeline');
    297  assert_equals(anim.timeline.source.id, 'B', 'Timeline not updated');
    298  assert_equals(getComputedStyle(target).translate, '100px');
    299 }, 'Timeline lookup updates candidate when closer match available.');
    300 
    301 promise_test(async t => {
    302  let wrapper = createScroller(t);
    303  wrapper.classList.remove('scroller');
    304  let target = createTarget(t);
    305 
    306  await runAndWaitForFrameUpdate(() => {
    307    // <div id='wrapper'> ...
    308    //   <div id='target'></div>
    309    // </div>
    310    document.body.appendChild(wrapper);
    311    wrapper.appendChild(target);
    312    target.style.animation = "anim 10s linear";
    313    target.style.animationTimeline = '--timeline';
    314  });
    315 
    316  // Timeline initially cannot be resolved, resulting in a null
    317  // timeline. The animation's hold time is zero.
    318  // let anim = document.getAnimations()[0];
    319  assert_equals(getComputedStyle(target).translate, '50px');
    320 
    321  await runAndWaitForFrameUpdate(() => {
    322    // <div id='wrapper' class="scroller"> ...
    323    //   <div id='target'></div>
    324    // </div>
    325    wrapper.classList.add('scroller');
    326    wrapper.style.scrollTimelineName = '--timeline';
    327    wrapper.scrollTop = 50; // 25%
    328  });
    329 
    330  // The timeline should be updated to scroller.
    331  assert_equals(getComputedStyle(target).translate, '75px');
    332 }, 'Timeline lookup updates candidate when match becomes available.');
    333 
    334 
    335 // -------------------------
    336 // Test scroll-timeline-axis
    337 // -------------------------
    338 
    339 promise_test(async t => {
    340  let [scroller, target] = createScrollerAndTarget(t);
    341  scroller.style.writingMode = 'vertical-lr';
    342 
    343  await runAndWaitForFrameUpdate(() => {
    344    // <div id='scroller' class='scroller'> ...
    345    //   <div id='target'></div>
    346    // </div>
    347    document.body.appendChild(scroller);
    348    scroller.appendChild(target);
    349 
    350    scroller.style.scrollTimeline = '--timeline block';
    351    target.style.animation = "anim-2 10s linear";
    352    target.style.animationTimeline = '--timeline';
    353  });
    354 
    355  await waitForScrollLeft(scroller, 50);
    356  assert_equals(getComputedStyle(target).zIndex, '50');
    357 }, 'scroll-timeline-axis is block');
    358 
    359 promise_test(async t => {
    360  let [scroller, target] = createScrollerAndTarget(t);
    361  scroller.style.writingMode = 'vertical-lr';
    362 
    363  await runAndWaitForFrameUpdate(() => {
    364    // <div id='scroller' class='scroller'> ...
    365    //   <div id='target'></div>
    366    // </div>
    367    document.body.appendChild(scroller);
    368    scroller.appendChild(target);
    369 
    370    scroller.style.scrollTimeline = '--timeline inline';
    371    target.style.animation = "anim-2 10s linear";
    372    target.style.animationTimeline = '--timeline';
    373  });
    374 
    375  await waitForScrollTop(scroller, 50);
    376  assert_equals(getComputedStyle(target).zIndex, '50');
    377 }, 'scroll-timeline-axis is inline');
    378 
    379 promise_test(async t => {
    380  let [scroller, target] = createScrollerAndTarget(t);
    381  scroller.style.writingMode = 'vertical-lr';
    382 
    383  await runAndWaitForFrameUpdate(() => {
    384    // <div id='scroller' class='scroller'> ...
    385    //   <div id='target'></div>
    386    // </div>
    387    document.body.appendChild(scroller);
    388    scroller.appendChild(target);
    389 
    390    scroller.style.scrollTimeline = '--timeline x';
    391    target.style.animation = "anim-2 10s linear";
    392    target.style.animationTimeline = '--timeline';
    393  });
    394 
    395  await waitForScrollLeft(scroller, 50);
    396  assert_equals(getComputedStyle(target).zIndex, '50');
    397 }, 'scroll-timeline-axis is x');
    398 
    399 promise_test(async t => {
    400  let [scroller, target] = createScrollerAndTarget(t);
    401  scroller.style.writingMode = 'vertical-lr';
    402 
    403  await runAndWaitForFrameUpdate(() => {
    404    // <div id='scroller' class='scroller'> ...
    405    //   <div id='target'></div>
    406    // </div>
    407    document.body.appendChild(scroller);
    408    scroller.appendChild(target);
    409 
    410    scroller.style.scrollTimeline = '--timeline y';
    411    target.style.animation = "anim-2 10s linear";
    412    target.style.animationTimeline = '--timeline';
    413  });
    414 
    415  await waitForScrollTop(scroller, 50);
    416  assert_equals(getComputedStyle(target).zIndex, '50');
    417 }, 'scroll-timeline-axis is y');
    418 
    419 promise_test(async t => {
    420  let [scroller, target] = createScrollerAndTarget(t);
    421 
    422  await runAndWaitForFrameUpdate(() => {
    423    // <div id='scroller' class='scroller'> ...
    424    //   <div id='target'></div>
    425    // </div>
    426    document.body.appendChild(scroller);
    427    scroller.appendChild(target);
    428 
    429    scroller.style.scrollTimeline = '--timeline block';
    430    target.style.animation = "anim-2 10s linear";
    431    target.style.animationTimeline = '--timeline';
    432  });
    433 
    434  await waitForScrollTop(scroller, 25);
    435  await waitForScrollLeft(scroller, 75);
    436  assert_equals(getComputedStyle(target).zIndex, '25');
    437 
    438  scroller.style.scrollTimelineAxis = 'inline';
    439  await waitForNextFrame();
    440  assert_equals(getComputedStyle(target).zIndex, '75');
    441 }, 'scroll-timeline-axis is mutated');
    442 
    443 </script>
    444 </body>