When I remove an item from a flexbox, the remaining items \"snap\" into their new positions immediately rather than animating.
Conceptually, since the items are chan
I've fixed up @skyline3000's demo based on this example from Treehouse. Not sure if this will break again if browsers change but this seems to be the intended way to animate flex size changes:
http://jsfiddle.net/2gbPg/2/
Also I used jQuery but it technically isn't required.
.flexed {
background: grey;
/* The border seems to cause drawing artifacts on transition. Probably a browser bug. */
/* border: 1px solid black; */
margin: 5px;
height: 100px;
flex-grow: 1;
transition: flex-grow 1000ms linear;
}
.removed {
/* Setting this to zero breaks the transition */
flex-grow: 0.00001;
}
One thing to note about the CSS is you can't transition to a flex-grow
of zero, it won't transition it will just disappear. You need to just put a very small value. Also there seems to be an artifacting bug when drawing borders so I've used a background in this case.
I accidentally got it to work in a simple way.
Basically, you set width:0;flex-grow:1
and of course add transition:all 2s;
and that's it. It's a curious hack.
See it working
I have been trying to animate rows in my flexbox. This was not possible through just css. So I did it with a bit of javascript and an extra parent for my flexbox rows.
HTML :
<div class="outer-container">
<div class="inner-container">
<div class="row">Row number 1</div>
</div>
</div>
<button id="add">Add one more item</button>
CSS :
.row{
height : 40px;
width : 200px;
border: 1px solid #cecece;
text-align : center;
padding-top : 20px;
}
.outer-container {
height : 500px;
border : 1px solid blue;
width : 250px;
display: flex;
justify-content : center;
align-items: center;
}
.inner-container {
height : 42px;
transition : height 0.3s;
}
button {
width : 200px;
height: 30px;
margin-left : 80px;
margin-top : 10px;
}
Javascript :
(() => {
let count = 1;
document.querySelector("#add").addEventListener('click', () => {
const template = document.createElement('div');
count += 1;
template.textContent = `Row number ${count}`;
template.className = 'row';
const innerContainer = document.querySelector('.inner-container');
innerContainer.appendChild(template);
innerContainer.style.height = `${innerContainer.clientHeight + 42}px`;
})
})();
Working demo : https://jsfiddle.net/crapbox/dnx654eo/1/
Remember that the Flexible Box Model and Grid Layout specifications are changing constantly, even the properties and valid values. The browser implementations are far from complete as well. That being said, you can transition on the flex
property so that the elements transition smoothly, then just listen for TransitionEnd
to finally remove the node from the DOM tree.
Here is an example JSFiddle, running in Chrome 21: http://jsfiddle.net/5kJjM/ (click the middle div)
var node = document.querySelector('#remove-me');
node.addEventListener('click', function(evt) {
this.classList.add('clicked');
}, false);
node.addEventListener('webkitTransitionEnd', function(evt) {
document.querySelector('#flexbox').removeChild(this);
}, false);
#flexbox {
display: -webkit-flex;
-webkit-flex-flow: row;
}
.flexed {
border: 1px solid black;
height: 100px;
-webkit-flex: 1 0 auto;
-webkit-transition: -webkit-flex 250ms linear;
}
.clicked {
-webkit-flex: 0 0 auto;
}
<div id="flexbox">
<div class="flexed"></div>
<div class="flexed" id="remove-me"></div>
<div class="flexed"></div>
</div>
Edit: To further clarify, when you remove a node, you should set its flex to 0, then remove it from the DOM. When adding a node, add it in with flex: 0, then transition it to flex:1
I've done a codepen that animates the elements when you remove one, take a look: https://codepen.io/MauriciAbad/pen/eQoQbK
It can be optimized, but works. ;D
<div class="container">
<div id="0"></div>
<div id="1"></div>
... more divs ...
</div>
.container{
display: flex;
flex-wrap: wrap;
}
.container > div{
flex-grow: 1;
transform-origin: left top;
}
var cards = document.querySelectorAll('.container > div');
cards.forEach((card) => {
card.addEventListener('click', () => {removeCard(card);});
});
var cardsOldInfo = //example content
{"cardId": {
"x": 100,
"y": 200,
"width": 100
}
};
var cardsNewInfo = cardsOldInfo;
function removeCard(card){
cardsOldInfo = getCardsInfo();
card.parentNode.removeChild(card);
cardsNewInfo = getCardsInfo();
moveCards();
}
function getCardsInfo(){
updateCards();
let cardsInfo = {};
cards.forEach((card) => {
var rect = card.getBoundingClientRect();
cardsInfo[card.id] = {
"x": rect.left,
"y": rect.top,
"width": (rect.right - rect.left)
};
});
return cardsInfo;
}
function moveCards(){
updateCards();
cards.forEach((card) => {
card.animate([
{
transform: `translate(${cardsOldInfo[card.id].x - cardsNewInfo[card.id].x}px, ${cardsOldInfo[card.id].y -cardsNewInfo[card.id].y}px) scaleX(${cardsOldInfo[card.id].width/cardsNewInfo[card.id].width})`
},
{
transform: 'none'
}
], {
duration: 250,
easing: 'ease-out'
});
});
}
function updateCards(){
cards = document.querySelectorAll('.container > div');
}
Another adaptation to @skyline3000 and @chris-nicola's solutions: http://jsfiddle.net/2gbPg/2/
You can simply animate max-width
(or max-height
as appropriate), animating to 0
when removing, and 100%
when inserting.