anime.js: как обратить svg морфинг - PullRequest
3 голосов
/ 06 апреля 2019

Я пытался установить direction: 'reverse', а также timeline.reverse(), но это не работает. Если я установлю loop: true, то вы можете увидеть обратную анимацию в цикле. Но я хочу вызвать это, например, через кнопка. Вот кодекс для чистого javascript и здесь версия с 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>

1 Ответ

1 голос
/ 17 апреля 2019

Используя timeline.reverse(), вы можете изменить направление анимации на временной шкале.

Я поместил временную шкалу в охватывающую область, поэтому вам не нужно перестраивать временную шкалу при каждом щелчке, иможно просто отменить его, когда произойдет щелчок.

Я также использовал прослушиватель событий для привязки функции вместо встроенного прослушивателя событий html.

Важно, чтобы вы использовали animejsv2.2.0, потому что API в терминах offset и timeline изменился в более новых версиях, поэтому этот пример не будет работать должным образом в более новых версиях.

Вот чистый пример 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>
...