鼠标滚动

47 阅读2分钟
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <link rel="stylesheet" href="./styles.css">
</head>
<body>
    <div class="container">
        <nav>
            <p id="logo">Imagecho</p>
            <button>Download</button>
        </nav>
        <section class="intro">     
        </section>
        <section class="steps">
            <div class="step-counter">
                <div class="counter-title">
                    <h1>steps</h1>
                </div>
                <div class="count">
                    <div class="count-container">
                        <h1>1</h1>
                        <h1>2</h1>
                        <h1>3</h1>
                        <h1>4</h1>
                        <h1>5</h1>
                    </div>
                </div>
            </div>
            <div class="cards">
                <div class="card">
                    <div class="card-img">
                        <img src="./img1.jpg" alt="">
                    </div>
                    <div class="card-content">
                        <p>Lorem ipsum dolor sit amet consectetur adipisicing elit. Quisquam, quos.</p>
                    </div>
                </div>
                <div class="card">
                    <div class="card-img">
                        <img src="./img2.jpg" alt="">
                    </div>
                    <div class="card-content">
                        <p>It can be used through clothing without removing the prosthesis and evenly</p>
                    </div>
                </div>
                <div class="card">
                    <div class="card-img">
                        <img src="./img3.jpg" alt="">
                    </div>
                    <div class="card-content">
                        <p>distributes air within the socket to adapt to different</p>
                    </div>
                </div>
                <div class="card">
                    <div class="card-img">
                        <img src="./img4.jpg" alt="">
                    </div>
                    <div class="card-content">
                        <p>residual limb morphologies.</p>
                    </div>
                </div>
                <div class="card">
                    <div class="card-img">
                        <img src="./img5.jpg" alt="">
                    </div>
                    <div class="card-content">
                        <p>The Overlay works with most liners and is compatible with distal fixation</p>
                    </div>
                </div>

                <div class="card empty"></div>
                <div class="card empty"></div>
            </div>
        </section>
        <section class="outro">
           <p> Our Overlay is designed to be used with <span>most liners and is compatible</span> with distal fixation</p>
        </section>
    </div>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/gsap.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/gsap/3.12.5/ScrollTrigger.min.js"></script>
    <script src="https://unpkg.com/lenis@1.1.18/dist/lenis.min.js"></script>
    <script src="./script.js"></script>
</body>
</html>
* {
  margin: 0;
  padding: 0;
  box-sizing: border-box;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
html,
body {
  width: 100vw;
  height: 900vh;
  background-color: #000;
  color: #fff;
  font-family: "Poppins", sans-serif;
}

img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
nav {
  position: absolute;
  top: 0;
  left: 0;
  width: 100vw;
  padding: 1.5em;
  display: flex;
  justify-content: space-between;
  align-items: center;
  z-index: 2;
}
p#logo {
  text-transform: uppercase;
  font-family: "Poppins", sans-serif;

  font-weight: 900;
}
button {
  border: none;
  outline: none;
  font-weight: 500;
  background-color: #fff;
  color: #000;
  padding: 0.75em 1em;
  border-radius: 0.25em;
  cursor: pointer;
}
section {
  position: relative;
  width: 100vw;
  height: 100vh;
  overflow: hidden;
}

.intro {
  background: url("./img2.jpg") no-repeat 50% 50%;
  background-size: cover;
}

.outro {
  display: flex;
  justify-content: center;
  align-items: center;
  background: linear-gradient(180deg, #000, #364549);
  background-size: 200% 200%;
}

.outro p {
  width: 75%;
  color: #fff;
  text-align: center;
  font-size: 52px;
  font-weight: 400;
  line-height: 1.125;
}

.outro p span {
  color: #75e1ff;
}

.cards {
  position: absolute;
  top: 25%;
  left: 50%;
  transform: translate(-50%, -50%);
  width: 150vw;
  height: 600px;
  will-change: transform;
}

.card {
  position: absolute;
  width: 500px;
  height: 550px;
  top: 50%;
  left: 50%;
  transform-origin: center center;
  margin-left: -250px;
  display: flex;
  flex-direction: column;
  gap: 1em;
  will-change: transform;
}

.card-img {
  flex: 1;
  border-radius: 0.5em;
  overflow: hidden;
}

.card-content {
  width: 100%;
  height: 60px;
}
.card-content p {
  text-align: left;
  color: #fff;
  font-size: 16px;
  font-weight: 500;
  line-height: 1.25;
}

.step-counter {
  position: absolute;
  display: flex;
  flex-direction: column;
  margin: 2em;
}

.counter-title,
.count {
  position: absolute;
  width: 1200px;
  height: 150px;
  clip-path: polygon(0 0, 100% 0, 100% 100%, 0 100%);
  overflow: hidden;
}

.count {
  top: -10px;
}

.count-container {
  position: relative;
  transform: translateY(150px);
  will-change: transform;
}

.step-counter h1 {
  width: 100%;
  position: relative;
  color: #fff;
  text-transform: uppercase;
  font-size: 150px;
  font-weight: 900;
  line-height: 1;
  letter-spacing: -0.04em;
  will-change: transform;
}

.empty {
  opacity: 0;
}

@media (max-width: 900px) {
  .counter-title {
    height: 30px;
  }
  .step-counter h1 {
    font-size: 30px;
  }
  .count {
    top: 0px;
    left: -10px;
  }
  .cards {
    top: 27.5%;
  }
  .card {
    width: 375px;
    height: 500px;
  }
}

document.addEventListener("DOMContentLoaded", () => {
  gsap.registerPlugin(ScrollTrigger);

  const lenis = new Lenis();
  lenis.on("scroll", ScrollTrigger.update);
  gsap.ticker.add((time)=>{
    lenis.raf(time * 1000);
  });
  gsap.ticker.lagSmoothing(0);


const stickySection = document.querySelector(".steps");
const stickyHeight = window.innerHeight * 7;
const cards = document.querySelectorAll(".card");
const countContainer = document.querySelector(".count-container");
const totalCards = cards.length;

ScrollTrigger.create({
  trigger: stickySection,
  start: "top top",
  end: `+=${stickyHeight}px`,
  pin: true,
  pinSpacing: true,
  onUpdate: (self) => {
    positionCards(self.progress);
  }
});

const getRadius = () =>{
    return window.innerWidth < 900 ? window.innerWidth * 7.5 : window.innerWidth * 2.5;
}

const arcAngle = Math.PI * 0.4

const startAngle = Math.PI / 2 - arcAngle / 2;

function positionCards(progress = 0){
    const radius = getRadius();
    const totalTravel = 1 + totalCards / 7.5;
    const adjustedProgress = (progress * totalTravel - 1) * 0.75;
    
    cards.forEach((card, index) => {
        const normalizedProgress =(totalCards - index - 1) / totalCards;
        const cardProgress = normalizedProgress + adjustedProgress;
        const angle = startAngle + cardProgress * arcAngle;
        const x = radius * Math.cos(angle);
        const y = radius * Math.sin(angle);
        const rotation = (angle - Math.PI / 2) * (180 / Math.PI);
        gsap.set(card,{
            x: x,
            y: -y + radius,
            rotation: -rotation,
            transformOrigin:"center center",
        })
    });
}
positionCards(0);
let currentCardIndex = 0;
const options = {
    root: null,
    rootMargin: "0px",
    threshold: 0.5,
}

const observer = new IntersectionObserver((entries)=>{
    entries.forEach((entry)=>{
        if(entry.isIntersecting){
            lastScrollY = window.scrollY;
            let cardIndex = Array.from(cards).indexOf(entry.target);
            currentCardIndex = cardIndex;

            const targetY = 150 -currentCardIndex * 150;
            gsap.to(countContainer,{
                scrollTo: {y:targetY,autoKill:false},
                duration:0.5,
                ease:"power2.inOut",
                overwrite:true,
            });
        }
    });
},options);

cards.forEach((card) => {
    observer.observe(card);
});

window.addEventListener("resize",()=>{
    positionCards(0);
});

});