SHARE
In the world of web design, engaging animations can significantly enhance user experience. One particularly eye-catching effect is the stacked floating cards animation, which creates the illusion of papers or cards naturally settling onto a surface with a subtle floating motion. This tutorial breaks down the process of creating this impressive animation using GSAP (GreenSock Animation Platform) and Elementor.
The foundation of this animation begins with creating the proper container structure in Elementor. The layout requires:
– Top and bottom containers set to 100VH for proper scrolling space
– A main container with a dark background color
– A card-holding container with evenly spaced vertical alignment
– Individual card containers set to 70% width with specific positioning
Each card container requires careful setup to achieve the desired effect:
– Position: Sticky positioning at 15VH from the top
– Custom CSS implementation for consistent card heights
– Inner container padding for styling flexibility
– Background images sized to cover the entire card space
– Responsive design considerations for mobile and tablet views
1. Rotation Effects
– Customizable rotation values (-5 degrees default)
– Adjustable parameters for different device sizes
– Natural paper-settling appearance
2. Scaling Animation
– Initial normal size scaling
– Growth to 1.2x when entering viewport
– Final scaling to 0.9x for depth effect
– Smooth transitions between scaling stages
3. Brightness Control
– Progressive darkness effect for stacked cards
– Adjustable brightness values
– Scroll-triggered opacity changes
4. Floating Animation
– Subtle up and down rotation
– Customizable floating intensity
– Continuous motion for stacked cards
One of the strongest features of this implementation is its natural adaptation to different screen sizes:
– Minimal code adjustments needed for responsiveness
– Consistent animation performance across devices
– Adjustable card sizes for optimal mobile viewing
– Smooth transitions on both tablet and phone displays
The animation offers several customization possibilities:
– Adjustable rotation angles for different visual effects
– Scalable card sizes
– Controllable brightness levels for stacked cards
– Variable floating animation intensity
– Flexible container padding and spacing
This stacked cards animation offers several advantages:
– Quick and straightforward implementation
– Minimal device-specific adaptations required
– Smooth performance across all devices
– Versatile application for various content types
– Professional and polished appearance
The stacked floating cards animation provides a modern and engaging way to present content on websites. Whether used for portfolios, product showcases, or general content presentation, this effect adds a professional touch while maintaining functionality across all devices. The combination of GSAP and Elementor makes implementation accessible while offering extensive customization options to match any design needs.
The beauty of this animation lies in its simplicity of implementation coupled with its sophisticated appearance. By following the provided code and customization options, developers can quickly integrate this engaging effect into their projects while maintaining full control over the animation’s behavior and appearance.
Add this code into your HTML widget or the Custom Code of Elementor
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.12.5/dist/ScrollTrigger.min.js"></script>
<script>
const cardsContainer = document.querySelector('.cards');
const cards = document.querySelectorAll('.card');
cards.forEach((card, index) => {
const cardInner = card.querySelector('.card__inner');
// Check if the card is the last in the array
const isLastCard = index === cards.length - 1;
// Random rotation between -5 and +5 degrees
const rotation = gsap.utils.random(-5, 5, true);
// Set initial values for scale and filter
gsap.set(cardInner, {
scale: 1, // Set initial scale
filter: 'brightness(1)', // Set initial brightness
rotation: rotation // Initial rotation
});
// First ScrollTrigger for scaling and rotation
ScrollTrigger.create({
trigger: card,
start: 'top 100%', // Scaling starts when the card reaches the middle of the viewport
end: 'top 25%', // Scaling ends when the card reaches 25% of the viewport
scrub: 1, // Sync the animation with scrolling
onUpdate: (self) => {
const progress = self.progress;
// Scale and rotate the card
gsap.to(cardInner, {
scale: gsap.utils.interpolate(1.2, 0.9, progress), // Scale from 1.2 to 0.9
rotation: rotation, // Maintain the rotation
overwrite: false // No overwrite to avoid conflicts
});
}
});
// Second ScrollTrigger for dimming the previous card
ScrollTrigger.create({
trigger: card,
start: 'top 75%', // The new card dims the previous one when it reaches 75% of the viewport
end: 'top 50%', // Dimming ends when the top of the card reaches 50% of the viewport
scrub: 1, // Sync the dimming with scrolling
onUpdate: (self) => {
const progress = self.progress;
// Dim the previous card (if available)
if (index > 0) { // Check if there is a previous card
const previousCardInner = cards[index - 1].querySelector('.card__inner');
gsap.to(previousCardInner, {
filter: `brightness(${gsap.utils.interpolate(1, 0.5, progress)})`, // Dimming the previous card
overwrite: false // No overwrite to avoid conflicts
});
}
}
});
// Floating effect for the cards
gsap.to(cardInner, {
y: '+=20', // Slight floating upwards
repeat: -1, // Infinite loop
yoyo: true, // Move back and forth
ease: 'power1.inOut', // Smooth transitions
duration: gsap.utils.random(1, 2), // Random duration between 1 and 2 seconds
overwrite: false // No overwrite to avoid conflicts
});
});
</script>
.card {
position: sticky !important;
top: 15vh;
height: 70vh;
}
@media (max-width: 768px) {
.card {
position: sticky;
top: 15vh;
height: 70vh;
}}
@media (max-width: 480px) {
.card {
position: sticky;
top: 15vh;
height: 70vh;
}}
© Copyright Lechclick 2025