3

I tried to set direction: 'reverse' and also timeline.reverse(), but it doesn't work. If I set loop: true, then you can see the reverse animation in the loop. But I want to trigger it via e.g. a button. Here is a codepen for pure javascript and here is a version with vue.js.

function doFunction() {

var paths = [
  {id: '#path309', d: 'm 55.184523,127.42857 34.962798,139.6183 43.46726,120.24702 z'}, 
  {id: '#path311', d: 'm 54.389347,121.02185 -10.922087,-0.77483 11.717263,7.18155 z'}
];

var timeline = anime.timeline({ autoplay: true, direction: 'alternate', loop: false });

paths.forEach(function(path, index) {
  timeline
  .add({
    targets: path.id,
    d: {
      value: path.d,
      duration: 1000,
      easing: 'easeInOutQuad'
    },
    offset: 1000 + 10 * index
  });
});

}
<script src='https://cdnjs.cloudflare.com/ajax/libs/animejs/2.2.0/anime.min.js'></script>
<input id="clickMe" type="button" value="clickme" onclick="doFunction();" />  

<svg xmlns="http://www.w3.org/2000/svg" width="100mm" height="100mm" viewBox="0 0 200 200">

  <path d="m 110.51595,80.797791 26.28083,12.271789 2.96257,10.935445 z" id="path309" />
  <path d="m 70.51595,80.797791 -5.00762,25.058566 11.28845,-2.786777 z" id="path311" />

</svg>
sunwarr10r
  • 4,420
  • 8
  • 54
  • 109

1 Answers1

2

By using timeline.reverse() you can reverse the direction of the timeline to be animated.

I placed the timeline in an encompassing scope, so you don't have to rebuild the timeline on each click, and can just reverse it when a click occurs.

I also used an event Listener to bind the function, instead of an html inline event listener.

It is important, that you use animejs v2.2.0, because the API in terms of offset and timeline has changed in newer versions, so this example will not work proper in newer versions.

Here is the pure JavaScript example:

/**
 * We start in a scoped function to store our timeline there
 */
+function(button) {
  /**
   * A flag to know if we need to call reverse or not, we shouldn't on the first click
   */
  var firstRun = true;
  
  var paths = [
        {id: '#path309', d: 'm 55.184523,127.42857 34.962798,139.6183 43.46726,120.24702 z'}, 
        {id: '#path311', d: 'm 54.389347,121.02185 -10.922087,-0.77483 11.717263,7.18155 z'}
  ];
  /**
   * Disable the autoplay, we don't wanna jump the gun. 
   * Of if you do, remove the firstRun variable and if check.
   */ 
  var timeline = anime.timeline({ autoplay: false, direction: 'alternate', loop: false });

  paths.forEach(function(path, index) {
    timeline
      .add({
        targets: path.id,
        d: {
          value: path.d,
          duration: 1000,
          /**
           * I changed your chosen transform it because the delay 
           * your transform caused an almost second delay before
           * it started animating.
           */
          easing: 'easeInOutSine'
        },
        offset: 1000 + 10 * index
      });
  });
  /**
   * Listening to the click on the button
   */
  button.addEventListener('click', function() {
    /**
     * If the animation has run, reverse the timeline
     */
    if(!firstRun) {
       timeline.reverse();
    }    
    /**
     * Play the animation!
     */ 
    timeline.play();
    /**
     * We've run once. TIme to enable the reversing of the timeline.
     */
    firstRun = false;
  });
}(document.getElementById("clickMe"));
<script src='https://cdnjs.cloudflare.com/ajax/libs/animejs/2.2.0/anime.min.js'></script>
<input id="clickMe" type="button" value="clickme" />  
<svg xmlns="http://www.w3.org/2000/svg" width="100mm" height="100mm" viewBox="0 0 200 200">

  <path d="m 110.51595,80.797791 26.28083,12.271789 2.96257,10.935445 z" id="path309" />
  <path d="m 70.51595,80.797791 -5.00762,25.058566 11.28845,-2.786777 z" id="path311" />

</svg>

And here is the vue.js example:

new Vue({
  el: '#app',
  data() { 
    return {
      timeline: anime.timeline({ autoplay: false, direction: 'alternate', loop: false }),
      prepared: false,
      switch1: false,
      paths: [
        {id: '#path309', d: 'm 55.184523,127.42857 34.962798,139.6183 43.46726,120.24702 z'}, 
        {id: '#path311', d: 'm 54.389347,121.02185 -10.922087,-0.77483 11.717263,7.18155 z'}
      ]
    }
  },
  
  watch: {
    //called whenever switch1 changes
    switch1(switchVal){
      this.transform(switchVal)
    }
  },

  methods: {
    /**
     *  Call prepare later on, because otherwise the dom doesn't really seem initalized when the code isn't placed at the end of the document. Otherwise you can use created: to do this and a seperate flag for the first run.
     */
    prepare:  function() {
      var timeline = this.timeline;
      this.paths.forEach(function(path, index) {
        timeline.add({
            targets: path.id,
            d: {
              value: path.d,
              duration: 1000,
              easing: 'easeInOutSine'
            }, offset: 1000 + 10 * index
          })
        });
      
      this.prepared = true;
    },
    transform: function(direction) {
      if(!this.prepared) {
        this.prepare();
      }
      else {
        this.timeline.reverse();
      }
     this.timeline.play();
    }
  }   
})
#app {
   padding-bottom:200px;
}
<link rel='stylesheet' href='https://unpkg.com/vuetify/dist/vuetify.min.css'>
<script src='https://unpkg.com/vue/dist/vue.js'></script>
<script src='https://unpkg.com/vuetify/dist/vuetify.min.js'></script>
<script src='https://unpkg.com/vue-i18n/dist/vue-i18n.js'></script>
<script src='https://unpkg.com/animejs@2.2.0/anime.min.js'></script>

<div id="app">

  <svg 
    xmlns="http://www.w3.org/2000/svg" 
    width="100mm" 
    height="100mm" 
    viewBox="0 0 200 200"
  >
      
    <path d="m 110.51595,80.797791 26.28083,12.271789 2.96257,10.935445 z" id="path309" />
    <path d="m 70.51595,80.797791 -5.00762,25.058566 11.28845,-2.786777 z" id="path311" />
      
  </svg>
  
  <v-switch 
    v-model="switch1" 
    label="Switch to transform and reverse"
    color="green"
  ></v-switch>
  
</div>
sunwarr10r
  • 4,420
  • 8
  • 54
  • 109
Tschallacka
  • 27,901
  • 14
  • 88
  • 133
  • 1
    Thank you for the answer. I tried to reproduce this with vue.js and unfortunately `timeline.reverse()` doesn't do easing, it just jumps back. Could you take a look at it? https://codepen.io/saitam1/pen/gyerEX – sunwarr10r Apr 18 '19 at 14:06
  • 1
    Yea, the problem is that you rebuild your timeline every time. You build the timeline ONCE. And then act on it. You don't remember the state, you do reverse or whatever, but that's not how it works, you need to reverse **everytime** after you started reversing, because how the timeline's state is changed. I'll have a look see how I can modify your pen that it'll actually work. But you should have added that in the original question that you wanted it to work with vue, even though the core concepts stay the same. – Tschallacka Apr 18 '19 at 14:24
  • 1
    @saitam I added a new snippet to my answer to illustrate how to use the concepts I talked about in my answer in vue.js You can see it in codepen at https://codepen.io/anon/pen/rbdKeq – Tschallacka Apr 18 '19 at 14:47