//add this to your project
function animateChildren(container) {
function getFlexItemsInfo(container) {
return Array.from(container.children).map((item) => {
const rect = item.getBoundingClientRect()
return {
element: item,
x: rect.left,
y: rect.top,
width: rect.right - rect.left,
height: rect.bottom - rect.top,
}
})
}
function animateFlexItems(oldFlexItemsInfo, newFlexItemsInfo) {
for (const newFlexItemInfo of newFlexItemsInfo) {
const oldFlexItemInfo = oldFlexItemsInfo.find(e => e.element == newFlexItemInfo.element);
if (!oldFlexItemInfo) {
continue;
}
const translateX = oldFlexItemInfo.x - newFlexItemInfo.x
const translateY = oldFlexItemInfo.y - newFlexItemInfo.y
const scaleX = oldFlexItemInfo.width / newFlexItemInfo.width
const scaleY = oldFlexItemInfo.height / newFlexItemInfo.height
newFlexItemInfo.element.animate(
[
{
transform: `translate(${translateX}px, ${translateY}px) scale(${scaleX}, ${scaleY})`,
},
{ transform: 'none' },
],
{
duration: 250,
easing: 'ease-out',
}
)
}
}
let oldFlexItemsInfo = getFlexItemsInfo(container);
// Callback function to execute when mutations are observed
const childListMutationCallback = function(mutationsList, observer) {
const newFlexItemsInfo = getFlexItemsInfo(container);
if (oldFlexItemsInfo) {
animateFlexItems(oldFlexItemsInfo, newFlexItemsInfo);
}
oldFlexItemsInfo = newFlexItemsInfo;
};
new MutationObserver(childListMutationCallback).observe(container, { childList: true });
}
const container = document.querySelector('.container');
animateChildren(container);
//emulate existing adding/removing items
document.addEventListener('click', e => {
const item = e.target.closest('.item');
if (item) {
e.target.matches('.btn-close')
? container.removeChild(item)
: container.prepend(item.cloneNode(true));
}
});
.container {
display: flex;
flex-wrap: wrap;
gap: 2em;
width: 100%;
overflow: none;
}
.container>* {
transform-origin: left top;
}
.container>* {
flex-grow: 1;
max-width: 50%;
min-width: 20%;
border-radius: 5px;
height: 5em;
margin: 0.5rem;
box-shadow: 0 1px 8px rgba(0,0,0,0.3);
padding: 1em;
background: white;
}
.btn-close:after {
padding: .25em .25em;
content: 'X';
border: solid 1px
}
<div class="container">
<div class="item">
Item 1 <span class="btn-close"></span>
</div>
<div class="item" style="background: #f64f59">
Item 2 <span class="btn-close"></span>
</div>
<div class="item" style="background: #c471ed ">
Click to add <span class="btn-close"></span>
</div>
</div>