skip to Main Content

I’m trying to understand if it’s possible to accumulate animations with the Web API animation and I can’t seem to figure it out.

//move element very fast to 100,100 with no duration
el.animate([{transform: 'translate(100px, 100px)'}], {duration: 0, fill: 'forwards'});

//add another movement after that with duration
el.animate([{transform: 'translateX(200px)'}], {duration: 0, fill: 'forwards', composite: 'accumulate'});

Everything happens properly, the box moves to 100,100 then the animation starts. The problem is, at the end of the animation, the boxes moves up like it has suddenly lost the Y transform. It is possible to keep the Y transform at the end? I thought fill: 'forwards' + composite : 'accumulate' would do that …

Note: once in a while, it does behave properly so I’m not sure what’s going on. The expected behavior is

  • Move box to 100,100
  • Then apply a relative transform, meaning the box would end up at 300,100
const el = document.querySelector('.box');

el.animate([{transform: 'translate(100px, 100px)'}], {duration: 0, fill: 'forwards'});

el.animate([{transform: 'translateX(200px)'}], {duration: 2000, fill: 'forwards', composite: 'accumulate'});
.container{
  display: block;
  height: 100vh;
  overflow: hidden;
}
.box{
  position: absolute;
  width: 300px;
  height: 200px;
  background-color: brown;
 }
<div class="container">
  <div class="box">  
  </div>
</div>

JsFiddle

2

Answers


  1. I think in order to achieve that, you would need to remove the transform and set your animate function with left and top properties.

    As I clearly understand from your post and have tested it a bit, you wanted your current box to remain in the last position where the second animations ends. The transform function I think it is a bit tricky though as it is somehow resetting the Y axis position and it pushes your box to the top of the container for some reason.

    I believe it is better to work both of your animations with the aforementioned properties.

    I am attaching you a reproducible example which for me the animations are working as expected from what you described.

    I hope that can help you with what you seek.

    const el = document.querySelector('.box');
    el.animate([{left: '100px', top:'0'}], {duration: 0, fill: 'both'});
    
    el.animate([{left: '200px', top: '100px'}], {duration: 2000, fill: 'both', composite: 'accumulate'});
    .container{
      display: block;
      height: 100vh;
      overflow: hidden;
    }
    .box{
      position: absolute;
      width: 300px;
      height: 100px;
      background-color: brown;
     }
    <div class="container">
      <div class="box">  
      </div>
    </div>
    Login or Signup to reply.
  2. As far as I remember composite option is not that smart, it compiles different types of transforms into one, but much more like string concatenation. This behavior can change in the future based on different vendors.

    For the time being, you can use DOMMatrix to perform the operations you want prior to animating the element. This gives you quiet a bit of control on how you would want ‘composite’ to behave.

    In the example I am using a dom manipulation library but you can easily get the promise from native Element.animate via finished prop. The rest is getting the current transformation matrix via getComputedStyle and using that to accumulate transformations via DOMMatrix.prototype. In this example, I am using DOMMAtrix.prototype.translateSelf.

    A word of note, using getComputedStyle sparingly in your scripts, you
    usually do this via polling/throttling the function that makes the
    call to it. Another option is to set a property or Symbol on the element that keeps track of the current DOMMatrix so you do not have to call getComputedStyle.

    (async () => {
     const el = document.querySelector('.box');
     await ch(el).animate([{transform: 'translate(100px, 100px)'}], {duration: 2000, fill: 'forwards'}).promiseAnimate();
     const currMatrix = new DOMMatrix(getComputedStyle(el).transform);
     ch.animate([{transform: currMatrix.translateSelf(200)}], {duration: 2000, fill: 'forwards'})
    })()
    .container{
      display: block;
      height: 100vh;
      overflow: hidden;
    }
    .box{
      position: absolute;
      width: 300px;
      height: 200px;
      background-color: brown;
     }
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/cahir.0.0.6.evergreen.umd.min.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/collections/DOM/ch.js"></script>
    <div class="container">
      <div class="box">  
      </div>
    </div>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search