最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to animate the sorting of a list with Vue.js - Stack Overflow

programmeradmin4浏览0评论

I’m trying to animate the sorting of a list with Vue.js, but not all items are animated. Do you know why? And how to make it work?

new Vue({
  el: '#app',
  data: {
    reverse: 1,
    items: [
      { name: 'Foo' },
      { name: 'Bar' },
      { name: 'Baz' },
      { name: 'Qux' }
    ]
  }
})
.moving-item {
  transition: all 1s ease;
  -webkit-transition: all 1s ease;
}
ul {
  list-style-type: none;
  padding: 0;
  position: relative;
}
li {
  position: absolute;
  border: 1px solid #42b983;
  height: 20px;
  width: 150px;
  padding: 5px;
  margin-bottom: 5px;
  color: #42b983;
}
<script src=".0.0-alpha.2/vue.min.js"></script>
<div id="app">
  <button on-click="reverse = Math.abs(reverse-1)">
    <span v-if="reverse == 0">△</span>
    <span v-if="reverse == 1">▽</span> Order
  </button>
  <ul>
    <li class="moving-item" v-for="item in items | orderBy 'name' reverse" bind-style="{ top: ($index * 35) + 'px'}">{{ item.name }}</li>
  </ul>  
</div>

I’m trying to animate the sorting of a list with Vue.js, but not all items are animated. Do you know why? And how to make it work?

new Vue({
  el: '#app',
  data: {
    reverse: 1,
    items: [
      { name: 'Foo' },
      { name: 'Bar' },
      { name: 'Baz' },
      { name: 'Qux' }
    ]
  }
})
.moving-item {
  transition: all 1s ease;
  -webkit-transition: all 1s ease;
}
ul {
  list-style-type: none;
  padding: 0;
  position: relative;
}
li {
  position: absolute;
  border: 1px solid #42b983;
  height: 20px;
  width: 150px;
  padding: 5px;
  margin-bottom: 5px;
  color: #42b983;
}
<script src="https://cdnjs.cloudflare./ajax/libs/vue/1.0.0-alpha.2/vue.min.js"></script>
<div id="app">
  <button on-click="reverse = Math.abs(reverse-1)">
    <span v-if="reverse == 0">△</span>
    <span v-if="reverse == 1">▽</span> Order
  </button>
  <ul>
    <li class="moving-item" v-for="item in items | orderBy 'name' reverse" bind-style="{ top: ($index * 35) + 'px'}">{{ item.name }}</li>
  </ul>  
</div>

Share Improve this question edited Sep 7, 2015 at 20:23 Samuel De Backer asked Sep 4, 2015 at 22:36 Samuel De BackerSamuel De Backer 3,4612 gold badges27 silver badges38 bronze badges
Add a ment  | 

2 Answers 2

Reset to default 7

I believe the problem is that only one of the elements is remaining in the DOM during the sort. The other three are being removed and reinserted to satisfy the new ordering – but as a result they are not triggering an animation.

Typically, animation is done using the Vue transition system (http://vuejs/guide/transitions.html). However, the same basic problem of deletion and reinsertion not tracking position state will occur using that technique. Usually, items are animated independent of their previous and new positions (like fade-out in their old position and fade-in in their new one).

If you really need to animate from the the old position to the new one, I think you would need to write your own Javascript transition that remembers the previous position of each item before it is removed and animates it to the new position when it is inserted.

There is an example here which should be a good starting point: http://vuejs/guide/transitions.html#JavaScript_Only_Transitions

Another option is to not sort by a filter and do it in javascript instead (so that the v-for only renders once). Then target your bind-style against a new index parameter on your items like this:

new Vue({
  el: '#app',
  data: {
    reverse: 1,
    items: [
      { name: 'Foo', position: 0 },
      { name: 'Bar', position: 1 },
      { name: 'Baz', position: 2 },
      { name: 'Qux', position: 3 }
    ]
  },
  methods: {
    changeOrder: function (event) {
      var self = this;
      self.reverse = self.reverse * -1
      var newItems = self.items.slice().sort(function (a, b) { 
        var result;
        if (a.name < b.name) {
          result = 1
        }
        else if (a.name > b.name) {
          result = -1
        }
        else {
          result = 0
        }
        return result * self.reverse
      })
      newItems.forEach(function (item, index) {
        item.position = index;
      });
    }
  }
})
 
.moving-item {
  transition: all 1s ease;
  -webkit-transition: all 1s ease;
}
ul {
  list-style-type: none;
  padding: 0;
  position: relative;
}
li {
  position: absolute;
  border: 1px solid #42b983;
  height: 20px;
  width: 150px;
  padding: 5px;
  margin-bottom: 5px;
  color: #42b983;
}
<script src="https://cdnjs.cloudflare./ajax/libs/vue/1.0.0-alpha.2/vue.min.js"></script>
<div id="app">
  <button on-click="changeOrder">
    <span v-if="reverse == -1">△</span>
    <span v-if="reverse == 1">▽</span> Order
  </button>
  <ul>
    <li class="moving-item" v-for="item in items" bind-style="{ top: (item.position * 35) + 'px'}">{{ item.name }}</li>
  </ul>  
</div>

Edit
The Vue 3 contain an example for this exact use case: https://vuejs/guide/built-ins/transition-group.html#move-transitions

V2 has this built in: https://v2.vuejs/v2/guide/transitions.html#Transition-Modes

Check out "List Move Transitions" and the example with the shuffle button.

发布评论

评论列表(0)

  1. 暂无评论