前端嘛 Logo
前端嘛
欢迎来到哈比村

欢迎来到哈比村

2026-01-07
<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Image Zoom in with ScrollTrigger</title>
    <link
      rel="stylesheet"
      href="https://cdnjs.cloudflare.com/ajax/libs/modern-normalize/3.0.1/modern-normalize.min.css"
    />
    <style>
      :root {
        --black: #1f1f1f;
        --white: #fff;
      }

      body {
        background-color: var(--black);
        color: var(--white);
        font-family: "Roboto", sans-serif;
        font-optical-sizing: auto;
        font-size: 20px;
        line-height: 1.5;
      }

      .hero__content {
        height: 100vh;
        overflow-x: hidden;
        width: 100%;
      }

      .hero__bg {
        background-image: url("https://fecoder-pic-1302080640.cos.ap-nanjing.myqcloud.com/hobbiton.jpg");
        background-position: center center;
        background-repeat: no-repeat;
        background-size: cover;
        filter: blur(3px) brightness(1.5);
        /* initial fog */
        height: 100vh;
        width: 100%;
        will-change: filter;
      }

      .hero__title {
        color: var(--black);
        filter: blur(10px);
        font-size: clamp(3.125rem, 17.321vw + -1.357rem, 12.5rem);
        line-height: clamp(4.688rem, 21.363vw + -0.84rem, 16.25rem);
        left: 50%;
        margin: 0;
        opacity: 0;
        padding: 0;
        position: absolute;
        top: 50vh;
        transform: translate(-50%, -50%) scale(0.5);
        z-index: 100;
      }

      .hero__cover {
        --overlay-opacity: 1;
        position: absolute;
        left: 0;
        top: 0;
        height: 100vh;
        width: 100vw;
        perspective: 500px;
        overflow: hidden;
        z-index: 2;
      }

      .hero__cover::after {
        background: radial-gradient(circle, rgba(0, 0, 0, 0) 20%, black 90%);
        content: "";
        inset: 0;
        opacity: var(--overlay-opacity);
        position: absolute;
        pointer-events: none;
      }

      .hero__cover-img {
        height: 100%;
        -o-object-fit: cover;
        object-fit: cover;
        width: 100%;
        pointer-events: none;
      }

      .section {
        max-width: 600px;
        margin: 0 auto;
        padding: 100px 0;
      }

      .hobbiton {
        position: relative;
      }
      .hobbiton::after {
        background: radial-gradient(
          circle,
          rgba(0, 0, 0, 0) 40%,
          rgba(0, 0, 0, 0.7) 90%
        );
        content: "";
        inset: 0;
        opacity: var(--overlay-opacity);
        position: absolute;
        pointer-events: none;
      }

      .hobbiton-img {
        height: 100vh;
        -o-object-fit: cover;
        object-fit: cover;
        width: 100%;
      }
    </style>
  </head>

  <body>
    <main>
      <div class="hero-container">
        <section class="hero">
          <div class="hero__content">
            <div class="hero__bg"></div>
            <h1 class="hero__title font-bold">Hobbiton</h1>
          </div>
          <div class="hero__cover">
            <img
              class="hero__cover-img"
              src="https://fecoder-pic-1302080640.cos.ap-nanjing.myqcloud.com/hobbit-hole.webp"
              alt=""
            />
          </div>
        </section>
      </div>
    </main>
    <script src="https://unpkg.com/gsap@3/dist/gsap.min.js"></script>
    <script src="https://unpkg.com/gsap@3/dist/ScrollSmoother.min.js"></script>
    <script src="https://unpkg.com/gsap@3/dist/ScrollTrigger.min.js"></script>
    <script src="https://unpkg.com/gsap@3/dist/SplitText.min.js"></script>
    <script>
      gsap.registerPlugin(ScrollTrigger, ScrollSmoother);

      ScrollSmoother.create({
        smooth: 1,
        effects: true,
        normalizeScroll: true,
      });

      gsap
        .timeline({
          scrollTrigger: {
            trigger: ".hero-container",
            start: "top top",
            end: "+=150%",
            pin: true,
            scrub: 1,
            // markers: true
          },
        })
        .to(".hero__cover-img", {
          scale: 2,
          z: 350,
          transformOrigin: "center center",
          ease: "power1.inOut",
        })
        .to(
          ".hero__cover",
          {
            "--overlay-opacity": 0,
            ease: "power1.inOut",
          },
          "<" // sync with image zoom
        )
        .to(
          ".hero__bg",
          {
            scale: 1.1,
            filter: "blur(0px) brightness(1)",
            ease: "power1.inOut",
          },
          "<"
        )
        .to(
          ".hero__title",
          {
            scale: 1,
            xPercent: -50,
            yPercent: -50,
            opacity: 1,
            filter: "blur(0px)",
            ease: "power1.inOut",
          },
          "<"
        );

      const splitLetters = SplitText.create(
        document.querySelector(".opacity-reveal")
      );
      gsap.set(splitLetters.chars, { opacity: "0.2", y: 0 });

      gsap
        .timeline({
          scrollTrigger: {
            trigger: ".section-stick",
            pin: true,
            start: "center center",
            end: "+=1500",
            //markers: true,
            scrub: 1,
          },
        })
        .to(splitLetters.chars, {
          opacity: "1",
          duration: 1,
          ease: "none",
          stagger: 1,
        })
        .to({}, { duration: 10 })
        .to(".opacity-reveal", {
          opacity: "0",
          scale: 1.2,
          duration: 50,
        });
    </script>
  </body>
</html>