testing/web-platform/tests/web-animations/animation-model/combining-effects/effect-composition.html
author Stephen McGruer <smcgruer@chromium.org>
Wed, 14 Aug 2019 10:56:48 +0000
changeset 488116 b9780850967fe06ee0e91cf86d055554d69996f2
parent 396582 368eea9148057dd4617da8e18b8db17b7fbc9889
permissions -rw-r--r--
Bug 1572755 [wpt PR 18356] - Fix bug where composite: replace did not replace previous effects, a=testonly Automatic update from web-platform-tests Fix bug where composite: replace did not replace previous effects Consider a set of effects: e.animate([{ "width": "0" }, { "width": "5px" }], { duration: 100 }); e.animate([{ "width": "0" }, { "width": "5px" }], { duration: 100, composite: 'add' }); e.animate([{ "width": "0" }, { "width": "2px" }], { duration: 100 }); e.animate([{ "width": "0" }, { "width": "2px" }], { duration: 100, composite: 'add' }); Previously the code in CopyToActiveInterpolationsMap would incorrectly move the third effect to the start of the list to replace the original keyframe pair, resulting in an effect stack of: { "width": "0" }, { "width": "2px" } { "width": "0" }, { "width": "5px" } { "width": "0" }, { "width": "2px" } This is wrong; not only has it retained an effect it shouldn't have, it has also re-ordered keyframes which might break non-commutative additive properties. This CL fixes the logic to properly clear out existing effects when a composite: 'replace' effect is put onto the stack. Bug: 992378 Change-Id: I94ae54429ac7d4d28a0702d397ab64c2e45dee65 Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1746302 Commit-Queue: Stephen McGruer <smcgruer@chromium.org> Reviewed-by: Yi Gu <yigu@chromium.org> Cr-Commit-Position: refs/heads/master@{#686024} -- wpt-commits: a851a6a8e8b11160bcd6f5861553cf8419258136 wpt-pr: 18356

<!doctype html>
<meta charset=utf-8>
<title>Effect composition</title>
<link rel="help" href="https://drafts.csswg.org/web-animations/#effect-composition">
<script src=/resources/testharness.js></script>
<script src=/resources/testharnessreport.js></script>
<script src="../../testcommon.js"></script>
<div id="log"></div>
<script>
'use strict';

for (const composite of ['accumulate', 'add']) {
  test(t => {
    const div = createDiv(t);
    div.style.marginLeft = '10px';
    const anim =
      div.animate({ marginLeft: ['0px', '10px'], composite }, 100);

    anim.currentTime = 50;
    assert_equals(getComputedStyle(div).marginLeft, '15px',
      'Animated margin-left style at 50%');
  }, `${composite} onto the base value`);

  test(t => {
    const div = createDiv(t);
    const anims = [];
    anims.push(div.animate({ marginLeft: ['10px', '20px'],
                             composite: 'replace' },
                           100));
    anims.push(div.animate({ marginLeft: ['0px', '10px'],
                             composite },
                           100));

    for (const anim of anims) {
      anim.currentTime = 50;
    }

    assert_equals(getComputedStyle(div).marginLeft, '20px',
      'Animated style at 50%');
  }, `${composite} onto an underlying animation value`);

  test(t => {
    const div = createDiv(t);
    div.style.marginLeft = '10px';
    const anim =
      div.animate([{ marginLeft: '10px', composite },
                   { marginLeft: '30px', composite: 'replace' }],
                  100);

    anim.currentTime = 50;
    assert_equals(getComputedStyle(div).marginLeft, '25px',
      'Animated style at 50%');
  }, `Composite when mixing ${composite} and replace`);

  test(t => {
    const div = createDiv(t);
    div.style.marginLeft = '10px';
    const anim =
      div.animate([{ marginLeft: '10px', composite: 'replace' },
                   { marginLeft: '20px' }],
                  { duration: 100 , composite });

    anim.currentTime = 50;
    assert_equals(getComputedStyle(div).marginLeft, '20px',
      'Animated style at 50%');
  }, `${composite} specified on a keyframe overrides the composite mode of`
     + ' the effect');

  test(t => {
    const div = createDiv(t);
    div.style.marginLeft = '10px';
    const anim =
      div.animate([{ marginLeft: '10px', composite: 'replace' },
                   { marginLeft: '20px' }],
                  100);

    anim.effect.composite = composite;
    anim.currentTime = 50;                       // (10 + (10 + 20)) * 0.5
    assert_equals(getComputedStyle(div).marginLeft, '20px',
      'Animated style at 50%');
  }, 'unspecified composite mode on a keyframe is overriden by setting'
      + ` ${composite} of the effect`);
}

test(t => {
  const div = createDiv(t);
  const anims = [];
  anims.push(div.animate({ marginLeft: ['10px', '20px'],
                           composite: 'replace' },
                         100));
  anims.push(div.animate({ marginLeft: ['0px', '10px'],
                           composite: 'add' },
                         100));
  // This should fully replace the previous effects.
  anims.push(div.animate({ marginLeft: ['20px', '30px'],
                           composite: 'replace' },
                         100));
  anims.push(div.animate({ marginLeft: ['30px', '40px'],
                           composite: 'add' },
                         100));

  for (const anim of anims) {
    anim.currentTime = 50;
  }

  // The result of applying the above effect stack is:
  //  underlying = 0.5 * 20 + 0.5 * 30 = 25
  //  result = 0.5 * (underlying + 30px) + 0.5 * (underlying + 40px)
  //         = 60
  assert_equals(getComputedStyle(div).marginLeft, '60px',
    'Animated style at 50%');
}, 'Composite replace fully replaces the underlying animation value');

</script>