Tiny cart animation
by Julien Roche
HTML
<header class="main__header">
<h1 class="main__header__label">My header</h1>
<i class="main__header__cart"></i>
</header>
<section class="main__content">
<div class="main__content__product">
<img class="main__content__product__image" src="https://static.pexels.com/photos/126407/pexels-photo-126407.jpeg" />
<div class="main__content__product__label">A</div>
</div>
<div class="main__content__product">
<img class="main__content__product__image" src="https://static.pexels.com/photos/96938/pexels-photo-96938.jpeg" />
<div class="main__content__product__label">B</div>
</div>
<div class="main__content__product">
<img class="main__content__product__image" src="https://static.pexels.com/photos/127028/pexels-photo-127028.jpeg" />
<div class="main__content__product__label">C</div>
</div>
<div class="main__content__product">
<img class="main__content__product__image" src="https://static.pexels.com/photos/45170/kittens-cat-cat-puppy-rush-45170.jpeg" />
<div class="main__content__product__label">D</div>
</div>
</section>
CSS
.effect--move {
background-color: white;
background-position: top left;
background-repeat: no-repeat;
background-size: cover;
position: fixed;
transition: transform 250ms linear 150ms;
z-index: 5000;
}
.effect--freeze {
pointer-events: none;
}
.main {
}
.main__header {
border-bottom: 1px solid gray;
display: flex;
padding-bottom: 1rem;
}
.main__header__label {
flex: 1 1 auto;
line-height: 48px;
margin-right: 5px;
}
.main__header__cart {
background-image: url('https://cdn4.iconfinder.com/data/icons/ionicons/512/icon-ios7-cart-outline-48.png');
background-position: center center;
background-repeat: no-repeat;
flex: 0 0 48px;
}
.main__content {
}
.main__content__product {
cursor: pointer;
display: inline-block;
margin: 10px 5px 10px 5px;
outline: none;
}
.main__content__product__image {
height: 80px;
object-fit: cover;
padding: 5px 5px 5px 5px;
width: 80px;
}
.main__content__product__label {
color: gray;
font-size: 12px;
margin: 5px 5px 5px 5px;
text-align: center;
}
Babel + JSX
function move (imageElement, targetElement) {
return new Promise((resolve, reject) => {
let animationElement = null;
function transitionHandler() {
document.body.removeChild(animationElement);
animationElement.removeEventListener('transitionend', transitionHandler, false);
document.body.classList.remove('effect--freeze');
resolve();
}
try {
let imageElementPosition = imageElement.getBoundingClientRect();
let targetElementPosition = targetElement.getBoundingClientRect();
animationElement = document.createElement('div');
animationElement.classList.add('effect--move');
animationElement.style.backgroundImage = `url("${imageElement.src}")`;
animationElement.style.height = `${imageElementPosition.height}px`;
animationElement.style.left = `${imageElementPosition.left}px`;
animationElement.style.top = `${imageElementPosition.top}px`;
animationElement.style.width = `${imageElementPosition.width}px`;
animationElement.addEventListener('transitionend', transitionHandler, false);
document.body.classList.add('effect--freeze');
document.body.appendChild(animationElement);
animationElement.style.transform = `translate(${targetElementPosition.x - imageElement.x}px, ${targetElementPosition.y - imageElement.y}px)`;
} catch (error) {
document.body.classList.remove('effect--freeze');
reject(error);
}
});
}
document.addEventListener('click', event => {
let paths = event.path.map(p => p.className);
let indexOfPath = paths.indexOf('main__content__product');
if (indexOfPath >= 0) {
move(
event.path[indexOfPath].querySelector('img'),
document.querySelector('.main__header__cart')
);
}
}, false)