JSFiddle - React, Tailwind, and code Playground

by Patrick Ludewig

HTML

<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/ScrollTrigger.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/lenis.min.js"></script>
<link rel="stylesheet" href="https://cdn.prod.website-files.com/62cd5016acc6a2088b75fdb3/css/hausgemacht-wien.webflow.shared.d996818d8.min.css">
<div class="dresscode-grid">
  <div class="scroll">
    <div class="w-layout-blockcontainer container-col-8 w-container">
      <h1 class="is-h1">Dresscode</h1>
      <p class="is-txt-small">
        Auf unseren Sex-Positive und Body Neutrality Partys schmeißen wir uns in
        und aus der Schale und ziehen an was unsere Seelentiere zum Schnurren
        bring!<br />Dazu gehören unter anderem: <br />Dessous &amp; Lingerie,
        Kink, Lack &amp; Leder, Latex, Spitze, Mesh, Vinyl &amp; PVC, Seile
        &amp; Ketten, Physiotapes sowie nackerpatzig sein, Bodypainting &amp;
        Glitzer.<br />Ein Konzept hinter dem Outfit gibt Pluspunkte und kreativ
        zu werden ist sehr erwünscht! (Beispiele: Schals, Ketten aus dem
        Baumarkt, Fake-Blumen, Flügel etc. etc. worin Mensch sich hot fühlt)<br /><br />Wir
        sind beim Dresscode bewusst streng – wir wollen lieber mehr aus- als
        anziehen, damit sich auch die am wenigsten bekleideten Personen,
        insbesondere FLINTA*, in der Menge wohlfühlen.<br /><br />Unsere
        wunderbare Community hat sich für euch zur Inspiration ablichten lassen
        und liefert ein paar An- und Auszieh-Ideen.
      </p>
    </div>
    <p class="scroll-text">– scroll –</p>
  </div>
  <div class="dresscode-grid__wrapper w-dyn-list">
    <div role="list" class="dresscode-grid__list w-dyn-items">
      <div role="listitem" class="dresscode-grid__container w-dyn-item">
        <div class="dresscode-grid__image-wrapper">
          <a
           ...

JavaScript

gsap.registerPlugin(ScrollTrigger);

window.addEventListener("DOMContentLoaded", () => {
    /* LENIS SMOOTH SCROLL (OPTIONAL) */
    const lenis = new Lenis({
        autoRaf: true,
    });
    
    /* LENIS SMOOTH SCROLL (OPTIONAL) */
    gsap.to('.scroll', {
        autoAlpha: 0,
        duration: 0.2,
        scrollTrigger: {
            trigger: '.dresscode-grid__wrapper',
            start: 'top top',
            end: 'top top-=1',
            toggleActions: "play none reverse none"
        }
    });
    
    const W = window.innerWidth;
    const H = window.innerHeight;
    
    const container = document.querySelector('.dresscode-grid__wrapper');
    const medias = container.querySelectorAll('.dresscode-grid__container');
    
    // Set initial random positions for each media element (subtle offset from center)
    medias.forEach(media => {
        gsap.set(media, {
            x: (Math.random() - 0.5) * 0.16 * W, // Random horizontal offset (16% of viewport width)
            y: (Math.random() - 0.5) * 0.1 * H   // Random vertical offset (10% of viewport height)
        });
    });
    
    // Entry animation - elements come into view from bottom
    medias.forEach(media => {
        gsap.from(media, {
            rotation: (Math.random() - 0.5) * 80,    // Random rotation between -40 and 40 degrees
            yPercent: (Math.random() - 0.5) * 300,   // Random vertical offset between -150% and 150%
            xPercent: Math.random() * 400,           // Random horizontal offset between 0 and 400%
            scale: 0.5 + Math.random() * 0.5,       // Random scale between 0.5 and 1.0
            opacity: 0,                              // Start invisible
            ease: 'power1.out',
            scrollTrigger: {
                trigger: media,
                start: 'top 110%',    // Start when top of element is 110% down the viewport (below view)
                end: 'top 65%',       // End when top of...