Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[animation-timeline] Animation events: No begin events, complete events only once each #216

Open
janpio opened this issue Feb 4, 2019 · 4 comments

Comments

@janpio
Copy link
Contributor

janpio commented Feb 4, 2019

I am having problems with the events of animation-timeline, and this is the result of my investigation what is going on:


animation:

When we just have animation, we get animationbegin events when the animations start (either automatically or because of startEvents) and animationend when an animation is really finished (loop: true never finish).

Here is my file I used to reproduce and test this (a slightly modified version of https://github.com/supermedium/superframe/tree/master/components/animation/#browser):

<head>
  <title>My A-Frame Scene</title>
  <script src="https://aframe.io/releases/0.8.2/aframe.min.js"></script>
  <script src="https://unpkg.com/aframe-animation-component@^5.1.2/dist/aframe-animation-component.min.js"></script>

<script>
AFRAME.registerComponent('logevents', {
    init: function () {
        console.log('# component updateLookAt, init()')
        var element = this.el;
        element.addEventListener('animationbegin', function(e) {
            console.log('animationbegin', e.detail.name);
        });
        element.addEventListener('animationcomplete', function(e) {
            console.log('animationcomplete', e.detail.name, this.components[e.detail.name].animation);
        });
    }
});
</script>

</head>

<body>
  <a-scene>
    
    <a-entity position="0 0 -4">
        <a-entity logevents position="-2 0 0" geometry="primitive: box" material="color: black"
                animation__color="property: components.material.material.color; type: color; dir: alternate; dur: 1000;
                                    easing: easeInSine; to: #FFF">
        </a-entity>

        <a-entity id="fade" logevents geometry="primitive: box" material="color: orange; transparent: true"
                animation__fadein="property: components.material.material.opacity; dur: 1000; easing: linear; from: 0; to: 1; startEvents: fadein"
                animation__fadeout="property: components.material.material.opacity; dur: 1000; easing: linear; from: 1; to: 0; startEvents: fadeout">
        </a-entity>

        <a-cylinder logevents color="#F55" radius="0.1"
                    animation="property: components.material.material.color; type: color; dir: alternate; dur: 1000;
                            easing: easeInSine; loop: true; to: #5F5"
                    animation__scale="property: scale; dir: alternate; dur: 200;
                            easing: easeInSine; loop: true; to: 1.2 1 1.2"
                    animation__yoyo="property: position; dir: alternate; dur: 1000;
                                    easing: easeInSine; loop: true; to: 0 2 0">
        </a-cylinder>
    </a-entity>

    <a-camera></a-camera>
  </a-scene>
</body>

Executing it logs the following:

...
# component updateLookAt, init()
animation.html:13 animationbegin animation__color
animation.html:9 # component updateLookAt, init()
animation.html:9 # component updateLookAt, init()
animation.html:13 animationbegin animation
animation.html:13 animationbegin animation__scale
animation.html:13 animationbegin animation__yoyo
animation.html:16 animationcomplete animation__color {update: ƒ, begin: undefined, run: undefined, complete: ƒ, loop: 0, …}

When you run document.getElementById('fade').emit('fadein') in the console, you get additional logging:

animationbegin animation__fadein
undefined
animation.html:16 animationcomplete animation__fadein {update: ƒ, begin: undefined, run: undefined, complete: ƒ, loop: 0, …}

This is great! 👍 ✅


animation-timeline:

With animation-timeline I unfortunately do not get the expected events.

I built an example (the example on https://github.com/supermedium/superframe/tree/master/components/animation-timeline#browser doesn't work at all, so built my own) with a very simple timeline: 4 timeline "steps" of 3 animations:

<head>
  <title>My A-Frame Scene</title>
  <script src="https://aframe.io/releases/0.8.2/aframe.min.js"></script>
  <script src="https://unpkg.com/aframe-animation-component@^5.1.2/dist/aframe-animation-component.min.js"></script>
  <script src="https://cdn.jsdelivr.net/gh/supermedium/superframe/components/animation-timeline/dist/aframe-animation-timeline-component.js"></script>

<script>
AFRAME.registerComponent('logevents', {
    init: function () {
        console.log('# component updateLookAt, init()')
        var element = this.el;
        element.addEventListener('animationbegin', function(e) {
            console.log('animationbegin', e.detail.name);
        });
        element.addEventListener('animationcomplete', function(e) {
            console.log('animationcomplete', e.detail.name, this.components[e.detail.name].animation);
        });
    }
});
</script>

</head>

<body>
  <a-scene>
    
    <a-assets>
        <a-timeline id="myTimeline">
            <a-timeline-animation select="#box" name="scale_up"></a-timeline-animation>
            <a-timeline-animation select="#box" name="rotate"></a-timeline-animation>
            <a-timeline-animation select="#box" name="scale_down"></a-timeline-animation>
            <a-timeline-animation select="#box" name="rotate"></a-timeline-animation>
        </a-timeline>
    </a-assets>

    <a-entity id="animation-timeline" animation-timeline__1="timeline: #myTimeline; loop: true;" >
        <a-box id="box" color="red" logevents height="1" width="1" depth="1" scale="1 1 1" position="1 0 -4"
            animation__scale_up="property: scale; from: 1 1 1; to: 2 2 2; dur: 1500; autoplay: false"
            animation__scale_down="property: scale; from: 2 2 2; to: 1 1 1; dur: 1500; autoplay: false"
            animation__rotate="property: rotation; from: 0 0 0; to: 90 90 90; dur: 1500; autoplay: false"
            >
        </a-box>
    </a-entity>

    <a-camera></a-camera>
  </a-scene>
</body>

The output in the console is this:

...
# component updateLookAt, init()
animation-timeline.html:17 animationcomplete animation__scale_up {update: ƒ, begin: undefined, run: undefined, complete: ƒ, loop: 0, …}
animation-timeline.html:17 animationcomplete animation__rotate {update: ƒ, begin: undefined, run: undefined, complete: ƒ, loop: 0, …}
animation-timeline.html:17 animationcomplete animation__scale_down {update: ƒ, begin: undefined, run: undefined, complete: ƒ, loop: 0, …}
animation-timeline.html:17 animationcomplete animation__rotate {update: ƒ, begin: undefined, run: undefined, complete: ƒ, loop: 0, …}

So no animationbegin and although the animation keeps running in the browser, no more animationcomplete after the first run through the complete timeline as well. 👎 ❌

I would have expected a) the animationbegin events to be fired as with just animation, and b) all events to be fired each time the timeline loops - Those are new animations after all

Was my expectation wrong?
Or did I mess up my code (and the example) somehow?


Some background why I need this:

I have a planet with cities. I want to animate an airplane between cities. The plane first has to ascend from the departure city to the apex, then descend to the stopover city. From the stopover it ascends to the second apex and descends into the arrival city. This is done via animation-timeline and works perfectly.

But as the flight path is not a straight line, I have to reorient (rotate) the plane at least in the stopover, so it looks towards the arrival - which can be done with look-at. It would be even greater if it could focus on the apex on ascend, then the city on descend. (And as there are several flight paths that can be switched between, I also have to first orient the plane in departure before starting the animation). That is why I need an event each time the animation is ran, so I can update the value for look_at of my plane.

image


Probably related issues: #137

@janpio
Copy link
Contributor Author

janpio commented Feb 4, 2019

Writing this issue gave me an idea for a partial workaround as well (as it usually does):
If you remove loop:true from the animation-timeline and add a startEvents property, you can listen to the animationtimelinecomplete event of animation-timeline, and just retrigger the animation by firing the event you defined. This will restart the animation from the start, which will give you at least the complete events again.

<head>
  <title>My A-Frame Scene</title>
  <script src="https://aframe.io/releases/0.8.2/aframe.min.js"></script>
  <script src="https://unpkg.com/aframe-animation-component@^5.1.2/dist/aframe-animation-component.min.js"></script>
  <script src="https://cdn.jsdelivr.net/gh/supermedium/superframe/components/animation-timeline/dist/aframe-animation-timeline-component.js"></script>

<script>
AFRAME.registerComponent('logevents', {
    init: function () {
        console.log('# component updateLookAt, init()')
        var element = this.el;
        element.addEventListener('animationbegin', function(e) {
            console.log('animationbegin', e.detail.name);
        });
        element.addEventListener('animationcomplete', function(e) {
            console.log('animationcomplete', e.detail.name);
        });
        element.addEventListener('animationtimelinecomplete', function(e) {
            console.log('animationtimelinecomplete', e.detail.name);
            document.getElementById('animation-timeline').emit('playTimeline')
        });
    }
});
</script>

</head>

<body>
  <a-scene>
    
    <a-assets>
        <a-timeline id="myTimeline">
            <a-timeline-animation select="#box" name="scale_up"></a-timeline-animation>
            <a-timeline-animation select="#box" name="rotate"></a-timeline-animation>
            <a-timeline-animation select="#box" name="scale_down"></a-timeline-animation>
            <a-timeline-animation select="#box" name="rotate"></a-timeline-animation>
        </a-timeline>
    </a-assets>

    <a-entity id="animation-timeline" logevents animation-timeline__1="timeline: #myTimeline; startEvents: playTimeline" >
        <a-box id="box" color="red" logevents height="1" width="1" depth="1" scale="1 1 1" position="1 0 -4"
            animation__scale_up="property: scale; from: 1 1 1; to: 2 2 2; dur: 1500; autoplay: false"
            animation__scale_down="property: scale; from: 2 2 2; to: 1 1 1; dur: 1500; autoplay: false"
            animation__rotate="property: rotation; from: 0 0 0; to: 90 90 90; dur: 1500; autoplay: false"
            >
        </a-box>
    </a-entity>

    <a-camera></a-camera>
  </a-scene>
</body>

@ngokevin
Copy link
Collaborator

ngokevin commented Feb 4, 2019

So is the problem is that the complete events never fire on loop: true? I wouldn't be surprised if so.

@janpio
Copy link
Contributor Author

janpio commented Feb 4, 2019

No, not quite:

  1. We get no *begin events at all for animation-timeline:
    https://glitch.com/~aframe-animation-timeline-noloop
    (But we get both the *complete events from the animations and the animation-timeline).

  2. And for a looping animation-timeline, all the *complete events are only fired once, instead of for each run through the timeline:
    https://glitch.com/~aframe-animation-timeline-loop

@lchsam
Copy link

lchsam commented Nov 27, 2020

I believe I found the reason why there are no animationbegin events for animation-timeline. Perhaps animationcomplete events not firing is of the same reason.

The config object declared in the animation component only contains an animation complete callback, complete: function() {...}, (A-Frame's animation component) and no animation begin callback , begin: function() {...}. The animation-timeline component relies on the config object for firing animationcomplete events. To fix it, I believe we need to pass in an animation begin callback that emits an animationbegin event and not let the animation component emit the event directly.

Modify A-Frame/src/components/animation.js like so:

this.config = {
  // add this new begin callback to the config object
  begin: function() {
     self.el.emit('animationbegin', self.eventDetail, false);
  },
  complete: function () { ... }
}

Remove this.el.emit('animationbegin', this.eventDetail, false); at line 196.

For the second problem with looping, I am not exactly sure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants