Djave
Djave

Reputation: 9329

How to reset CSS animations in Vue

I have a list like this:

var v = new Vue({
  'el' : '#app',
  'data' : {
    'list' : [1,2,3,4,5,6,7,8,9,10]
  },
  
  methods: {
    activateClass($event){
      $event.target.classList.remove('animate');
      void $event.target.offsetWidth;
      $event.target.classList.add('animate');
    },
    changeRandomValue(){
      var randomAmount = Math.round(Math.random() * 12);
      var randomIndex = Math.floor(Math.random() * this.list.length);
      Vue.set(this.list, randomIndex, randomAmount)
    }
  },
  
  mounted(){
    var vm = this;
    setInterval(function(){
      vm.changeRandomValue();
    }, 500);
  }
})
.animate{
  animation: fade 0.5s;
}

@keyframes fade{
  0% { background:blue; }
  100% { background:white; }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.3/vue.min.js"></script>
<div id="app">
  <ul>
    <li v-for="item in list" v-html="item" @click="activateClass($event)"></li>
  </ul>
</div>

If you run the above snippet, you'll see that if you click an li it will use this code:

activateClass($event){
  $event.target.classList.remove('animate');
  void $event.target.offsetWidth;
  $event.target.classList.add('animate');
}

To add a class to it once and play an animation (https://css-tricks.com/restart-css-animation/). Awesome!

Now, I also have a changeRandomValue() function that selects one element from the list array and change its value.

How would I use the activateClass method from inside of the changeRandomValue method?

I've had a few thoughts about using events on the element, so it would be something like:

<li v-for="item in list" v-html="item"
    @click="activateClass($event)"
    @myValueChanged="activateClass($event)"
></li>

But I don't think anything like that exists. I have also been looking into watchers but I don't think this is really what they are for.

I have no problem taking the element that has been clicked and finding the data that it is referencing, but I can't work out how to take the data that has been changed and then find its dom reference.

The reason I'm not using class bindings is that I need to trigger a reflow. Maybe there is a really easy way to do that with vue, but I'm not aware of it.

Upvotes: 8

Views: 6075

Answers (2)

Etheryte
Etheryte

Reputation: 25328

One easy solution would be to use class bindings and the animationend event.
You can also trigger the same solution programmatically, as shown in the mounted event.

new Vue({
  el: '#app',
  data: {
    items: [{
      id: 1,
      highlight: false
    }, {
      id: 2,
      highlight: false
    }]
  },
  mounted() {
    // Demonstrate programmatic highlighting
    setTimeout(() => {
      this.items[1].highlight = true
      setTimeout(() => {
        this.items[1].highlight = true
      }, 1000)
    }, 1000)
  },
  methods: {
    addClass(item) {
      item.highlight = true
    },
    removeClass(item) {
      item.highlight = false
    }
  }
})
p {
  cursor: pointer;
}

.animate {
  animation: fade 0.5s;
}

@keyframes fade {
  0% {
    background: blue;
  }
  100% {
    background: white;
  }
}
<script src="https://unpkg.com/vue@2"></script>

<div id="app">
  <p v-for="item in items" v-bind:class="{ animate: item.highlight }" v-on:click="addClass(item)" v-on:animationend="removeClass(item)">{{ item.id }}</p>
</div>

Upvotes: 5

Comfort Eagle
Comfort Eagle

Reputation: 2462

Similar to @Etheryte's solution, but shorter:

v-on:click="($event) => $event.target.classList.add('my-class')"
v-on:animationend="($event) => $event.target.classList.remove('my-class')"

Upvotes: 4

Related Questions