tor-browser

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

timeline-offset-in-keyframe.html (9086B)


      1 <!DOCTYPE html>
      2 <html>
      3 <head>
      4 <meta charset="utf-8">
      5 <link rel="help" src="https://drafts.csswg.org/scroll-animations-1/#named-timeline-range">
      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 <title>Animation range and delay</title>
     11 </head>
     12 <style type="text/css">
     13  #scroller {
     14    border:  10px solid lightgray;
     15    overflow-y: scroll;
     16    overflow-x: hidden;
     17    width: 300px;
     18    height: 200px;
     19  }
     20  #target {
     21    margin: 800px 10px;
     22    width: 100px;
     23    height: 100px;
     24    z-index: -1;
     25    background-color: green;
     26  }
     27 </style>
     28 <body>
     29  <div id=scroller>
     30    <div id=target></div>
     31  </div>
     32 </body>
     33 <script type="text/javascript">
     34  async function runTest() {
     35    function assert_progress_equals(anim, expected, errorMessage) {
     36      assert_approx_equals(
     37          anim.effect.getComputedTiming().progress,
     38          expected, 1e-6, errorMessage);
     39    }
     40 
     41    function assert_opacity_equals(expected, errorMessage) {
     42      assert_approx_equals(
     43          parseFloat(getComputedStyle(target).opacity), expected, 1e-6,
     44                     errorMessage);
     45    }
     46 
     47    async function runTimelineOffsetsInKeyframesTest(keyframes) {
     48      const testcase = JSON.stringify(keyframes);
     49      const anim = target.animate(keyframes, {
     50        timeline: new ViewTimeline( { subject: target }),
     51        rangeStart: { rangeName: 'contain', offset: CSS.percent(0) },
     52        rangeEnd: { rangeName: 'contain', offset: CSS.percent(100) },
     53        duration: 'auto', fill: 'both'
     54      });
     55      await anim.ready;
     56      await waitForNextFrame();
     57 
     58      // @ contain 0%
     59      scroller.scrollTop = 700;
     60      await waitForNextFrame();
     61 
     62      assert_progress_equals(
     63          anim, 0, `Testcase '${testcase}': progress at contain 0%`);
     64      assert_opacity_equals(
     65          1/3, `Testcase '${testcase}': opacity at contain 0%`);
     66 
     67      // @ contain 50%
     68      scroller.scrollTop = 750;
     69      await waitForNextFrame();
     70      assert_progress_equals(
     71          anim, 0.5, `Testcase '${testcase}': progress at contain 50%`);
     72      assert_opacity_equals(
     73          0.5, `Testcase '${testcase}': opacity at contain 50%`);
     74 
     75      // @ contain 100%
     76      scroller.scrollTop = 800;
     77      await waitForNextFrame();
     78      assert_progress_equals(
     79          anim, 1, `Testcase '${testcase}': progress at contain 100%`);
     80      assert_opacity_equals(
     81          2/3, `Testcase '${testcase}': opacity at contain 100%`);
     82      anim.cancel();
     83    }
     84 
     85    async function runParseNumberOrPercentInKeyframesTest(keyframes) {
     86      const anim = target.animate(keyframes, {
     87        timeline: new ViewTimeline( { subject: target }),
     88        rangeStart: { rangeName: 'contain', offset: CSS.percent(0) },
     89        rangeEnd: { rangeName: 'contain', offset: CSS.percent(100) },
     90        duration: 'auto', fill: 'both'
     91      });
     92      await anim.ready;
     93      await waitForNextFrame();
     94 
     95      const maxScroll = scroller.scrollHeight - scroller.clientHeight;
     96      scroller.scrollTop = maxScroll / 2;
     97      await waitForNextFrame();
     98 
     99      const testcase = JSON.stringify(keyframes);
    100      assert_progress_equals(anim, 0.5, testcase);
    101      assert_opacity_equals(0.5, testcase);
    102      anim.cancel();
    103    }
    104 
    105    async function runInvalidKeyframesTest(keyframes) {
    106      assert_throws_js(TypeError, () => {
    107        target.animate(keyframes, {
    108          timeline: new ViewTimeline( { subject: target }),
    109        });
    110      }, `Invalid keyframes test case "${JSON.stringify(keyframes)}"`);
    111    }
    112 
    113    promise_test(async t => {
    114      // Test equivalent typed-OM and CSS representations of timeline offsets.
    115      // Test array and object form for keyframes.
    116      const keyframeTests = [
    117        // BaseKeyframe form with offsets expressed as typed-OM.
    118        [
    119          {
    120            offset: { rangeName: 'cover', offset: CSS.percent(0) },
    121            opacity: 0
    122          },
    123          {
    124            offset: { rangeName: 'cover', offset: CSS.percent(100) },
    125            opacity: 1
    126          }
    127        ],
    128        // BaseKeyframe form with offsets expressed as CSS text.
    129        [
    130          { offset: "cover 0%", opacity: 0 },
    131          { offset: "cover 100%", opacity: 1 }
    132        ],
    133        // BasePropertyIndexedKeyframe form with offsets expressed as typed-OM.
    134        {
    135          opacity: [0, 1],
    136          offset: [
    137            { rangeName: 'cover', offset: CSS.percent(0) },
    138            { rangeName: 'cover', offset: CSS.percent(100) }
    139          ]
    140        },
    141        // BasePropertyIndexedKeyframe form with offsets expressed as CSS text.
    142        { opacity: [0, 1], offset: [ "cover 0%", "cover 100%" ]}
    143      ];
    144 
    145      for (let i = 0; i < keyframeTests.length; i++) {
    146        await runTimelineOffsetsInKeyframesTest(keyframeTests[i]);
    147      }
    148 
    149    }, 'Timeline offsets in programmatic keyframes');
    150 
    151    promise_test(async t => {
    152      const keyframeTests = [
    153        [{offset: "0.5", opacity: 0.5 }],
    154        [{offset: "50%", opacity: 0.5 }],
    155        [{offset: "calc(20% + 30%)", opacity: 0.5 }]
    156      ];
    157 
    158      for (let i = 0; i < keyframeTests.length; i++) {
    159        await runParseNumberOrPercentInKeyframesTest(keyframeTests[i]);
    160      }
    161 
    162    }, 'String offsets in programmatic keyframes');
    163 
    164    promise_test(async t => {
    165      const invalidKeyframeTests = [
    166        // BasePropertyKefyrame:
    167        [{ offset: { rangeName: 'somewhere', offset: CSS.percent(0) }}],
    168        [{ offset: { rangeName: 'entry', offset: CSS.px(0) }}],
    169        [{ offset: "here 0%" }],
    170        [{ offset: "entry 3px" }],
    171        // BasePropertyIndexedKeyframe with sequence:
    172        { offset: [{ rangeName: 'somewhere', offset: CSS.percent(0) }]},
    173        { offset: [{ rangeName: 'entry', offset: CSS.px(0) }]},
    174        { offset: ["here 0%"] },
    175        { offset: ["entry 3px" ]},
    176        // BasePropertyIndexedKeyframe without sequence:
    177        { offset: { rangeName: 'somewhere', offset: CSS.percent(0) }},
    178        { offset: { rangeName: 'entry', offset: CSS.px(0) }},
    179        { offset: "here 0%" },
    180        { offset: "entry 3px" },
    181        // <number> or <percent> as string:
    182        [{ offset: "-1" }],
    183        [{ offset: "2" }],
    184        [{ offset: "-10%" }],
    185        [{ offset: "110%" }],
    186        { offset: ["-1"], opacity: [0.5] },
    187        { offset: ["2"], opacity: [0.5] },
    188        { offset: "-1", opacity: 0.5 },
    189        { offset: "2", opacity: 0.5 },
    190        // Extra stuff at the end.
    191        [{ offset: "0.5 trailing nonsense" }],
    192        [{ offset: "cover 50% eureka" }]
    193      ];
    194      for( let i = 0; i < invalidKeyframeTests.length; i++) {
    195        await runInvalidKeyframesTest(invalidKeyframeTests[i]);
    196      }
    197    }, 'Invalid timeline offset in programmatic keyframe throws');
    198 
    199 
    200    promise_test(async t => {
    201      const anim = target.animate([
    202          { offset: "cover 0%", opacity: 0 },
    203          { offset: "cover 100%", opacity: 1 }
    204        ], {
    205        rangeStart: { rangeName: 'contain', offset: CSS.percent(0) },
    206        rangeEnd: { rangeName: 'contain', offset: CSS.percent(100) },
    207        duration: 10000, fill: 'both'
    208      });
    209 
    210      scroller.scrollTop = 750;
    211 
    212      await anim.ready;
    213      assert_opacity_equals(1, `Opacity with document timeline`);
    214 
    215      anim.timeline = new ViewTimeline( { subject: target });
    216      await anim.ready;
    217 
    218      assert_progress_equals(anim, 0.5, `Progress at contain 50%`);
    219      assert_opacity_equals(0.5, `Opacity at contain 50%`);
    220 
    221      anim.timeline = document.timeline;
    222      assert_false(anim.pending);
    223      await waitForNextFrame();
    224      assert_opacity_equals(1, `Opacity after resetting timeline`);
    225 
    226      anim.cancel();
    227    }, 'Timeline offsets in programmatic keyframes adjust for change in ' +
    228       'timeline');
    229 
    230    promise_test(async t => {
    231      const anim = target.animate([], {
    232          timeline: new ViewTimeline( { subject: target }),
    233          rangeStart: { rangeName: 'contain', offset: CSS.percent(0) },
    234          rangeEnd: { rangeName: 'contain', offset: CSS.percent(100) },
    235          duration: 'auto', fill: 'both'
    236        });
    237 
    238      await anim.ready;
    239      await waitForNextFrame();
    240 
    241      scroller.scrollTop = 750;
    242      await waitForNextFrame();
    243      assert_progress_equals(
    244          anim, 0.5, `Progress at contain 50% before effect change`);
    245      assert_opacity_equals(1, `Opacity at contain 50% before effect change`);
    246 
    247      anim.effect = new KeyframeEffect(target, [
    248          { offset: "cover 0%", opacity: 0 },
    249          { offset: "cover 100%", opacity: 1 }
    250        ], { duration: 'auto', fill: 'both' });
    251      await waitForNextFrame();
    252      assert_progress_equals(
    253          anim, 0.5, `Progress at contain 50% after effect change`);
    254      assert_opacity_equals(0.5, `Opacity at contain 50% after effect change`);
    255    }, 'Timeline offsets in programmatic keyframes resolved when updating ' +
    256       'the animation effect');
    257  }
    258 
    259  // TODO(kevers): Add tests for getKeyframes once
    260  // https://github.com/w3c/csswg-drafts/issues/8507 is resolved.
    261 
    262  window.onload = runTest;
    263 </script>
    264 </html>