Search code examples
javascriptvue.jssvgquasar-frameworksvg-animate

SVG animation endEvent not firing after migrating from Vue 2 to Vue 3


I'm currently migrating a vue 2 / Quasar 1 project to vue 3 / Quasar 2 and I'm struggling with an event on an SVG animation element that doesn't seem to fire in the new version....

<animateTransform
    begin="startAnimation.begin"
    attributeName="transform"
    attributeType="XML"
    type="rotate"
    from="-18 150 380"
    to="0 150 380"
    dur="0.6s"
    repeatCount="1"
    fill="freeze"
    calcMode="spline"
    keySplines="0.3 0.7 0.3 0.7"
    keyTimes="0;1"
    @endEvent="animationEndHandler()"
/>

The endEvent event (https://developer.mozilla.org/en-US/docs/Web/API/SVGAnimationElement/endEvent_event) works fine in the old version but nothing happens in the new one. Everything else is the same and I have tried reducing the animationEndHandler method to nothing but a console.log('hello')

I've tried in Chrome and Safari on Mac, and built with Cordova for iOS, so I'm confident it's not a browser issue as the old version works in the same browsers.

Just because I ran out of anything else to try, I tried all of the following too...

@endEvent="animationEndHandler"
@endevent="animationEndHandler"
@end-event="animationEndHandler"
@endevent="animationEndHandler()"
@end-event="animationEndHandler()"
v-on:endEvent="animationEndHandler()"
v-on:endEvent="animationEndHandler"
v-on:endevent="animationEndHandler()"
v-on:endevent="animationEndHandler"
v-on:end-event="animationEndHandler()"
v-on:end-event="animationEndHandler"

When I inspect the <animateTransform> element in chrome dev tools and look in the Event Listeners tab on the right hand side I can see "endevent" in there, and my animationEndHandler referenced in the handler, but it never seems to get called.

If anyone can offer any suggestions as to why this no longer works under Vue 3 I would be massively grateful!


Solution

  • The cause is not clear to me (perhaps a Vue 3 bug), but you could workaround it by declaring your own custom directive that mimics v-on (for which the @ is shorthand).

    For example, locally register a directive, named on2:

    export default {
      directives: {
        on2(el, binding) {
          // <div v-on2:foo="bar">  ->  binding = { arg: 'foo', value: this.bar }
          el.addEventListener(binding.arg, binding.value);
        },
      },
    }
    

    Or globally register the directive:

    createApp(...)
      .directive('on2', (el, binding) => {
        el.addEventListener(binding.arg, binding.value);
      })
      .mount('#app')
    

    And use it in your template:

    <animateTransform v-on2:endEvent="animationEndHandler" />
    

    Vue 3 demo