You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将CSS粘性定位迁移为GSAP ScrollSmoother的Pin功能?

如何将CSS粘性定位迁移为GSAP ScrollSmoother的Pin功能?

嘿,我来帮你搞定把CSS粘性定位换成GSAP ScrollSmoother的Pin功能这件事!先看看你原来的实现里,.history__wrap和.history-film都用了position: sticky来固定元素配合滚动动画,现在要迁移到ScrollSmoother的Pin功能,得一步步调整,我给你拆解清楚:

迁移核心步骤

  • 先初始化ScrollSmoother实例,这是使用Pin功能的前提
  • 移除CSS里的position: sticky规则,把固定逻辑交给ScrollSmoother
  • 调整原有ScrollTrigger的start/end参数,适配ScrollSmoother的滚动上下文
  • 微调动画的触发时机,确保和Pin的状态同步

替换后的完整代码

HTML部分(补充滚动容器结构)

<!-- 新增ScrollSmoother需要的滚动容器 -->
<div class="smooth-scroll-wrapper">
  <div class="smooth-scroll-content">
    <section class="history">
      <div class="history__wrap">
        <div class="history-text">
          <p>estd</p>
        </div>
        <div class="history-camera">
          <img src="你的相机图片路径" alt="Camera">
        </div>
        <div class="history-heading">
          <h2>HISTORY</h2>
        </div>
      </div>
      <div class="history-film">
        <div class="history-film-mask">
          <div class="history-film__item">
            <div class="history-film__content">
              <div class="history-film__text">
                <h2>1999</h2>
                <p>Some history text here</p>
              </div>
            </div>
            <ul class="history-film__dots">
              <li></li>
              <li></li>
              <li></li>
            </ul>
          </div>
          <!-- 其他history-film__item重复结构 -->
        </div>
      </div>
    </section>
    <!-- 页面其他内容 -->
  </div>
</div>

CSS部分(移除粘性定位规则)

/* ScrollSmoother容器基础样式 */
.smooth-scroll-wrapper {
  position: fixed;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  overflow: hidden;
}
.smooth-scroll-content {
  position: relative;
  width: 100%;
}

/* 原有样式保留,仅移除position: sticky规则 */
.history {
  min-height: 400dvh;
  display: flex;
  justify-content: flex-start;
  flex-direction: column;
  position: relative;
}
.history__wrap {
  overflow: hidden;
  min-height: 100dvh;
  width: 100vw;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  /* 移除position: sticky; top: 0; */
}
.history-text {
  text-align: center;
  margin-bottom: 40px;
}
.history-text p {
  color: var(--color-tamarillo);
  font-size: 15px;
  line-height: 22px;
  font-weight: 500;
  text-transform: uppercase;
}
.history-camera {
  text-align: center;
  position: relative;
  z-index: 2;
  width: 100%;
}
.history-heading {
  transform: rotate(-12deg);
  text-align: center;
}
.history-heading h2 {
  font-family: "PP Playground", sans-serif;
  font-size: 253px;
  line-height: 364px;
  font-weight: 500;
  color: var(--color-tamarillo);
  margin-top: -150px;
  margin-left: -110px;
}
.history-film {
  background-color: var(--color-black);
  clip-path: ellipse(300% 40% at 50% 50%);
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  min-height: 100dvh;
  width: 100%;
  position: relative;
  overflow-x: hidden;
  /* 移除position: sticky; top: 30px; left: 0%; */
  opacity: 0;
  transition: opacity 0.4s ease-out;
  scale: 0.5;
  transform-origin: center !important;
  margin-top: -100%;
}
.history-film__img {
  width: 100%;
  text-align: center;
}
.history-film__img img {
  height: 100%;
  width: 100%;
  max-width: 70%;
  object-fit: cover;
  object-position: top;
}
.history-film::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 15%;
  height: 100%;
  background: linear-gradient(to right, var(--color-black) 0%, transparent 100%);
  z-index: 3;
}
.history-film::after {
  content: "";
  position: absolute;
  top: 0;
  right: 0;
  width: 10%;
  height: 100%;
  background: linear-gradient(to left, var(--color-black) 0%, transparent 100%);
  z-index: 3;
}
.history-film__item {
  min-width: 50vw;
  height: 80dvh;
  background-color: var(--color-black-light);
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-direction: column;
  padding: 50px 0px;
}
.history-film__item.large {
  min-width: 100vw;
}
.history-film__content {
  display: flex;
  justify-content: center;
  width: 100%;
  padding: 50px 20px;
  flex: 1;
  height: calc(100% - 100px);
}
.history-film__text {
  max-width: 90%;
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding: 25px 0px;
}
.history-film__content h2 {
  font-family: "PP Playground", sans-serif;
  font-size: 9em;
  line-height: 1;
  font-weight: 500;
  color: var(--color-warm-white);
}
.history-film__content p {
  font-size: 1.2em;
  line-height: 1.467;
  font-weight: 500;
  color: var(--color-warm-white);
  text-transform: uppercase;
}
.history-film__dots {
  display: flex;
  align-items: center;
  justify-content: space-around;
  list-style: none;
  margin: 0;
  padding: 0;
  width: 100%;
}
.history-film__dots li {
  width: 3.05vw;
  height: 3.05vw;
  background-color: var(--color-black);
}
.history-film-mask {
  display: flex;
  align-items: center;
  flex-wrap: nowrap;
  position: relative;
}

JS部分(初始化ScrollSmoother + 替换Pin逻辑)

// 1. 初始化ScrollSmoother实例
const smoother = ScrollSmoother.create({
  wrapper: ".smooth-scroll-wrapper",
  content: ".smooth-scroll-content",
  smooth: 1.5, // 可根据需求调整平滑度
  normalizeScroll: true,
  effects: true
});

// CAMERA SCALE 逻辑
const historyElement = document.querySelector('.history');
const historyHeight = historyElement.offsetHeight;

// 用ScrollSmoother的Pin替代CSS sticky固定.history__wrap
smoother.pin(".history__wrap", {
  start: "top top",
  end: `bottom-=${historyHeight / 1.5}px top`,
  onUnpin: () => {
    document.querySelector('.history__wrap').style.opacity = 0;
    document.querySelector('.history-film').style.opacity = 1;
    historyElement.style.backgroundColor = '#171819';
  },
  onPinBack: () => {
    document.querySelector('.history__wrap').style.opacity = 1;
    document.querySelector('.history-film').style.opacity = 0;
    historyElement.style.backgroundColor = '#faf2dd';
  }
});

const cameraTl = gsap.timeline({
  scrollTrigger: {
    trigger: ".history",
    start: "top top",
    end: `bottom-=${historyHeight / 1.5}px top`,
    scrub: 1,
    containerAnimation: smoother // 关联到ScrollSmoother动画
  }
});

const scaleValue = window.matchMedia("(max-width: 991px)").matches ? 10 : 22;
cameraTl.to(".history-camera > img", {
  x: 0,
  y: 0,
  scale: scaleValue,
  ease: "none",
}).to(".history-film", {
  marginTop: 0,
  y: 0,
  opacity: 1,
  ease: "none",
  duration: 0.5,
  delay: 0,
  scale: 1,
}, '-=0.2');

// FILM HISTORY 逻辑
const filmHistory = gsap.utils.toArray(".history-film__item");
const filmItemWidth = filmHistory.reduce((diff, item) => {
  return diff + item.offsetWidth;
}, 0);

// Pin固定.history-film
smoother.pin(".history-film", {
  start: `top+=${historyHeight - (historyHeight / 2.5) + 100}px bottom`,
  end: "bottom bottom",
  onUnpin: () => {
    document.querySelector('.site-header').classList.remove('white');
  },
  onPinBack: () => {
    document.querySelector('.site-header').classList.add('white');
  }
});

const filmTl = gsap.timeline({
  scrollTrigger: {
    trigger: ".history",
    scrub: 3,
    start: `top+=${historyHeight - (historyHeight / 2.5) + 100}px bottom`,
    end: () => `bottom bottom`,
    containerAnimation: smoother
  }
});

filmTl.to(filmHistory, {
  x: () => `-${filmItemWidth - window.innerWidth}px`,
  ease: "none"
});

// NEXT BLOCK BG COLOR 逻辑
gsap.to('.next-block', {
  scrollTrigger: {
    trigger: '.next-block',
    start: 'top 50%',
    toggleActions: 'play none reset none',
    containerAnimation: smoother
  }
});

关键修改点说明

  1. ScrollSmoother初始化:必须先创建wrapper和content容器,所有滚动动画都将在这个上下文内运行,避免和原生滚动冲突。
  2. Pin替代Sticky:用smoother.pin()完全接管元素的固定逻辑,把原来ScrollTrigger里的onLeave/onEnterBack回调迁移到Pin的onUnpin/onPinBack中,逻辑更统一。
  3. 关联ScrollTrigger与Smoother:所有ScrollTrigger实例都要加上containerAnimation: smoother,确保动画触发时机和ScrollSmoother的平滑滚动同步,不会出现错位。
  4. 删除CSS Sticky:原来的position: sticky会和Pin功能冲突,必须彻底移除,避免元素定位混乱。

这样调整后,原来的滚动动画就完全适配ScrollSmoother的Pin功能了,滚动会更丝滑,动画和固定元素的配合也更稳定~

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 14:54:32