之前從底層實現過動畫效果的拖動排序,見此文
也初步領略過<transtion-group>的威力,在不使用JavaScript的情況下實現動效柱形圖
這次就把到手的武器改造一下之前的拖動排序的例子,就仨字——太爽啦!
樣式同前
ul.item { list-style-type: disc; margin-block-start: 0; margin-block-end: 0; margin-inline-start: 0; margin-inline-end: 0; padding-inline-start: 0; } .item li { height: 50px; line-height: 50px; border: 1px solid #aaaaaa; width: 200px; padding: 0 10px; display: flex; align-items: center; }
先把DOM渲染出來
new Vue({ el: "#drag", data: { draggingItem: null, lastItem: null, items: Array.from({ length: 5 }, (_item, index) => ({ key: index + 1 + "", name: "Item" + (index + 1), })), }, });
模板是異常的簡單
<transition-group name="flip-list" tag="ul" class="item"> <li draggable="true" :key="item.key" v-for="item of items" {{item.name}} </li> </transition-group>
然后可以在<transition-group>加name了。
<transition-group name="flip-list" tag="ul" class="item"> <!-- 省略 --> </transition-group>
當然也要加上配套的樣式
.flip-list-move { transition: transform 0.3s; }
下面就是給子元素加上事件了
<li draggable="true" :key="item.key" v-for="item of items" @dragstart="dragstart(item)" @dragover="dragover(item)"> {{item.name}} </li>
就兩個事件
dragstart: function (item) { this.draggingItem = item; }, dragover: function (item) { if (item !== this.draggingItem && this.lastItem !== item) { const fromIndex = this.items.indexOf(this.draggingItem); const toIndex = this.items.indexOf(item); const temp = this.items[fromIndex]; this.items[fromIndex] = this.items[toIndex]; this.items[toIndex] = temp; this.items = [...this.items]; } this.lastItem = item; }
這里我和之前的寫法略作了改動,判斷拖動元素和拖入元素不是同一個,還判斷了拖入元素和上次拖入元素也不是同一個,不然會發生不停抖動的情況,因為拖動的時候發生了元素移動,導致反復觸發dragover事件。
注意最后這里的this.items = [...this.items],因為上面是直接在索引上修改數組元素的,而Vue2的響應式對數組在索引上的修改察覺不到,所以重新給this.items賦值,不過好在有key的加持,Vue只會針對修改的部分進行更新,無須擔心性能問題。
整個的解決方案把所有HTML、CSS代碼都算入也就不滿80行,讓我切切實實感受到了<transition-group>的威力——大殺器也,如果在Vue應用中有列表型元素的動畫需求,第一考慮的就該是它。
