Minty's A-Frame Tips & Tricks : Looping Animation Timeline
Tags: aframe, webxr, webdev
Published: 2023-12-06 10:07:13

Hello, my name is Minty Crisp. I am a Web XR developer with a speciality in A-Frame. This is the 1st post in a series I am calling Minty's A-Frame Tips & Tricks where I will share hopefully unique and informative ways of using A-Frame that I have learned on my journey so far.

Check out my portfolio @ <a href="https://mintycrisp.com" target="_blank">MintyCrisp.com</a> to see more of my projects, socials, links and contact info.

If you enjoyed this content or any of my other work, please consider sharing or sponsoring me, so that I may be able to deliver better content more often. You can support me on <a href="https://ko-fi.com/mintycrisp" target="_blank">Ko-fi</a>

And if you are in need of an A-Frame Developer, Debugger or Tutor, I am available.

<hr />

View, follow along & remix these examples on <a href="https://glitch.com/~mintycrisp-aframe-animation-looping" target="_blank">Glitch</a> . Examples start from the far left and move to the right with each subsequent addition. Use your mouse to rotate the view and WASD keys to move the player controller.

{% glitch https://mintycrisp-aframe-animation-looping.glitch.me %}

Animating in A-Frame is very straight forward. You can find the document overview for this component here -> <a href="https://aframe.io/docs/1.5.0/components/animation.html" target="_blank">A-Frame 1.5.0 Animation</a>.

Let's start with looping a single animation, which is very basic. Just by setting the 'loop' property to 'true' or a number, it will repeat your animation. You can utilize 'easing' and direction 'dir' for greater effect such as moving back and forth, but lets focus on looping.

```html
<!-- Basic Animation Loop -->
<a-entity
id='animTest0'
position="-2 1 -3" 
geometry="primitive: box; height: 1; width: 1; depth: 1;"
material="shader: flat; color:#4CC3D9"

animation__basic="property: object3D.position.z; from: -3; to: -6; dur: 3000; delay: 0; loop: true; dir: normal; easing: linear; elasticity: 400; autoplay: true; enabled: true;"
></a-entity>
```
This will animate our entity to move backwards and repeat that animation forever. Basic, but effective. Now, playing a set of animations in succession is also pretty basic. Just add multiple animations and calculate the delay for each subsequent animation, so that they play one after another. 

```html
<!-- Animation Set via Delay -->
<a-entity
id='animTest1'
position="-1 1 -3" 
geometry="primitive: box; height: 1; width: 1; depth: 1;"
material="shader: flat; color:#EF2D5E"

animation__set1="property: object3D.position.z; from: -3; to: -6; dur: 3000; delay: 0; loop: false; dir: normal; easing: linear; elasticity: 400; autoplay: true; enabled: true;"

animation__set2="property: object3D.position.y; from: 1; to: 3; dur: 3000; delay: 3000; loop: false; dir: normal; easing: linear; elasticity: 400; autoplay: true; enabled: true;"
></a-entity>
```

This works okay, but is not ideal. The next step is to instead of calculating the delay for each one we can have the next animation listen for the previous animation's completion. To do so, you can utilize the 'startEvents' property with the animation's event which fires when it completes. 
```
The event that fires is named 'animationcomplete__name' with 'name' being the name of the animation such as 'taco' with 'animation__taco'.
```
```html
<!-- Animation Set via Events -->
<a-entity
id='animTest2'
position="0 1 -3" 
geometry="primitive: box; height: 1; width: 1; depth: 1;"
material="shader: flat; color:#FFC65D"

animation__set1="property: object3D.position.z; from: -3; to: -6; dur: 3000; delay: 0; loop: false; dir: normal; easing: linear; elasticity: 400; autoplay: true; enabled: true;"

animation__set2="property: object3D.position.y; from: 1; to: 3; dur: 3000; delay: 0; loop: false; dir: normal; easing: linear; elasticity: 400; autoplay: false; enabled: true; startEvents: animationcomplete__set1"
></a-entity>
```

That's better, but what if we want this set of animations to loop? For that, we can utilize the same technique we just used and have the 1st animation listen for the last animation's completion. While that itself is straight forward, using the 'startEvents' property introduces a caveat in the timeline logic. It causes the animation component to ignore the 'autoplay' property. That means when we use this technique to loop a set of animations, the very first animation will never fire automatically even if we set 'autoplay' to 'true'. So if we want this set of animations to start as soon as it is added, then we need to set up a precursor animation to start the loop chain. We can set any normal property to be animated with a duration of '0' and the 'to' / 'from' property equal to whatever start value it has. And when adding multiple event conditions to startEvents, you just need to seperate them with a comma ',' .

```html
<!-- Animation Set Loop via Events # 1-->
<a-entity
id='animTest3'
position="1 1 -3" 
geometry="primitive: box; height: 1; width: 1; depth: 1;"
material="shader: flat; color:#7BC8A4"

animation__set0="property: object3D.position.z; from: -3; to: -3; dur: 0; delay: 0; loop: false; dir: normal; easing: linear; elasticity: 400; autoplay: true; enabled: true;"

animation__set1="property: object3D.position.z; from: -3; to: -6; dur: 3000; delay: 0; loop: false; dir: normal; easing: linear; elasticity: 400; autoplay: false; enabled: true; startEvents: animationcomplete__set0, animationcomplete__set2"

animation__set2="property: object3D.position.y; from: 1; to: 3; dur: 3000; delay: 0; loop: false; dir: normal; easing: linear; elasticity: 400; autoplay: false; enabled: true; startEvents: animationcomplete__set1"
></a-entity>
```

Now we are looping! Although my basic 2 axis position animation set loop example doesn't look very good, here is one last example which will have the box move in a looping loop :).

```html
<!-- Animation Set Loop via Events #2 -->
<a-entity
id='animTest4'
position="2 1 -3" 
geometry="primitive: box; height: 1; width: 1; depth: 1;"
material="shader: flat; color:#c87bbf"

animation__set0="property: object3D.position.z; from: -3; to: -3; dur: 0; delay: 0; loop: false; dir: normal; easing: linear; elasticity: 400; autoplay: true; enabled: true;"

animation__set1="property: object3D.position.z; from: -3; to: -6; dur: 3000; delay: 0; loop: false; dir: normal; easing: linear; elasticity: 400; autoplay: false; enabled: true; startEvents: animationcomplete__set0, animationcomplete__set4"

animation__set2="property: object3D.position.y; from: 1; to: 3; dur: 3000; delay: 0; loop: false; dir: normal; easing: linear; elasticity: 400; autoplay: false; enabled: true; startEvents: animationcomplete__set1"

animation__set3="property: object3D.position.z; from: -6; to: -3; dur: 3000; delay: 0; loop: false; dir: normal; easing: linear; elasticity: 400; autoplay: false; enabled: true; startEvents: animationcomplete__set2"

animation__set4="property: object3D.position.y; from: 3; to: 1; dur: 3000; delay: 0; loop: false; dir: normal; easing: linear; elasticity: 400; autoplay: false; enabled: true; startEvents: animationcomplete__set3"
></a-entity>
```

And there you go. Now you can build your own complex looping animations to make your A-Frame scenes sparkle. One last note, you can also use the Animation Timeline component to accomplish the same thing, but with the added bonus of connecting to other entities and their animations. Check out that component -> <a href="https://github.com/supermedium/superframe/tree/master/components/animation-timeline/" target="_blank">here</a>. 

<hr />

That's it for this edition of Minty's A-Frame Tips & Tricks : Looping Animation Timeline.

If you would like to see more of my projects or get in contact with me, check out my portfolio @ <a href="https://mintycrisp.com" target="_blank">MintyCrisp.com</a>.

And if you enjoyed this content or any of my other work, please consider sharing or sponsoring me, so that I may be able to deliver better content more often. You can support me on <a href="https://ko-fi.com/mintycrisp" target="_blank">Ko-fi</a>

Thank you for reading!