i have a vertical list of items, each of which can be removed. I put my items inside a transition-group and created simple opacity and transform transitions for them. The transitions on the removed elements work as expected, however if I remove an element which is not placed at the bottom, the ones beneath just jump up and take its place without any transition. I Can’t find a way to target this behaviour. All I want is just that the elements below slide up smoothly.
Is there any way to achieve this effect by using css transitipms and Vue’s animation hooks?
Here is a demo: https://jsfiddle.net/gcp18nq0/
Template:
<div id="app"> <div class="form"> <label for="name">Name</label> <input type="text" id="name" v-model="name"> <button @click="addPlayer">Add player</button> </div> <div class="players"> <transition-group name="player"> <div class="panel" v-for="player in players" :key="player.id"> <h2> {{ player.name}} <span class="remove" @click="removePlayer(player.id)">Remove</span> </h2> </div> </transition-group> </div> </div>
Script:
data() { return { name: "", players: [ {id: 1, name: 'Player1'}, {id: 2, name: 'Player2'}, {id: 3, name: 'Player3'}, ] } }, methods: { addPlayer: function () { //,,,, }, removePlayer: function (playerId) { //... } } });
CSS
.form { margin:0 auto; width:400px; } .panel { width: 400px; margin: 10px auto; overflow: hidden; border: 1px solid; text-align: center; } .remove { float: right; cursor: pointer; text-decoration: underline; font-size: 12px; vertical-align: bottom } .player-enter, .player-leave-to /* .fade-leave-active below version 2.1.8 */ { opacity: 0; } .player-enter { transform: translateY(30%); } .player-leave-to { transform: translateX(30%); } .player-enter-active, .player-leave-active { transition: all 1.5s; } .player-move { transition: all 1.5s; }
The only working way I found was by adding position:absolute on “player-leave-active” state but since the element collapses it changes its vertical position, which is not the desired effect. I also tried changing the height but there the elements below still jump up a bit after the height is set to 0. Im sure that this can be achieved easily with jQuery but i believe that there should be a way to do it without js.
Thank you in advance!
p.s. its my first post here, so i hope that it was explained clearly enough.
Advertisement
Answer
So I made some small tweaks to your fiddle: https://jsfiddle.net/gcp18nq0/1/ and hopefully that is what you looking for.
The most important change was setting display: inline-block
on the .panel
class, according to the Vue documentation:
One important note is that these FLIP transitions do not work with elements set to display: inline. As an alternative, you can use display: inline-block or place elements in a flex context.
new Vue({ el: "#app", data() { return { name: "", players: [{ id: 1, name: 'Batman' }, { id: 2, name: 'Robin' }, { id: 3, name: 'Superman' }, { id: 4, name: 'Spiderman' }, ] } }, methods: { addPlayer: function() { const newPlayer = { id: this.players.length + 1, name: this.name, }; this.players.push(newPlayer); }, deletePlayer: function(playerId) { let playerToRemove = this.players.find((player) => { return player.id === playerId; }); let playerIndex = this.players.indexOf(playerToRemove); this.players.splice(playerIndex, 1); } } });
.form { margin: 0 auto; width: 400px; } .panel { width: 400px; margin: 6px auto; overflow: hidden; border: 1px solid; text-align: center; transition: all 1s; display: inline-block; } .players { position: relative; text-align: center; } .remove { float: right; cursor: pointer; text-decoration: underline; font-size: 12px; vertical-align: bottom } .player-enter, .player-leave-to { opacity: 0; } .player-enter { transform: translateY(30%); } .player-leave-to { transform: translateX(300%); } .player-leave-active { position: absolute; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script> <div id="app"> <div class="form"> <label for="name">Name</label> <input type="text" id="name" v-model="name"> <button @click="addPlayer">Add player</button> </div> <div class="players"> <transition-group name="player" tag="div"> <div class="panel" v-for="player in players" :key="player.id"> <h2> {{ player.name}} <span class="remove" @click="deletePlayer(player.id)">Remove</span> </h2> </div> </transition-group> </div> </div>