tor-browser

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

transform-interpolation-001.html (11477B)


      1 <!DOCTYPE html>
      2 <meta charset="UTF-8">
      3 <title>transform interpolation</title>
      4 <link rel="help" href="https://drafts.csswg.org/css-transforms/#transform-property">
      5 <meta name="assert" content="transform supports animation as a transform list">
      6 <meta name="timeout" content="long">
      7 
      8 <script src="/resources/testharness.js"></script>
      9 <script src="/resources/testharnessreport.js"></script>
     10 <script src="/css/support/interpolation-testcommon.js"></script>
     11 
     12 <style>
     13 .target {
     14  color: white;
     15  width: 100px;
     16  height: 100px;
     17  background-color: black;
     18  display: inline-block;
     19  overflow: hidden;
     20 }
     21 .expected {
     22  background-color: green;
     23 }
     24 .target > div {
     25  width: 10px;
     26  height: 10px;
     27  display: inline-block;
     28  background: orange;
     29  margin: 1px;
     30 }
     31 .test {
     32  overflow: hidden;
     33 }
     34 </style>
     35 
     36 <body>
     37  <template id="target-template">
     38    <div></div>
     39  </template>
     40 </body>
     41 
     42 <script>
     43 
     44 // The default comparison function calls normalizeValue, which rounds
     45 // everything to two decimal places, which isn't OK for the matrices
     46 // that result from large perspective values.
     47 const compareWithPerspective = (actual, expected) => {
     48  // TODO: This RegExp should be more precise to capture only what is a
     49  // valid float, and this code should be merged with other code doing
     50  // the same thing, e.g., RoundMatrix in
     51  // web-animations/animation-model/animation-types/property-list.js .
     52  const matrixRegExp = /^matrix3d\(((?:(?:[-0-9.e]+), ){15}(?:[-0-9.]+))\)$/;
     53  const actualMatch = actual.match(matrixRegExp);
     54  const expectedMatch = expected.match(matrixRegExp);
     55  assert_not_equals(actualMatch, null, `${actual} should be a matrix`);
     56  assert_not_equals(expectedMatch, null, `${expected} should be a matrix`);
     57  if (actualMatch === null || expectedMatch === null) {
     58    return;
     59  }
     60  const actualArray = actualMatch[1].split(", ").map(Number);
     61  const expectedArray = expectedMatch[1].split(", ").map(Number);
     62  assert_equals(actualArray.length, 16);
     63  assert_equals(expectedArray.length, 16);
     64 
     65  if (actualArray.length != expectedArray.length) {
     66    return;
     67  }
     68 
     69  for (let i in actualArray) {
     70    const error = Math.abs((actualArray[i] - expectedArray[i])) /
     71                  Math.max(1e-6,
     72                           Math.min(Math.abs(expectedArray[i]),
     73                                    Math.abs(actualArray[i])));
     74    assert_less_than(error, 1e-5, `comparing (at index ${i} actual value "${actual}" [${actualArray[i]}] and expected value "${expected}" [${expectedArray[i]}]`);
     75  }
     76 };
     77 
     78 // The spec at
     79 // https://drafts.csswg.org/css-transforms-2/#interpolation-of-transform-functions
     80 // requires that perspective be interpolated by decomposing the matrix
     81 // (which is trivial for perspective) and then interpolating the pieces.
     82 // The piece that's interpolated (the z part of the perspective array)
     83 // contains the negative reciprocal of the argument to perspective().
     84 const interpolatePerspective = (from, to, progress) => {
     85  return 1.0/((1.0 - progress) * (1.0/from) + progress * (1.0/to));
     86 };
     87 
     88 // Perspective
     89 test_interpolation({
     90  property: 'transform',
     91  from: 'perspective(400px)',
     92  to: 'perspective(500px)',
     93  comparisonFunction: compareWithPerspective
     94 }, [
     95  {at: -1, expect: `perspective(${interpolatePerspective(400, 500, -1)}px)`},
     96  {at: 0, expect: `perspective(${interpolatePerspective(400, 500, 0)}px)`},
     97  {at: 0.25, expect: `perspective(${interpolatePerspective(400, 500, 0.25)}px)`},
     98  {at: 0.75, expect: `perspective(${interpolatePerspective(400, 500, 0.75)}px)`},
     99  {at: 1, expect: `perspective(${interpolatePerspective(400, 500, 1)}px)`},
    100  {at: 2, expect: `perspective(${interpolatePerspective(400, 500, 2)}px)`},
    101 ]);
    102 test_interpolation({
    103  property: 'transform',
    104  from: 'skewX(10rad) perspective(400px)',
    105  to: 'skewX(20rad) perspective(500px)',
    106  comparisonFunction: compareWithPerspective
    107 }, [
    108  {at: -1, expect: `skewX(0rad) perspective(${interpolatePerspective(400, 500, -1)}px)`},
    109  {at: 0, expect: `skewX(10rad) perspective(${interpolatePerspective(400, 500, 0)}px)`},
    110  {at: 0.25, expect: `skewX(12.5rad) perspective(${interpolatePerspective(400, 500, 0.25)}px)`},
    111  {at: 0.75, expect: `skewX(17.5rad) perspective(${interpolatePerspective(400, 500, 0.75)}px)`},
    112  {at: 1, expect: `skewX(20rad) perspective(${interpolatePerspective(400, 500, 1)}px)`},
    113  {at: 2, expect: `skewX(30rad) perspective(${interpolatePerspective(400, 500, 2)}px)`},
    114 ]);
    115 test_interpolation({
    116  property: 'transform',
    117  from: 'scaleZ(1) perspective(400px)',
    118  to: 'scaleZ(2) perspective(500px)',
    119  comparisonFunction: compareWithPerspective
    120 }, [
    121  {at: -1, expect: `scaleZ(0) perspective(${interpolatePerspective(400, 500, -1)}px)`},
    122  {at: 0, expect: `scaleZ(1.0) perspective(${interpolatePerspective(400, 500, 0)}px)`},
    123  {at: 0.25, expect: `scaleZ(1.25) perspective(${interpolatePerspective(400, 500, 0.25)}px)`},
    124  {at: 0.75, expect: `scaleZ(1.75) perspective(${interpolatePerspective(400, 500, 0.75)}px)`},
    125  {at: 1, expect: `scaleZ(2) perspective(${interpolatePerspective(400, 500, 1)}px)`},
    126  {at: 2, expect: `scaleZ(3) perspective(${interpolatePerspective(400, 500, 2)}px)`},
    127 ]);
    128 // Test that the transform identity function for perspective is perspective(none)
    129 test_interpolation({
    130  property: 'transform',
    131  from: 'scaleZ(2)',
    132  to: 'scaleZ(2) perspective(500px)',
    133  comparisonFunction: compareWithPerspective
    134 }, [
    135  {at: -1, expect: `scaleZ(2)`},
    136  {at: 0, expect: `scaleZ(2)`},
    137  {at: 0.5, expect: `scaleZ(2) perspective(1000px)`},
    138  {at: 1, expect: `scaleZ(2) perspective(500px)`},
    139  {at: 2, expect: `scaleZ(2) perspective(250px)`},
    140 ]);
    141 test_interpolation({
    142  property: 'transform',
    143  from: 'perspective(none)',
    144  to: 'perspective(500px)',
    145 }, [
    146  {at: -1, expect: `perspective(none)`},
    147  {at: 0, expect: `perspective(none)`},
    148  {at: 0.5, expect: `perspective(1000px)`},
    149  {at: 1, expect: `perspective(500px)`},
    150  {at: 2, expect: `perspective(250px)`},
    151 ]);
    152 
    153 // Rotate
    154 test_interpolation({
    155  property: 'transform',
    156  from: 'rotate(30deg)',
    157  to: 'rotate(330deg)'
    158 }, [
    159  {at: -1, expect: 'rotate(-270deg)'},
    160  {at: 0, expect: 'rotate(30deg)'},
    161  {at: 0.25, expect: 'rotate(105deg)'},
    162  {at: 0.75, expect: 'rotate(255deg)'},
    163  {at: 1, expect: 'rotate(330deg)'},
    164  {at: 2, expect: 'rotate(630deg)'},
    165 ]);
    166 test_interpolation({
    167  property: 'transform',
    168  from: 'rotateX(0deg)',
    169  to: 'rotateX(700deg)'
    170 }, [
    171  {at: -1, expect: 'rotateX(-700deg)'},
    172  {at: 0, expect: 'rotateX(0deg)'},
    173  {at: 0.25, expect: 'rotateX(175deg)'},
    174  {at: 0.75, expect: 'rotateX(525deg)'},
    175  {at: 1, expect: 'rotateX(700deg)'},
    176  {at: 2, expect: 'rotateX(1400deg)'},
    177 ]);
    178 test_interpolation({
    179  property: 'transform',
    180  from: 'rotateY(0deg)',
    181  to: 'rotateY(800deg)'
    182 }, [
    183  {at: -1, expect: 'rotateY(-800deg)'},
    184  {at: 0, expect: 'rotateY(0deg)'},
    185  {at: 0.25, expect: 'rotateY(200deg)'},
    186  {at: 0.75, expect: 'rotateY(600deg)'},
    187  {at: 1, expect: 'rotateY(800deg)'},
    188  {at: 2, expect: 'rotateY(1600deg)'},
    189 ]);
    190 test_interpolation({
    191  property: 'transform',
    192  from: 'rotateZ(0deg)',
    193  to: 'rotateZ(900deg)'
    194 }, [
    195  {at: -1, expect: 'rotateZ(-900deg)'},
    196  {at: 0, expect: 'rotateZ(0deg)'},
    197  {at: 0.25, expect: 'rotateZ(225deg)'},
    198  {at: 0.75, expect: 'rotateZ(675deg)'},
    199  {at: 1, expect: 'rotateZ(900deg)'},
    200  {at: 2, expect: 'rotateZ(1800deg)'},
    201 ]);
    202 // Interpolation is about a common axis if either endpoint has a rotation angle
    203 // of zero.
    204 test_interpolation({
    205  property: 'transform',
    206  from: 'rotateX(0deg)',
    207  to: 'rotateY(900deg)'
    208 }, [
    209  {at: -1, expect: 'rotateY(-900deg)'},
    210  {at: 0, expect: 'rotateY(0deg)'},
    211  {at: 0.25, expect: 'rotateY(225deg)'},
    212  {at: 0.75, expect: 'rotateY(675deg)'},
    213  {at: 1, expect: 'rotateY(900deg)'},
    214  {at: 2, expect: 'rotateY(1800deg)'},
    215 ]);
    216 test_interpolation({
    217  property: 'transform',
    218  from: 'rotateY(900deg)',
    219  to: 'rotateZ(0deg)'
    220 }, [
    221  {at: -1, expect: 'rotateY(1800deg)'},
    222  {at: 0, expect: 'rotateY(900deg)'},
    223  {at: 0.25, expect: 'rotateY(675deg)'},
    224  {at: 0.75, expect: 'rotateY(225deg)'},
    225  {at: 1, expect: 'rotateY(0deg)'},
    226  {at: 2, expect: 'rotateY(-900deg)'},
    227 ]);
    228 test_interpolation({
    229  property: 'transform',
    230  from: 'rotate3d(7, 8, 9, 100deg)',
    231  to: 'rotate3d(7, 8, 9, 260deg)'
    232 }, [
    233  {at: -1, expect: 'rotate3d(7, 8, 9, -60deg)'},
    234  {at: 0, expect: 'rotate3d(7, 8, 9, 100deg)'},
    235  {at: 0.25, expect: 'rotate3d(7, 8, 9, 140deg)'},
    236  {at: 0.75, expect: 'rotate3d(7, 8, 9, 220deg)'},
    237  {at: 1, expect: 'rotate3d(7, 8, 9, 260deg)'},
    238  {at: 2, expect: 'rotate3d(7, 8, 9, 420deg)'},
    239 ]);
    240 test_interpolation({
    241  property: 'transform',
    242  from: 'rotate3d(7, 8, 9, 0deg)',
    243  to: 'rotate3d(7, 8, 9, 450deg)'
    244 }, [
    245  {at: -1, expect: 'rotate3d(7, 8, 9, -450deg)'},
    246  {at: 0, expect: 'rotate3d(7, 8, 9, 0deg)'},
    247  {at: 0.25, expect: 'rotate3d(7, 8, 9, 112.5deg)'},
    248  {at: 0.75, expect: 'rotate3d(7, 8, 9, 337.5deg)'},
    249  {at: 1, expect: 'rotate3d(7, 8, 9, 450deg)'},
    250  {at: 2, expect: 'rotate3d(7, 8, 9, 900deg)'},
    251 ]);
    252 test_interpolation({
    253  property: 'transform',
    254  from: 'rotate3d(0, 1, 0, 0deg)',
    255  to: 'rotate3d(0, 1, 0, 450deg)'
    256 }, [
    257  {at: -1, expect: 'rotate3d(0, 1, 0, -450deg)'},
    258  {at: 0, expect: 'rotate3d(0, 1, 0, 0deg)'},
    259  {at: 0.25, expect: 'rotate3d(0, 1, 0, 112.5deg)'},
    260  {at: 0.75, expect: 'rotate3d(0, 1, 0, 337.5deg)'},
    261  {at: 1, expect: 'rotate3d(0, 1, 0, 450deg)'},
    262  {at: 2, expect: 'rotate3d(0, 1, 0, 900deg)'},
    263 ]);
    264 // Rotation is about a common axis if the axes are colinear.
    265 test_interpolation({
    266  property: 'transform',
    267  from: 'rotate3d(0, 1, 0, 0deg)',
    268  to: 'rotate3d(0, 2, 0, 450deg)'
    269 }, [
    270  {at: -1, expect: 'rotate3d(0, 1, 0, -450deg)'},
    271  {at: 0, expect: 'rotate3d(0, 1, 0, 0deg)'},
    272  {at: 0.25, expect: 'rotate3d(0, 1, 0, 112.5deg)'},
    273  {at: 0.75, expect: 'rotate3d(0, 1, 0, 337.5deg)'},
    274  {at: 1, expect: 'rotate3d(0, 1, 0, 450deg)'},
    275  {at: 2, expect: 'rotate3d(0, 1, 0, 900deg)'},
    276 ]);
    277 test_interpolation({
    278  property: 'transform',
    279  from: 'rotate3d(1, 1, 0, 90deg)',
    280  to: 'rotate3d(0, 1, 1, 180deg)'
    281 }, [
    282  {at: -1, expect: 'rotate3d(0.41, -0.41, -0.82, 120deg)'},
    283  {at: 0, expect: 'rotate3d(1, 1, 0, 90deg)'},
    284  {at: 0.25, expect: 'rotate3d(0.524083, 0.804261, 0.280178, 106.91deg)'},
    285  {at: 0.75, expect: 'rotate3d(0.163027, 0.774382, 0.611354, 153.99deg)'},
    286  {at: 1, expect: 'rotate3d(0, 1, 1, 180deg)'},
    287  {at: 2, expect: 'rotate3d(0.71, 0, -0.71, 90deg)'},
    288 ]);
    289 test_interpolation({
    290  property: 'transform',
    291  from: 'none',
    292  to: 'rotate(90deg)'
    293 }, [
    294  {at: -1, expect: 'rotate(-90deg)'},
    295  {at: 0, expect: 'rotate(0deg)'},
    296  {at: 0.25, expect: 'rotate(22.5deg)'},
    297  {at: 0.75, expect: 'rotate(67.5deg)'},
    298  {at: 1, expect: 'rotate(90deg)'},
    299  {at: 2, expect: 'rotate(180deg)'},
    300 ]);
    301 test_interpolation({
    302  property: 'transform',
    303  from: 'rotate(90deg)',
    304  to: 'none'
    305 }, [
    306  {at: -1, expect: 'rotate(180deg)'},
    307  {at: 0, expect: 'rotate(90deg)'},
    308  {at: 0.25, expect: 'rotate(67.5deg)'},
    309  {at: 0.75, expect: 'rotate(22.5deg)'},
    310  {at: 1, expect: 'rotate(0deg)'},
    311  {at: 2, expect: 'rotate(-90deg)'},
    312 ]);
    313 test_interpolation({
    314  property: 'transform',
    315  from: 'rotateX(0deg) rotateY(0deg) rotateZ(0deg)',
    316  to: 'rotateX(700deg) rotateY(800deg) rotateZ(900deg)'
    317 }, [
    318  {at: -1, expect: 'rotateX(-700deg) rotateY(-800deg) rotateZ(-900deg)'},
    319  {at: 0, expect: 'rotateX(0deg) rotateY(0deg) rotateZ(0deg)'},
    320  {at: 0.25, expect: 'rotateX(175deg) rotateY(200deg) rotateZ(225deg)'},
    321  {at: 0.75, expect: 'rotateX(525deg) rotateY(600deg) rotateZ(675deg)'},
    322  {at: 1, expect: 'rotateX(700deg) rotateY(800deg) rotateZ(900deg)'},
    323  {at: 2, expect: 'rotateX(1400deg) rotateY(1600deg) rotateZ(1800deg)'},
    324 ]);
    325 </script>