anime.js: 如何反转 svg 变形

anime.js: how to reverse svg morphing

我尝试设置 direction: 'reverse'timeline.reverse(),但它不起作用。如果我设置loop: true,那么你可以在循环中看到反向动画。但我想通过例如触发它一个按钮。这里有一个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>

通过使用timeline.reverse(),您可以反转要动画化的时间轴的方向。

我把时间线放在一个包含范围内,所以你不必在每次点击时重建时间线,并且可以在点击发生时反转它。

我还使用事件监听器来绑定函数,而不是 html 内联事件监听器。

使用 animejs v2.2.0 很重要,因为 offsettimeline 方面的 API 在新版本中发生了变化,所以这个示例在较新版本中将无法正常工作。

这是纯粹的 JavaScript 示例:

/**
 * 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>

这里是 vue.js 示例:

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>