Vue users are easy to implement such item shuffle animations, see their official docs:

Once the animated elements are not in the view the animation breaks. I fixed it by editing refreshPosition function:
refreshPosition(prop: string) {
this.items.forEach(item => {
item[prop] = {
top: item.el.offsetTop,
left: item.el.offsetLeft
}
});
}
Originally @yurzui used el.getBoundingClientRect() to get the positions but this method returns positions relative to the viewport.
I changed it so it gets the positions using el.offsetTop and el.offsetLeft which are relative to the first ancestor that isn't positioned 'static'.