如何将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 } });
关键修改点说明
- ScrollSmoother初始化:必须先创建wrapper和content容器,所有滚动动画都将在这个上下文内运行,避免和原生滚动冲突。
- Pin替代Sticky:用
smoother.pin()完全接管元素的固定逻辑,把原来ScrollTrigger里的onLeave/onEnterBack回调迁移到Pin的onUnpin/onPinBack中,逻辑更统一。 - 关联ScrollTrigger与Smoother:所有ScrollTrigger实例都要加上
containerAnimation: smoother,确保动画触发时机和ScrollSmoother的平滑滚动同步,不会出现错位。 - 删除CSS Sticky:原来的
position: sticky会和Pin功能冲突,必须彻底移除,避免元素定位混乱。
这样调整后,原来的滚动动画就完全适配ScrollSmoother的Pin功能了,滚动会更丝滑,动画和固定元素的配合也更稳定~
内容来源于stack exchange
相关产品推荐
相关产品推荐

