Add to cart animation
Flying Product to Cart Animation
CSS
GSAP
HTML
JavaScript
This project creates an elegant e-commerce interface featuring a visually appealing animation when products are added to the shopping cart. The product image smoothly flies from its original position to the cart icon, providing users with satisfying visual feedback. The cart panel slides in from the right side when clicked, displaying all added items with quantity controls and a checkout button.
Elegant Shop - Flying Cart Animation
The application showcases a responsive product grid with modern card designs. Each product card contains an image, title, description, price, and an "Add to Cart" button. When users click this button, the product image animates and flies to the cart icon in the header, creating a delightful user experience.
The sliding cart panel provides a comprehensive overview of all selected items, allowing users to increase or decrease quantities or remove items entirely. The total amount updates in real-time as users modify their cart contents.
The design emphasizes clean aesthetics with rounded corners, subtle shadows, and a pleasing color palette. The interface is fully responsive, adapting seamlessly to different screen sizes while maintaining functionality and visual appeal.
The sliding cart panel provides a comprehensive overview of all selected items, allowing users to increase or decrease quantities or remove items entirely. The total amount updates in real-time as users modify their cart contents.
The design emphasizes clean aesthetics with rounded corners, subtle shadows, and a pleasing color palette. The interface is fully responsive, adapting seamlessly to different screen sizes while maintaining functionality and visual appeal.
HTML (html)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Elegant Shop - Flying Cart Animation</title>
<!-- Font Awesome for icons -->
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"
/>
<!-- Google Fonts -->
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link
href="https://fonts.googleapis.com/css2?family=Poppins:wght@300;400;500;600;700&display=swap"
rel="stylesheet"
/>
<!-- GSAP for animations -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.2/gsap.min.js"></script>
<link rel="stylesheet" href="./style.css" />
</head>
<body>
<!-- Header with Navigation -->
<header>
<div class="container">
<nav>
<div class="logo">
<i class="fas fa-shopping-bag"></i>
<span>Elegant Shop</span>
</div>
<div class="cart-icon" id="cartIcon">
<i class="fas fa-shopping-cart"></i>
<span class="cart-count" id="cartCount">0</span>
</div>
</nav>
</div>
</header>
<!-- Main Content -->
<main class="container">
<div class="products" id="products">
<!-- Products will be added dynamically -->
</div>
</main>
<!-- Cart Panel -->
<div class="cart-overlay" id="cartOverlay"></div>
<div class="cart-panel" id="cartPanel">
<div class="cart-header">
<div class="cart-title">
<i class="fas fa-shopping-cart"></i>
<span>Your Cart</span>
</div>
<button class="close-cart" id="closeCart">
<i class="fas fa-times"></i>
</button>
</div>
<div class="cart-items" id="cartItems">
<div class="empty-cart" id="emptyCart">
<i class="fas fa-shopping-basket"></i>
<p>Your cart is empty</p>
<p>Add some products to see them here!</p>
</div>
<!-- Cart items will be added dynamically -->
</div>
<div class="cart-footer">
<div class="cart-total">
<span>Total:</span>
<span class="total-amount" id="totalAmount">$0.00</span>
</div>
<button class="checkout-btn" id="checkoutBtn">
<i class="fas fa-credit-card"></i>
<span>Proceed to Pay</span>
</button>
</div>
</div>
<script src="./script.js"></script>
</body>
</html>
CSS (css)
:root {
--primary: #6366f1;
--primary-light: #818cf8;
--primary-dark: #4f46e5;
--secondary: #f97316;
--text-dark: #1f2937;
--text-light: #6b7280;
--bg-light: #f9fafb;
--bg-dark: #111827;
--white: #ffffff;
--shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1),
0 2px 4px -1px rgba(0, 0, 0, 0.06);
--shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1),
0 4px 6px -2px rgba(0, 0, 0, 0.05);
--radius: 0.5rem;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: "Poppins", sans-serif;
background-color: var(--bg-light);
color: var(--text-dark);
min-height: 100vh;
overflow-x: hidden;
}
.container {
max-width: 1200px;
margin: 0 auto;
padding: 0 1rem;
}
/* Header Styles */
header {
background-color: var(--white);
box-shadow: var(--shadow);
position: sticky;
top: 0;
z-index: 100;
}
nav {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 0;
}
.logo {
font-size: 1.5rem;
font-weight: 700;
color: var(--primary);
display: flex;
align-items: center;
gap: 0.5rem;
}
.logo i {
color: var(--secondary);
}
.cart-icon {
position: relative;
cursor: pointer;
font-size: 1.5rem;
color: var(--primary);
transition: transform 0.3s ease;
}
.cart-icon:hover {
transform: scale(1.1);
}
.cart-count {
position: absolute;
top: -8px;
right: -8px;
background-color: var(--secondary);
color: var(--white);
font-size: 0.7rem;
font-weight: 600;
width: 20px;
height: 20px;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
}
/* Product Grid */
.products {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 2rem;
padding: 2rem 0;
}
.product {
background-color: var(--white);
border-radius: var(--radius);
overflow: hidden;
box-shadow: var(--shadow);
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
.product:hover {
transform: translateY(-5px);
box-shadow: var(--shadow-lg);
}
.product-img {
position: relative;
height: 200px;
overflow: hidden;
background-color: #f3f4f6;
}
.product-img img {
width: 100%;
height: 100%;
object-fit: cover;
transition: transform 0.5s ease;
}
.product:hover .product-img img {
transform: scale(1.05);
}
.product-info {
padding: 1.5rem;
}
.product-title {
font-size: 1.1rem;
font-weight: 600;
margin-bottom: 0.5rem;
}
.product-price {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 1rem;
}
.price {
font-size: 1.2rem;
font-weight: 700;
color: var(--primary);
}
.add-to-cart {
background: linear-gradient(135deg, var(--primary), var(--primary-dark));
color: var(--white);
border: none;
padding: 0.5rem 1rem;
border-radius: var(--radius);
font-weight: 500;
cursor: pointer;
transition: transform 0.3s ease, box-shadow 0.3s ease;
display: flex;
align-items: center;
gap: 0.5rem;
}
.add-to-cart:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(79, 70, 229, 0.3);
}
.product-description {
color: var(--text-light);
font-size: 0.9rem;
margin-bottom: 1rem;
line-height: 1.5;
}
/* Cart Panel */
.cart-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: rgba(0, 0, 0, 0.5);
z-index: 200;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease;
}
.cart-panel {
position: fixed;
top: 0;
right: -100%;
width: 100%;
max-width: 400px;
height: 100%;
background-color: var(--white);
z-index: 300;
box-shadow: -5px 0 15px rgba(0, 0, 0, 0.1);
transition: right 0.5s cubic-bezier(0.68, -0.55, 0.27, 1.55);
display: flex;
flex-direction: column;
}
.cart-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1.5rem;
border-bottom: 1px solid #e5e7eb;
}
.cart-title {
font-size: 1.2rem;
font-weight: 600;
display: flex;
align-items: center;
gap: 0.5rem;
}
.close-cart {
background: none;
border: none;
font-size: 1.5rem;
cursor: pointer;
color: var(--text-dark);
transition: transform 0.3s ease;
}
.close-cart:hover {
transform: rotate(90deg);
}
.cart-items {
flex: 1;
overflow-y: auto;
padding: 1rem;
}
.empty-cart {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
color: var(--text-light);
text-align: center;
padding: 2rem;
}
.empty-cart i {
font-size: 4rem;
margin-bottom: 1rem;
color: #e5e7eb;
}
.cart-item {
display: flex;
align-items: center;
gap: 1rem;
padding: 1rem;
border-radius: var(--radius);
background-color: #f9fafb;
margin-bottom: 1rem;
transition: transform 0.3s ease;
}
.cart-item:hover {
transform: translateX(-5px);
}
.cart-item-img {
width: 80px;
height: 80px;
border-radius: var(--radius);
overflow: hidden;
flex-shrink: 0;
}
.cart-item-img img {
width: 100%;
height: 100%;
object-fit: cover;
}
.cart-item-info {
flex: 1;
}
.cart-item-title {
font-weight: 500;
margin-bottom: 0.25rem;
}
.cart-item-price {
color: var(--primary);
font-weight: 600;
margin-bottom: 0.5rem;
}
.cart-item-quantity {
display: flex;
align-items: center;
gap: 0.5rem;
}
.quantity-btn {
width: 28px;
height: 28px;
border-radius: 50%;
border: 1px solid #e5e7eb;
background-color: var(--white);
display: flex;
justify-content: center;
align-items: center;
cursor: pointer;
transition: background-color 0.3s ease;
}
.quantity-btn:hover {
background-color: var(--primary);
color: var(--white);
}
.quantity {
font-weight: 500;
}
.remove-item {
color: #ef4444;
background: none;
border: none;
cursor: pointer;
font-size: 1.2rem;
transition: transform 0.3s ease;
}
.remove-item:hover {
transform: scale(1.1);
}
.cart-footer {
padding: 1.5rem;
border-top: 1px solid #e5e7eb;
background-color: #f9fafb;
}
.cart-total {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
font-weight: 600;
}
.total-amount {
font-size: 1.2rem;
color: var(--primary);
}
.checkout-btn {
width: 100%;
padding: 1rem;
border-radius: var(--radius);
background: linear-gradient(135deg, var(--primary), var(--primary-dark));
color: var(--white);
border: none;
font-weight: 600;
cursor: pointer;
transition: transform 0.3s ease, box-shadow 0.3s ease;
display: flex;
justify-content: center;
align-items: center;
gap: 0.5rem;
}
.checkout-btn:hover {
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(79, 70, 229, 0.3);
}
/* Flying Image Animation */
.flying-item {
position: fixed;
z-index: 1000;
pointer-events: none;
border-radius: var(--radius);
box-shadow: var(--shadow);
}
/* Responsive Styles */
@media (max-width: 768px) {
.products {
grid-template-columns: repeat(auto-fill, minmax(220px, 1fr));
gap: 1.5rem;
}
.cart-panel {
max-width: 100%;
}
.product-title {
font-size: 1rem;
}
.product-description {
font-size: 0.8rem;
}
.price {
font-size: 1rem;
}
.add-to-cart {
padding: 0.4rem 0.8rem;
font-size: 0.9rem;
}
}
@media (max-width: 480px) {
.products {
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
gap: 1rem;
}
.product-img {
height: 150px;
}
.product-info {
padding: 1rem;
}
.cart-item {
flex-direction: column;
align-items: flex-start;
}
.cart-item-img {
width: 100%;
height: 120px;
}
.remove-item {
position: absolute;
top: 1rem;
right: 1rem;
background-color: rgba(255, 255, 255, 0.8);
border-radius: 50%;
width: 30px;
height: 30px;
display: flex;
justify-content: center;
align-items: center;
}
.cart-item {
position: relative;
}
}
/* Animation Classes */
.fade-in {
animation: fadeIn 0.5s ease forwards;
}
@keyframes fadeIn {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
/* Badge for new products */
.badge {
position: absolute;
top: 10px;
left: 10px;
background: var(--secondary);
color: white;
padding: 0.25rem 0.5rem;
border-radius: 20px;
font-size: 0.7rem;
font-weight: 600;
z-index: 10;
}
JAVASCRIPT (javascript)
// Product Data
const products = [
{
id: 1,
name: "Premium Wireless Headphones",
price: 129.99,
image:
"https://images.unsplash.com/photo-1505740420928-5e560c06d30e?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80",
description: "Immersive sound quality with noise cancellation technology.",
isNew: true,
},
{
id: 2,
name: "Smart Fitness Watch",
price: 89.99,
image:
"https://images.unsplash.com/photo-1523275335684-37898b6baf30?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80",
description: "Track your fitness goals with advanced health monitoring.",
isNew: false,
},
{
id: 3,
name: "Ultra HD Camera",
price: 349.99,
image:
"https://images.unsplash.com/photo-1526170375885-4d8ecf77b99f?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80",
description:
"Capture stunning photos and videos with this professional camera.",
isNew: true,
},
{
id: 4,
name: "Designer Sunglasses",
price: 79.99,
image:
"https://images.unsplash.com/photo-1572635196237-14b3f281503f?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80",
description: "Stylish protection with 100% UV filtering technology.",
isNew: false,
},
{
id: 5,
name: "Portable Bluetooth Speaker",
price: 59.99,
image:
"https://images.unsplash.com/photo-1608043152269-423dbba4e7e1?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80",
description: "Powerful sound in a compact, waterproof design.",
isNew: true,
},
{
id: 6,
name: "Leather Wallet",
price: 49.99,
image:
"https://images.unsplash.com/photo-1627123424574-724758594e93?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1000&q=80",
description: "Handcrafted genuine leather with RFID protection.",
isNew: false,
},
];
// Cart State
let cart = [];
let isCartOpen = false;
// DOM Elements
const productsContainer = document.getElementById("products");
const cartIcon = document.getElementById("cartIcon");
const cartCount = document.getElementById("cartCount");
const cartPanel = document.getElementById("cartPanel");
const cartOverlay = document.getElementById("cartOverlay");
const closeCart = document.getElementById("closeCart");
const cartItems = document.getElementById("cartItems");
const emptyCart = document.getElementById("emptyCart");
const totalAmount = document.getElementById("totalAmount");
const checkoutBtn = document.getElementById("checkoutBtn");
// Initialize Products
function initProducts() {
productsContainer.innerHTML = "";
products.forEach((product) => {
const productElement = document.createElement("div");
productElement.classList.add("product");
productElement.innerHTML = `
<div class="product-img">
${product.isNew ? '<span class="badge">New</span>' : ""}
<img src="${product.image}" alt="${
product.name
}" id="product-img-${product.id}">
</div>
<div class="product-info">
<h3 class="product-title">${product.name}</h3>
<p class="product-description">${product.description}</p>
<div class="product-price">
<span class="price">$${product.price.toFixed(2)}</span>
<button class="add-to-cart" data-id="${product.id}">
<i class="fas fa-cart-plus"></i>
<span>Add to Cart</span>
</button>
</div>
</div>
`;
productsContainer.appendChild(productElement);
});
// Add Event Listeners to Add to Cart Buttons
document.querySelectorAll(".add-to-cart").forEach((button) => {
button.addEventListener("click", handleAddToCart);
});
}
// Handle Add to Cart
function handleAddToCart(e) {
const button = e.currentTarget;
const productId = parseInt(button.getAttribute("data-id"));
const product = products.find((p) => p.id === productId);
// Create flying image
createFlyingImage(productId, button);
// Add to cart
addToCart(product);
}
// Create Flying Image Animation
function createFlyingImage(productId, button) {
// Get positions
const productImg = document.getElementById(`product-img-${productId}`);
const cartIconRect = cartIcon.getBoundingClientRect();
const productImgRect = productImg.getBoundingClientRect();
// Create flying image element
const flyingImg = document.createElement("img");
flyingImg.classList.add("flying-item");
flyingImg.src = productImg.src;
flyingImg.style.width = "80px";
flyingImg.style.height = "80px";
flyingImg.style.objectFit = "cover";
flyingImg.style.position = "fixed";
flyingImg.style.left = `${productImgRect.left}px`;
flyingImg.style.top = `${productImgRect.top}px`;
document.body.appendChild(flyingImg);
// Animate with GSAP
gsap.to(flyingImg, {
duration: 0.8,
x: cartIconRect.left - productImgRect.left + cartIconRect.width / 2 - 40,
y: cartIconRect.top - productImgRect.top + cartIconRect.height / 2 - 40,
scale: 0.3,
ease: "power2.out",
onComplete: () => {
// Remove flying image
document.body.removeChild(flyingImg);
// Animate cart icon
gsap.to(cartIcon, {
scale: 1.3,
duration: 0.2,
onComplete: () => {
gsap.to(cartIcon, {
scale: 1,
duration: 0.2,
});
},
});
},
});
}
// Add to Cart Function
function addToCart(product) {
const existingItem = cart.find((item) => item.id === product.id);
if (existingItem) {
existingItem.quantity += 1;
} else {
cart.push({
...product,
quantity: 1,
});
}
updateCartUI();
}
// Update Cart UI
function updateCartUI() {
// Update cart count
const totalItems = cart.reduce((total, item) => total + item.quantity, 0);
cartCount.textContent = totalItems;
// Update cart items
if (cart.length === 0) {
emptyCart.style.display = "flex";
} else {
emptyCart.style.display = "none";
// Clear current items
cartItems.innerHTML = "";
// Add cart items
cart.forEach((item) => {
const cartItem = document.createElement("div");
cartItem.classList.add("cart-item");
cartItem.innerHTML = `
<div class="cart-item-img">
<img src="${item.image}" alt="${item.name}">
</div>
<div class="cart-item-info">
<h4 class="cart-item-title">${item.name}</h4>
<div class="cart-item-price">$${(
item.price * item.quantity
).toFixed(2)}</div>
<div class="cart-item-quantity">
<button class="quantity-btn decrease" data-id="${
item.id
}">-</button>
<span class="quantity">${item.quantity}</span>
<button class="quantity-btn increase" data-id="${
item.id
}">+</button>
</div>
</div>
<button class="remove-item" data-id="${item.id}">
<i class="fas fa-trash"></i>
</button>
`;
cartItems.appendChild(cartItem);
});
// Add event listeners to cart item buttons
document.querySelectorAll(".quantity-btn.decrease").forEach((button) => {
button.addEventListener("click", decreaseQuantity);
});
document.querySelectorAll(".quantity-btn.increase").forEach((button) => {
button.addEventListener("click", increaseQuantity);
});
document.querySelectorAll(".remove-item").forEach((button) => {
button.addEventListener("click", removeItem);
});
}
// Update total amount
const total = cart.reduce(
(total, item) => total + item.price * item.quantity,
0
);
totalAmount.textContent = `$${total.toFixed(2)}`;
}
// Decrease Quantity
function decreaseQuantity(e) {
const productId = parseInt(e.currentTarget.getAttribute("data-id"));
const item = cart.find((item) => item.id === productId);
if (item.quantity > 1) {
item.quantity -= 1;
} else {
cart = cart.filter((item) => item.id !== productId);
}
updateCartUI();
}
// Increase Quantity
function increaseQuantity(e) {
const productId = parseInt(e.currentTarget.getAttribute("data-id"));
const item = cart.find((item) => item.id === productId);
item.quantity += 1;
updateCartUI();
}
// Remove Item
function removeItem(e) {
const productId = parseInt(e.currentTarget.getAttribute("data-id"));
cart = cart.filter((item) => item.id !== productId);
updateCartUI();
}
// Toggle Cart Panel
function toggleCart() {
if (isCartOpen) {
closeCartPanel();
} else {
openCartPanel();
}
}
// Open Cart Panel
function openCartPanel() {
cartPanel.style.right = "0";
cartOverlay.style.opacity = "1";
cartOverlay.style.visibility = "visible";
isCartOpen = true;
}
// Close Cart Panel
function closeCartPanel() {
cartPanel.style.right = "-100%";
cartOverlay.style.opacity = "0";
// Wait for the opacity transition to complete before hiding
setTimeout(() => {
if (!isCartOpen) {
cartOverlay.style.visibility = "hidden";
}
}, 300);
isCartOpen = false;
}
// Event Listeners
cartIcon.addEventListener("click", toggleCart);
closeCart.addEventListener("click", closeCartPanel);
cartOverlay.addEventListener("click", closeCartPanel);
checkoutBtn.addEventListener("click", () => {
if (cart.length > 0) {
alert("Thank you for your purchase! Your order has been placed.");
cart = [];
updateCartUI();
closeCartPanel();
}
});
// Initialize
document.addEventListener("DOMContentLoaded", () => {
initProducts();
updateCartUI();
});
Download Source Code
Get the complete source code for this tutorial to use in your projects.
Comments (0)
No comments yet. Be the first to comment!