tor-browser

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

setting-timeline.tentative.html (14549B)


      1 <!DOCTYPE html>
      2 <meta charset=utf-8>
      3 <title>Setting the timeline of scroll animation</title>
      4 <link rel="help"
      5      href="https://drafts.csswg.org/web-animations-1/#setting-the-timeline">
      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="testcommon.js"></script>
     10 <style>
     11  .scroller {
     12    overflow-x: hidden;
     13    overflow-y: auto;
     14    height: 200px;
     15    width: 100px;
     16    will-change: transform;
     17  }
     18  .contents {
     19    /* The height is set to align scrolling in pixels with logical time in ms */
     20    height: 1200px;
     21    width: 100%;
     22  }
     23  @keyframes anim {
     24    from { opacity: 0; }
     25    to { opacity: 1; }
     26  }
     27  .anim {
     28    animation: anim 1s paused linear;
     29  }
     30  #target {
     31    height:  100px;
     32    width:  100px;
     33    background-color: green;
     34    margin-top: -1000px;
     35  }
     36 </style>
     37 <body>
     38 <script>
     39 'use strict';
     40 
     41 function createAnimation(t) {
     42  const elem = createDiv(t);
     43  const animation = elem.animate({ opacity: [1, 0] }, 1000);
     44  return animation;
     45 }
     46 
     47 function createPausedCssAnimation(t) {
     48  const elem = createDiv(t);
     49  elem.classList.add('anim');
     50  return elem.getAnimations()[0];
     51 }
     52 
     53 function updateScrollPosition(timeline, offset) {
     54  const scroller = timeline.source;
     55  assert_true(!!scroller, 'source is resolved');
     56 
     57  // Wait for new animation frame which allows the timeline to compute new
     58  // current time.
     59  return runAndWaitForFrameUpdate(() => {
     60    scroller.scrollTop = offset;
     61  });
     62 }
     63 
     64 function assert_timeline_current_time(animation, timeline_current_time) {
     65  if (animation.currentTime instanceof CSSUnitValue){
     66    assert_percents_equal(animation.timeline.currentTime, timeline_current_time,
     67                          `Timeline's currentTime aligns with the scroll ` +
     68                          `position even when paused`);
     69  }
     70  else {
     71    assert_times_equal(animation.timeline.currentTime, timeline_current_time,
     72                       `Timeline's currentTime aligns with the scroll ` +
     73                       `position even when paused`);
     74  }
     75 }
     76 
     77 function assert_scroll_synced_times(animation, timeline_current_time,
     78                                    animation_current_time) {
     79  assert_timeline_current_time(animation, timeline_current_time);
     80  if (animation.currentTime instanceof CSSUnitValue){
     81    assert_percents_equal(animation.currentTime, animation_current_time,
     82        `Animation's currentTime aligns with the scroll position`);
     83  }
     84  else {
     85    assert_times_equal(animation.currentTime, animation_current_time,
     86        `Animation's currentTime aligns with the scroll position`);
     87  }
     88 }
     89 
     90 function assert_paused_times(animation, timeline_current_time,
     91                             animation_current_time) {
     92  assert_timeline_current_time(animation, timeline_current_time);
     93  if (animation.currentTime instanceof CSSUnitValue){
     94    assert_percents_equal(animation.currentTime, animation_current_time,
     95                          `Animation's currentTime is fixed while paused`);
     96  }
     97  else {
     98    assert_times_equal(animation.currentTime, animation_current_time,
     99                       `Animation's currentTime is fixed while paused`);
    100  }
    101 }
    102 
    103 promise_test(async t => {
    104  const scrollTimeline = createScrollTimeline(t);
    105  await updateScrollPosition(scrollTimeline, 100);
    106 
    107  const animation = createAnimation(t);
    108  animation.timeline = scrollTimeline;
    109  assert_true(animation.pending);
    110  await animation.ready;
    111 
    112  assert_equals(animation.playState, 'running');
    113  assert_scroll_synced_times(animation, 10, 10);
    114 }, 'Setting a scroll timeline on a play-pending animation synchronizes ' +
    115   'currentTime of the animation with the scroll position.');
    116 
    117 promise_test(async t => {
    118  const scrollTimeline = createScrollTimeline(t);
    119  await updateScrollPosition(scrollTimeline, 100);
    120 
    121  const animation = createAnimation(t);
    122  animation.pause();
    123  animation.timeline = scrollTimeline;
    124  assert_true(animation.pending);
    125  await animation.ready;
    126 
    127  assert_equals(animation.playState, 'paused');
    128  assert_paused_times(animation, 10, 0);
    129 
    130  await updateScrollPosition(animation.timeline, 200);
    131 
    132  assert_equals(animation.playState, 'paused');
    133  assert_paused_times(animation, 20, 0);
    134 
    135  animation.play();
    136  await animation.ready;
    137 
    138  assert_scroll_synced_times(animation, 20, 20);
    139 }, 'Setting a scroll timeline on a pause-pending animation fixes the ' +
    140   'currentTime of the animation based on the scroll position once resumed');
    141 
    142 promise_test(async t => {
    143  const scrollTimeline = createScrollTimeline(t);
    144  await updateScrollPosition(scrollTimeline, 100);
    145 
    146  const animation = createAnimation(t);
    147  animation.reverse();
    148  animation.timeline = scrollTimeline;
    149  await animation.ready;
    150 
    151  assert_equals(animation.playState, 'running');
    152  assert_scroll_synced_times(animation, 10, 90);
    153 },  'Setting a scroll timeline on a reversed play-pending animation ' +
    154    'synchronizes the currentTime of the animation with the scroll ' +
    155    'position.');
    156 
    157 promise_test(async t => {
    158  const scrollTimeline = createScrollTimeline(t);
    159  await updateScrollPosition(scrollTimeline, 100);
    160 
    161  const animation = createAnimation(t);
    162  await animation.ready;
    163 
    164  animation.timeline = scrollTimeline;
    165  assert_true(animation.pending);
    166  assert_equals(animation.playState, 'running');
    167  await animation.ready;
    168  assert_scroll_synced_times(animation, 10, 10);
    169 },  'Setting a scroll timeline on a running animation synchronizes the ' +
    170    'currentTime of the animation with the scroll position.');
    171 
    172 promise_test(async t => {
    173  const scrollTimeline = createScrollTimeline(t);
    174  await updateScrollPosition(scrollTimeline, 100);
    175 
    176  const animation = createAnimation(t);
    177  animation.pause();
    178  await animation.ready;
    179 
    180  animation.timeline = scrollTimeline;
    181  assert_false(animation.pending);
    182  assert_equals(animation.playState, 'paused');
    183  assert_paused_times(animation, 10, 0);
    184 
    185  animation.play();
    186  await animation.ready;
    187 
    188  assert_scroll_synced_times(animation, 10, 10);
    189 }, 'Setting a scroll timeline on a paused animation fixes the currentTime of ' +
    190   'the animation based on the scroll position when resumed');
    191 
    192 promise_test(async t => {
    193  const scrollTimeline = createScrollTimeline(t);
    194  await updateScrollPosition(scrollTimeline, 100);
    195 
    196  const animation = createAnimation(t);
    197  animation.reverse();
    198  animation.pause();
    199  await animation.ready;
    200 
    201  animation.timeline = scrollTimeline;
    202  assert_false(animation.pending);
    203  assert_equals(animation.playState, 'paused');
    204  assert_paused_times(animation, 10, 100);
    205 
    206  animation.play();
    207  await animation.ready;
    208 
    209  assert_scroll_synced_times(animation, 10, 90);
    210 }, 'Setting a scroll timeline on a reversed paused animation ' +
    211   'fixes the currentTime of the animation based on the scroll ' +
    212   'position when resumed');
    213 
    214 promise_test(async t => {
    215  const animation = createAnimation(t);
    216  const scrollTimeline = createScrollTimeline(t);
    217  animation.timeline = scrollTimeline;
    218  await animation.ready;
    219  await updateScrollPosition(scrollTimeline, 100);
    220 
    221  animation.timeline = document.timeline;
    222  assert_times_equal(animation.currentTime, 100);
    223 }, 'Transitioning from a scroll timeline to a document timeline on a running ' +
    224   'animation preserves currentTime');
    225 
    226 promise_test(async t => {
    227  const animation = createAnimation(t);
    228  const scrollTimeline = createScrollTimeline(t);
    229  animation.timeline = scrollTimeline;
    230  await animation.ready;
    231  await updateScrollPosition(scrollTimeline, 100);
    232 
    233  animation.pause();
    234  animation.timeline = document.timeline;
    235 
    236  await animation.ready;
    237  assert_times_equal(animation.currentTime, 100);
    238 }, 'Transitioning from a scroll timeline to a document timeline on a ' +
    239   'pause-pending animation preserves currentTime');
    240 
    241 promise_test(async t => {
    242  const animation = createAnimation(t);
    243  const scrollTimeline = createScrollTimeline(t);
    244  animation.timeline = scrollTimeline;
    245 
    246  animation.reverse();
    247  await animation.ready;
    248  await updateScrollPosition(scrollTimeline, 100);
    249 
    250  animation.pause();
    251  await animation.ready;
    252 
    253  assert_scroll_synced_times(animation, 10, 90);
    254 
    255  animation.timeline = document.timeline;
    256  assert_false(animation.pending);
    257  assert_equals(animation.playState, 'paused');
    258  assert_times_equal(animation.currentTime, 900);
    259 }, 'Transition from a scroll timeline to a document timeline on a reversed ' +
    260   'paused animation maintains correct currentTime');
    261 
    262 promise_test(async t => {
    263  const animation = createAnimation(t);
    264  const scrollTimeline = createScrollTimeline(t);
    265  animation.timeline = scrollTimeline;
    266  await animation.ready;
    267  await updateScrollPosition(scrollTimeline, 100);
    268 
    269  const progress = animation.currentTime.value / 100;
    270  const duration = animation.effect.getTiming().duration;
    271  animation.timeline = null;
    272 
    273  const expectedCurrentTime = progress * duration;
    274  assert_times_equal(animation.currentTime, expectedCurrentTime);
    275 }, 'Transitioning from a scroll timeline to a null timeline on a running ' +
    276    'animation preserves current progress.');
    277 
    278 promise_test(async t => {
    279  const keyframeEfect = new KeyframeEffect(createDiv(t),
    280                                           { opacity: [0, 1] },
    281                                           1000);
    282  const animation = new Animation(keyframeEfect, null);
    283  animation.startTime = 0;
    284  assert_equals(animation.playState, 'running');
    285 
    286  const scrollTimeline = createScrollTimeline(t);
    287  await updateScrollPosition(scrollTimeline, 100);
    288 
    289  animation.timeline = scrollTimeline;
    290  assert_equals(animation.playState, 'running');
    291  await animation.ready;
    292 
    293  assert_percents_equal(animation.currentTime, 10);
    294 }, 'Switching from a null timeline to a scroll timeline on an animation with ' +
    295   'a resolved start time preserved the play state');
    296 
    297 promise_test(async t => {
    298  const firstScrollTimeline = createScrollTimeline(t);
    299  await updateScrollPosition(firstScrollTimeline, 100);
    300 
    301  const secondScrollTimeline = createScrollTimeline(t);
    302  await updateScrollPosition(secondScrollTimeline, 200);
    303 
    304  const animation = createAnimation(t);
    305  animation.timeline = firstScrollTimeline;
    306  await animation.ready;
    307  assert_percents_equal(animation.currentTime, 10);
    308 
    309  animation.timeline = secondScrollTimeline;
    310  await animation.ready;
    311 
    312  assert_percents_equal(animation.currentTime, 20);
    313 }, 'Switching from one scroll timeline to another updates currentTime');
    314 
    315 promise_test(async t => {
    316  const scrollTimeline = createScrollTimeline(t);
    317  await updateScrollPosition(scrollTimeline, 100);
    318 
    319  const animation = createPausedCssAnimation(t);
    320  animation.timeline = scrollTimeline;
    321  await animation.ready;
    322  assert_equals(animation.playState, 'paused');
    323  assert_percents_equal(animation.currentTime, 0);
    324 
    325  const target = animation.effect.target;
    326  target.style.animationPlayState = 'running';
    327  await animation.ready;
    328 
    329  assert_percents_equal(animation.currentTime, 10);
    330 }, 'Switching from a document timeline to a scroll timeline updates ' +
    331   'currentTime when unpaused via CSS.');
    332 
    333 promise_test(async t => {
    334  const scrollTimeline = createScrollTimeline(t);
    335  await updateScrollPosition(scrollTimeline, 100);
    336 
    337  const animation = createAnimation(t);
    338  animation.pause();
    339  animation.currentTime = 500; // 50%
    340  animation.timeline = scrollTimeline;
    341  await animation.ready;
    342  assert_percents_equal(animation.currentTime, 50);
    343 
    344  animation.play();
    345  await animation.ready;
    346  assert_percents_equal(animation.currentTime, 10);
    347 }, 'Switching from a document timeline to a scroll timeline and updating ' +
    348   'currentTime preserves the progress while paused.');
    349 
    350 promise_test(async t => {
    351  const elem = createDiv(t);
    352  const animation = elem.animate(null, Infinity);
    353  await animation.ready;
    354 
    355  animation.timeline = new ScrollTimeline();
    356  let timing = animation.effect.getComputedTiming();
    357  assert_percents_equal(timing.endTime, 100);
    358  assert_percents_equal(timing.activeDuration, 100);
    359  assert_percents_equal(timing.duration, 100);
    360 
    361  animation.effect.updateTiming({ iterations: 2 });
    362  timing = animation.effect.getComputedTiming();
    363  assert_percents_equal(timing.endTime, 100);
    364  assert_percents_equal(timing.activeDuration, 100);
    365  assert_percents_equal(timing.duration, 50);
    366 
    367  // Blink implementation does not permit setting an infinite number of
    368  // iterations on a scroll-linked animation. Workaround by temporarily
    369  // switching back to a document timeline.
    370  animation.timeline = document.timeline;
    371  animation.effect.updateTiming({ iterations: Infinity });
    372  animation.timeline = new ScrollTimeline();
    373  timing = animation.effect.getComputedTiming();
    374  // Having an infinite number of iterations with a finite timeline results in
    375  // each iteration having zero duration.
    376  assert_percents_equal(timing.duration, 0);
    377  // If either the iteration duration or iteration count is zero, the active
    378  // duration is always zero.
    379  assert_percents_equal(timing.activeDuration, 0);
    380  assert_percents_equal(timing.endTime, 0);
    381 
    382 }, 'Switching from a document timeline to a scroll timeline on an infinite ' +
    383   'duration animation.');
    384 
    385 
    386 promise_test(async t => {
    387  const scrollTimeline = createScrollTimeline(t);
    388  const view_timeline = createViewTimeline(t);
    389  await updateScrollPosition(scrollTimeline, 100);
    390  const animation = createAnimation(t);
    391  animation.timeline = scrollTimeline;
    392  // Range name is ignored while attached to a non-view scroll-timeline.
    393  // Offsets are still applied to the scroll-timeline.
    394  animation.rangeStart = { rangeName: 'contain', offset: CSS.percent(10) };
    395  animation.rangeEnd = { rangeName: 'contain', offset: CSS.percent(90) };
    396  await animation.ready;
    397 
    398  assert_scroll_synced_times(animation, 10, 0);
    399  assert_percents_equal(animation.startTime, 10);
    400 
    401  animation.timeline = view_timeline;
    402  assert_true(animation.pending);
    403  await animation.ready;
    404 
    405  // Cover range is [0px, 300px]
    406  // Contain range is [100px, 200px]
    407  // start time = (contain 10% pos - cover start pos) / cover range * 100%
    408  const expected_start_time = 110 / 300 * 100;
    409  // timeline time = (scroll pos - cover start pos) / cover range * 100%
    410  const expected_timeline_time = 100 / 300 * 100;
    411  // current time = timeline time - start time.
    412  const expected_current_time = expected_timeline_time - expected_start_time;
    413 
    414  assert_percents_equal(animation.startTime, expected_start_time);
    415  assert_percents_equal(animation.timeline.currentTime, expected_timeline_time);
    416  assert_percents_equal(animation.currentTime, expected_current_time);
    417 }, 'Changing from a scroll-timeline to a view-timeline updates start time.');
    418 
    419 </script>
    420 </body>