Instruction

Step-by-step instructions for the Floun Webflow template, covering setup, GSAP interactions, and how to customize animations and page elements with ease.

GSAP Guide
Portfolio Hover Effect
This script adds a hover effect to a portfolio grid using GSAP. When you hover over any item (.portfolio-single), it dims all the other items to 50% opacity while keeping the hovered one fully visible, creating a spotlight effect. Once your cursor leaves the whole grid, every item fades back to full opacity. The smooth 0.4s transitions and overwrite: true keep the animations clean even when moving quickly between items.
<script>
document.addEventListener('DOMContentLoaded', function () {
  const group = document.querySelector('.portfolio-content');
  if (!group || typeof gsap === 'undefined') return;
  const items = gsap.utils.toArray('.portfolio-single', group);
  
  items.forEach((item) => {
    item.addEventListener('mouseenter', () => {
      items.forEach((other) => {
        gsap.to(other, {
          opacity: other === item ? 1 : 0.5,
          duration: 0.4,
          ease: 'power2.out',
          overwrite: true
        });
      });
    });
  });
  
  group.addEventListener('mouseleave', () => {
    gsap.to(items, {
      opacity: 1,
      duration: 0.4,
      ease: 'power2.out',
      overwrite: true
    });
  });
});
</script>
Hamburger Icon Click
This script animates a hamburger icon with GSAP. Clicking it toggles between a hamburger and an "X" — the middle line vanishes while the top and bottom rotate into a cross. While closed, hovering staggers the three lines to different widths for a subtle effect. The icon reverses back to a hamburger when clicked again or when any menu link is selected.
<script>
document.addEventListener('DOMContentLoaded', function () {
  const wrap = document.querySelector('.hamburger-wrap');
  if (!wrap || typeof gsap === 'undefined') return;
  const lines = gsap.utils.toArray('.hamburger-line', wrap);
  if (lines.length < 3) return;
  const [top, middle, bottom] = lines;
  const shift = '0.5rem'; 
  gsap.set(lines, { width: '100%', transformOrigin: 'center center' });
  
  const tl = gsap.timeline({
    paused: true,
    defaults: { duration: 0.4, ease: 'power3.inOut' }
  });
  tl
    .to(middle, { scaleX: 0 }, 0)
    .to(top,    { y: shift, rotate: 135 }, 0)
    .to(bottom, { y: '-' + shift, rotate: 45 }, 0);
  let isOpen = false;
  function open() {
    isOpen = true;
    gsap.to(lines, { width: '100%', duration: 0.3, ease: 'power2.out' });
    tl.play();
  }
  function close() {
    isOpen = false;
    gsap.to(lines, { width: '100%', duration: 0.3, ease: 'power2.out' });
    tl.reverse();
  }
  
  wrap.addEventListener('click', () => { isOpen ? close() : open(); });
 
  wrap.addEventListener('mouseenter', () => {
    if (isOpen) return;
    gsap.to(top,    { width: '85%',  duration: 0.3, ease: 'power2.out' });
    gsap.to(middle, { width: '65%',  duration: 0.3, ease: 'power2.out' });
    gsap.to(bottom, { width: '100%', duration: 0.3, ease: 'power2.out' });
  });
  wrap.addEventListener('mouseleave', () => {
    if (isOpen) return;
    gsap.to(lines, { width: '100%', duration: 0.3, ease: 'power2.out' });
  });
  
  gsap.utils.toArray('.menu-link').forEach((link) => {
    link.addEventListener('click', () => { if (isOpen) close(); });
  });
});
</script>