跳过主要内容

v3.3.0 中新增

ScrollTrigger

快速入门

CDN 链接

gsap.registerPlugin(ScrollTrigger) 

最简用法

gsap.to('.box', {
scrollTrigger: '.box', // start animation when ".box" enters the viewport
x: 500
});

ScrollTrigger 使任何人都能创建令人惊叹的基于滚动的动画并且只需少量代码即可实现。功能极其灵活。可执行 scrub、pin、snap,或直接触发任何与滚动相关的行为,即使这些行为与动画完全无关。

详细讲解

同时了解最常见的 ScrollTrigger 错误

简单示例

gsap.to('.box', {
scrollTrigger: '.box', // start the animation when ".box" enters the viewport (once)
x: 500
});

高级示例

let tl = gsap.timeline({
// yes, we can add it to an entire timeline!
scrollTrigger: {
trigger: '.container',
pin: true, // pin the trigger element while active
start: 'top top', // when the top of the trigger hits the top of the viewport
end: '+=500', // end after scrolling 500px beyond the start
scrub: 1, // smooth scrubbing, takes 1 second to "catch up" to the scrollbar
snap: {
snapTo: 'labels', // snap to the closest label in the timeline
duration: { min: 0.2, max: 3 }, // the snap animation should be at least 0.2 seconds, but no more than 3 seconds (determined by velocity)
delay: 0.2, // wait 0.2 seconds from the last scroll event before doing the snapping
ease: 'power1.inOut' // the ease of the snap animation ("power3" by default)
}
}
});

// add animations and labels to the timeline
tl.addLabel('start')
.from('.box p', { scale: 0.3, rotation: 45, autoAlpha: 0 })
.addLabel('color')
.from('.box', { backgroundColor: '#28a92b' })
.addLabel('spin')
.to('.box', { rotation: 360 })
.addLabel('end');

独立使用 / 自定义示例

不一定非得将 ScrollTrigger 直接嵌入到动画中(尽管这可能是最常见的使用方式)。你也可以对任何内容使用回调函数...

ScrollTrigger.create({
trigger: '#id',
start: 'top top',
endTrigger: '#otherID',
end: 'bottom 50%+=100px',
onToggle: (self) => console.log('toggled, isActive:', self.isActive),
onUpdate: (self) => {
console.log(
'progress:',
self.progress.toFixed(3),
'direction:',
self.direction,
'velocity',
self.getVelocity()
);
}
});

特性

特性亮点
  • 将任意动画链接到特定元素这样只有在该元素处于视口内时才会播放动画。这可以提高性能,并确保你的精美动画真正被用户看到!
  • ScrollTrigger 可以在进入/离开定义区域时对动画执行操作(播放、暂停、恢复、重新开始、倒放、完成、重置),或者将其直接链接到滚动条,使其像滑块控制器(scrubber) (scrub: true)。
  • 缓和动画与滚动条之间的关联使得它需要一定时间才能“跟上”,例如scrub: 1要花一秒的时间才能跟上。
  • ScrollSmootherGreenSock 的平滑滚动工具集成,基于原生滚动技术构建(会员专属功能)。
阅读更多...
  • 根据速度自动跳转到动画中的某些关键点实际上你可以getVelocity()在任何时候触发滚动的“跳转”。可以跳转到时间轴中最接近的标签或数组中的某个进度值,或者运行自己的基于函数的自定义逻辑来实现跳转
  • 将滚动触发器直接嵌入到任意 GSAP 动画中(包括时间轴)或创建独立实例并使用丰富的回调系统来执行任何你想做的操作。
  • 高级固定(pinning)功能可以在特定的滚动位置之间锁定一个元素的位置。框架会自动添加相应的 padding,以推动其他元素向下移动,当被固定的元素解除固定后它们会随之恢复(可通过pinSpacing: false来禁用此功能)。你甚至可以在不同位置多次固定同一个元素。
  • 定义滚动位置时具有极强的灵活性— 例如“当这个元素的中心碰到视口的中心时开始,当另一个元素的底部碰到视口的底部时结束”,可以使用关键字(顶部、中心、底部、左侧、右侧)、百分比、像素,甚至相对值如"+=300px"。一旦掌握了语法,就会发现它异常直观。
  • 支持垂直或水平滚动.
  • 丰富的回调系统包括 onEnter、onLeave、onEnterBack、onLeaveBack、onToggle、onUpdate、onScrubComplete 和 onRefresh。
  • 当窗口尺寸变化时自动重新计算位置。
  • 启用可视化标记在开发过程中可以看到起始/结束/触发点的确切位置。还提供大量定制选项,例如markers: {startColor:"green", endColor:"red", fontSize:"12px"}.
  • 切换 CSS 类。例如,toggleClass: "active"当 ScrollTrigger 处于激活状态时会为触发元素添加 "active" 类。也可以影响其他元素。
  • 响应式设计- 使用matchMedia()方法配合标准媒体查询语句,在不同屏幕尺寸下创建不同的配置。
  • 自定义容器- 不必使用视口;可以定义自定义滚动容器,比如一个<div>元素。
  • 高度优化以达到最大性能- 滚动事件进行了防抖处理,更新与 GSAP 和屏幕刷新同步,窗口大小调整的重新计算也进行了节流处理等。
  • 不劫持滚动,因此可以与其他原生技术(如 CSS 的滚动贴靠)结合使用。如果你想使用平滑滚动效果,可以使用ScrollSmoother它能与 ScrollTrigger 无缝集成,或者使用scrollerProxy()方法与第三方平滑滚动库集成。

配置对象

scrollTrigger可以用作trigger(见下方描述)或作为配置对象,支持以下任意属性:

    属性

    描述

  • animation

    补间 | 时间轴- 一个由 ScrollTrigger 控制的 GSAPTween或者时间轴实例。每个 ScrollTrigger 只控制一个动画,但你可以将所有动画包裹在一个时间轴里(推荐做法),或者创建多个 ScrollTrigger 来分别控制。
  • anticipatePin

    数字- 如果你固定较大的区域/面板,在快速滚动时可能会注意到固定效果似乎有轻微的延迟。这是因为大多数现代浏览器在单独的线程上处理滚动重绘,所以在固定的那一刻,浏览器可能已经绘制了未固定的预览内容,使其可见大约1/60秒。解决这个问题的唯一方法是让ScrollTrigger监控滚动速度并提前预测固定点,稍微早一点应用固定以避免未固定内容的闪烁。数值anticipatePin: 1通常就足够好了,但你可以减少或增加这个数字来控制固定提前的程度。不过,在许多情况下,你并不需要任何anticipatePin(默认值为0)。
  • containerAnimation

    补间 | 时间轴轻松触发由垂直滚动控制的“水平”滚动区域内的动画
    查看更多详细信息

    一种流行的效果是创建与垂直滚动相关联的水平移动区域,但由于该水平移动不是原生滚动,常规的ScrollTrigger无法知道何时某个元素在水平方向进入视野,因此你必须告诉ScrollTrigger去监控容器的[水平]动画以确定何时触发动作,例如:containerAnimation: yourTween.

    注意事项:容器的动画必须使用线性缓动(ease: "none")。此外,基于containerAnimation的ScrollTrigger不支持固定和对齐功能。你还应该避免横向动画化trigger元素,或者如果你确实这么做,请根据触发器被动画化的距离调整开始/结束值。

    更多信息请见此处.
  • end

    字符串 | 数字 | 函数- 决定ScrollTrigger的结束位置。
    查看更多详细信息

    可以是以下任一选项:

    • 字符串- 描述触发器上的某个位置和endTrigger(如果没有定义endTrigger,则为trigger)上的一个位置,以及位于scroller上的一个位置,这两个位置必须相交才能触发ScrollTrigger的结束。例如,"bottom center"表示的是“当endTrigger的底部到达滚动容器中心时”. "center 100px"表示的是“当endTrigger的中心距滚动容器顶部向下100px时”(假设为垂直滚动)。你可以使用如"top"、"bottom"、"center"这样的关键词(如果horizontal: true则用"left"和"right"),也可以使用像"80%"这样的百分比或像"100px"这样的像素值。这里的百分比和像素始终相对于元素/视口的左上角。你还可以定义单个相对值,比如"+=300"表示“比起始位置多出300px”,或者"+=100%"表示“滚动容器高度超出起始位置的距离”。"max"是一个特殊的关键词,表示最大滚动位置。
    • 数字- 一个确切的滚动值,比如200表示当视口/滚动条正好滚动了200像素时触发。
    • 函数- 一个函数,每当ScrollTrigger刷新并计算其位置时调用(通常是在创建ScrollTrigger时以及滚动容器尺寸变化时)。它应返回一个字符串或数字,格式如上所述。这使得动态计算值变得简单。和所有回调一样,此函数会接收到ScrollTrigger实例本身作为唯一的参数。

    这是一个静态位置,在ScrollTrigger创建时以及滚动条调整大小时根据正常文档流中的位置进行计算。它不会持续重新计算,因此例如如果你对触发器/endTrigger进行了动画处理,它不会自动更新对应的开始/结束值,因为ScrollTrigger高度优化了性能。你可以调用ScrollTrigger.refresh()来强制重新计算。默认值是"bottom top". clamp() 值 (版本3.12+)将你的结束值包裹在"clamp()"中以告诉ScrollTrigger始终保持计算出的值介于0(页面顶部)和最大滚动位置之间,这样就不会超出页面边界。实际应用中,这确保了靠近页面底部的任何触发元素不会有部分擦除动画结束的情况。例如,end: "clamp(bottom top)"- 任何常规基于字符串的值都可以放在clamp()里面。比如"clamp(20px 80%)"。这里是进一步解释的视频:

  • endTrigger

    字符串 | 元素- 元素(或用于选择元素的选择器文本),其在正常文档流中的位置用于计算ScrollTrigger的结束位置。除非endTriggertrigger元素不同,否则无需定义该值,因为默认情况下就是该元素。
  • fastScrollEnd

    布尔值 | 数值- 如果设置为true,如果你离开触发区域的速度快于某个特定速度(默认为2500px/s),则会强制当前ScrollTrigger的动画完成。这有助于避免用户快速滚动时出现重叠动画。你可以指定一个最小速度数值,例如离开触发区,fastScrollEnd: 3000就只会在速度超过3000px/s时激活。查看此处.
  • horizontal

    Boolean- 默认情况下,它认为你的设置使用的是垂直滚动,但如果您的项目使用的是水平滚动,只需设置horizontal: true即可。
  • id

    字符串- ScrollTrigger实例的任意唯一标识符,可用于ScrollTrigger.getById()。此ID也会添加到标记中。
  • invalidateOnRefresh

    Boolean- 如果true,那么每当发生refresh()(通常是窗口大小改变时)时,与ScrollTrigger关联的动画将调用它的invalidate()方法。这将清除所有内部记录的初始值。
  • 标记(markers)

    对象 | 布尔值- 添加在开发/故障排查期间有帮助的标记。markers: true使用默认值添加它们(startColor: "green", endColor: "red", fontSize: "16px", fontWeight: "normal", indent: 0),但您可以通过使用如下对象来定制它们:
    markers: {startColor: "white", endColor: "white", fontSize: "18px", fontWeight: "bold", indent: 20}
  • once(单次触发)

    Boolean- 如果truetrue销毁关联的动画。这对于仅希望动画在向前滚动时播放一次且从不重置或重新播放的情况非常理想。它还将 toggleActions 设置为 "play none none none"。
  • onEnter

    函数- 当滚动位置向前移动经过 "start" 时的回调函数(通常当触发器滚动到视图中时)。它接收一个参数——ScrollTrigger 实例本身,该实例具有类似progress, direction, isActive,以及getVelocity()的属性/方法。示例:
    onEnter: ({progress, direction, isActive}) => console.log(progress, direction, isActive)
  • onEnterBack

    函数- 当滚动位置向后移动经过 "end" 时的回调函数(通常当触发器再次向上滚动到视图中时)。它接收一个参数——ScrollTrigger 实例本身,该实例具有类似progress, direction, isActive,以及getVelocity()的属性/方法。示例:
    onEnterBack: ({progress, direction, isActive}) => console.log(progress, direction, isActive)
  • onLeave

    函数- 当滚动位置向前移动经过 "end" 时的回调函数(通常当触发器滚动出视图时)。它接收一个参数——ScrollTrigger 实例本身,该实例具有类似progress, direction, isActive,以及getVelocity()的属性/方法。示例:
    onLeave: ({progress, direction, isActive}) => console.log(progress, direction, isActive)
  • onLeaveBack

    函数- 当滚动位置向后移动经过 "start" 时的回调函数(通常当触发器完全回滚到起始点之外时)。它接收一个参数——ScrollTrigger 实例本身,该实例具有类似progress, direction, isActive,以及getVelocity()的属性/方法。示例:
    onLeaveBack: ({progress, direction, isActive}) => console.log(progress, direction, isActive)
  • onRefresh(刷新时)

    函数- 当发生刷新事件(通常是调整大小事件)强制 ScrollTrigger 重新计算其所有定位信息时调用的回调函数。它接收一个参数——ScrollTrigger 实例本身,该实例具有类似progress, direction, isActive,以及getVelocity()的属性/方法。示例:
    onRefresh: ({progress, direction, isActive}) => console.log(progress, direction, isActive)
  • onUpdate

    函数- 每当 ScrollTrigger 的进度发生变化时(意味着滚动位置改变了)就会调用的回调函数。如果您应用了数值型的scrubscrub(擦洗),请注意,由于滚动位置停止后动画仍会继续执行一小段时间,所以如果您的目标是每当动画更新时进行某些更新操作,最好将此回调添加到动画本身而不是 ScrollTrigger 上。onUpdate to the animation itself rather than the ScrollTrigger. 查看演示请访问此处.

    onUpdate 回调接收一个参数——ScrollTrigger 实例本身,该实例具有类似progress, direction, isActive,以及getVelocity().

    示例:
    onUpdate: self => console.log("progress", self.progress)
  • onScrubComplete(擦洗完成时)

    函数- 当数值型擦洗完成后调用的回调函数。只有应用了数值型擦洗(如scrub: 1scrub: 1progress, direction, isActive,以及getVelocity()的属性/方法。示例:
    onScrubComplete: ({progress, direction, isActive}) => console.log(progress, direction, isActive)
  • onSnapComplete(吸附完成时)

    函数- 当吸附完成后调用的回调函数。此功能仅在定义了snapsnap(吸附)的情况下适用。如果用户(或其他因素)以任何方式与滚动进行了交互,则吸附将被取消,在这种情况下不会触发 onSnapComplete 回调。该回调接收一个参数——ScrollTrigger 实例本身,该实例具有类似progress, direction, isActive,以及getVelocity()的属性/方法。示例:
    onSnapComplete: ({progress, direction, isActive}) => console.log(progress, direction, isActive)
  • onToggle(状态切换时)

    函数- 当 ScrollTrigger 从非激活状态切换到激活状态时,或者或者反过来也一样。这通常发生在滚动位置在任意方向上超过 "start" 或 "end" 时,但如果用户在同一帧内极速滚动而同时越过了 start 和 end,则状态并没有真正改变,因此不会触发 onToggle。您可以经常使用这个单一的回调函数来替代 onEnter、onLeave、onEnterBack 和 onLeaveBack,只需通过检查 isActive 属性即可实现状态切换。它接收一个参数——ScrollTrigger 实例本身,该实例具有类似progress, direction, isActive,以及getVelocity()的属性/方法。示例:
    onToggle: self => console.log("toggled to active:", isActive)
  • pin(固定)

    布尔值 | 字符串 | 元素- 在 ScrollTrigger 处于活动状态期间应固定的元素(或用于指定元素的选择器文本),这意味着它看起来会“粘”在其起始位置,而其余内容则继续在其下方滚动。只允许固定一个元素,但它可以包含任意数量的子元素。设置pin: truetruetrigger元素。

    警告不要对固定元素自身进行动画处理,因为这将扰乱测量结果(ScrollTrigger 针对性能进行了高度优化,并尽可能提前进行计算)。相反,您可以嵌套元素结构,使得只对固定元素内部的元素进行动画处理。

    注意:如果您固定的是某个元素,内部另一个也会被同样固定的元素,请务必定义一个pinnedContainer以便 ScrollTrigger 知道如何相应地偏移开始和结束位置。

    在使用 React?请确保正确清理资源——阅读这篇文章.

  • pinnedContainer(固定容器)

    元素 | 字符串- 如果您的 ScrollTrigger 的trigger/endTrigger元素位于另一个内部并且此父元素被其他ScrollTrigger 固定了(这种情况非常少见),那么该固定动作将持续影响 ScrollTrigger 的起始和结束位置的测量,因此您可以将pinnedContainer设置为那个父级/容器元素,使得 ScrollTrigger 能据此正确计算这些偏移量。同样,这种情况极为罕见。重要:不支持嵌套固定(nested pinning),因此该功能仅适用于非固定(non-pinning)的 ScrollTriggers

    (3.7.0版本中添加)

  • pinReparent

    Boolean- 如果true,在元素被固定期间,它将被重新父级为<body>这样它可以脱离任何祖先容器块。如果你在固定时注意到奇怪的行为(例如固定元素突然偏移然后随滚动移动),可能是某个祖先元素上有transform或者will-change,这会破坏行为(这是浏览器的特性,与 ScrollTrigger 无关)。最好在项目中避免这种情况,因为重新父级操作可能代价较高,但如果无法避免, position: fixed behavior (it's a browser thing, not ScrollTrigger). It's best to set up your project to avoid those because reparenting can be expensive, but pinReparent: true可以帮助你解决问题。只有在必要时才使用此功能。

    警告:如果你有依赖特定嵌套结构的 CSS 规则,这些规则会被重新父级影响并失效。例如像这样的 CSS 规则.section .panel p {color: white}将不再适用于嵌套的<p>元素,如果你通过.panel元素内部,请确保编写能适应重新父级的 CSS 规则。pinReparent: true because during the pin, it would no longer be inside the <section>对其进行固定,因为在固定过程中它不再位于

  • pinSpacer

    元素 | 字符串- 通常 ScrollTrigger 会在内部创建一个<div>来包裹固定的元素,但在极少数情况下,如果你在一个固定元素中加载了 iframe,这可能导致 ScrollTrigger 刷新时(如窗口调整大小)iframe 重新加载,因此此功能允许你指定一个用作占位符的元素来替代内部生成的 spacer。这样 ScrollTrigger 在刷新时不会移除/添加它,从而保持 iframe 内容不变。extremely rare scenario where you're loading an iframe into the pinned element, it can cause the iframe to refresh when ScrollTrigger refreshes (like on window resize), so this feature allows you to specify an element that should be used as the spacer instead of the internally-created one. That way, ScrollTrigger won't remove/add it during its refresh, keeping iframe content intact.
  • pinSpacing

    布尔值 | 字符串- 默认情况下,会向底部(或水平滚动时向右)添加 padding,以推动其他元素向下移动,使得当固定元素解除固定后,后续内容能够刚好接上。否则,内容可能会滚动到固定元素下方。你可以通过设置horizontal: true) to push other elements down so that when the pinned element gets unpinned, the following content catches up perfectly. Otherwise, things may scroll UNDER the pinned element. You can tell ScrollTrigger not to add any padding by setting pinSpacing: false.

    查看更多详细信息

    如果你更希望使用 margin 而不是 padding,可以设置pinSpacing: "margin".

    注意:pinSpacing 在大多数情况下是有效的,但实际效果取决于你的 DOM 和 CSS 设置方式。例如,如果你在一个具有 display: flex 或 position: absolute 的父元素中固定元素,则额外的 padding 不会推动其他元素下移,这时你需要手动调整间距。pinSpacing 只是一个适用于大多数情况的便利功能。

    重要:如果容器是display: flex, pinSpacing设置为false,因为在这种上下文中 padding 的工作方式不同,通常这是期望的行为。

    SnorklTV的ScrollTrigger Express课程中有一段有关固定(pinnging)功能的视频可能有助于你理解。

  • pinType

    "fixed" | "transform"- 默认情况下,position: fixed仅当滚动器是<body>时才会用于固定,否则将使用 transform(因为position: fixed在各种嵌套场景中不起作用),但你可以强制ScrollTrigger 使用position: fixed通过设置pinType: "fixed"。通常这不是必须或有益的。请注意,如果你设置了 CSS 属性will-change: transform,浏览器会将其视为应用了 transform,这会导致position: fixed元素出现问题(这与 ScrollTrigger/GSAP 无关)。
  • preventOverlaps

    布尔值 | 字符串- 此功能在 ScrollTrigger 即将触发动画时激活;它会查找之前的基于 ScrollTrigger 的动画,并强制这些先前的动画进入结束状态——避免难看的重叠。如果设置为true,它将影响所有前面的 ScrollTriggers。你可以使用任意字符串来限制其只影响具有匹配字符串的 ScrollTriggers。因此preventOverlaps: "group1"将只影响具有preventOverlaps: "group1"的其他 ScrollTriggers。查看示例请点击此处.
  • refreshPriority

    数值- 只要你按照页面上出现的顺序(从上到下或从左到右)创建 ScrollTriggers,就几乎不需要定义非常。我们强烈建议这么做。refreshPriority as long as you create your ScrollTriggers in the order they'd happen on the page (top-to-bottom or left-to-right)...which we 强烈推荐这样做。否则,使用来影响 ScrollTriggers 的刷新顺序,以确保固定元素的距离被添加到页面后续 ScrollTriggers 的 start/end 值中(这就是顺序重要的原因)。详情请参见refreshPriority to influence the order in which ScrollTriggers get refreshed to ensure that the pinning distance gets added to the start/end values of subsequent ScrollTriggers further down the page (that's why order matters). See the sort()方法。具有refreshPriority: 1的 ScrollTrigger 会比默认优先级refreshPriority: 0更早刷新。你也可以使用负数,并可为多个 ScrollTriggers 分配相同的数字。
  • scroller

    字符串 | 元素- 默认情况下,scroller视口本身,但如果你想将 ScrollTrigger 添加到某个可滚动的<div>容器中,比如,只需将其定义为 scroller。你可以使用类似 "#elementID" 的选择器文本或直接传入元素本身。
  • scrub

    布尔值 | 数值- 将动画的进度直接链接到滚动条,使其表现得像一个擦洗器。你可以启用平滑处理,使播放头需要一点时间才能追上滚动条的位置!它可以是以下任意一种
    • Boolean - scrub: true将动画的进度直接链接到 ScrollTrigger 的进度。
    • 数字- 播放头应花费的时间(以秒为单位)来“追上”,所以scrub: 0.5会让动画的播放头花 0.5 秒来追上滚动条的位置。这对于平滑处理非常有用。
  • snap

    数值 | 数组 | 函数 | 对象 | "labels" | "labelsDirectional"- 允许你在用户停止滚动后自动对齐到特定的进度值(介于 0 和 1 之间)。例如snap: 0.1会按 0.1 的增量进行对齐(即 10%、20%、30%,依此类推)。snap: [0, 0.1, 0.5, 0.8, 1]会强制只能停留在这些特定的进度值上。它可以是以下任意一种...
    查看更多详细信息
    • 数字 - snap: 0.1按0.1的增量(10%、20%、30%等)捕捉。如果你有特定数量的段落,只需执行1 / (sections - 1).
    • 数组 - snap: [0, 0.1, 0.5, 0.8, 1]朝最后一次滚动的方向将进度值数组中最接近的值作为目标进行捕捉(除非你设置了directional: false)。
    • 函数 - snap: (value) => Math.round(value / 0.2) * 0.2将自然目标值(基于速度)输入到函数中,并使用返回的值作为最终的进度值(在这种情况下是0.2的增量),因此你可以运行任何你想的逻辑。这些值应该始终介于0和1之间,表示动画的进度,所以0.5表示中间位置。
    • "标签" - snap: "labels"捕捉到时间轴上最近的标签(动画当然必须是带有标签的时间轴)
    • "labelsDirectional" - snap: "labelsDirectional"捕捉到最近一次滚动方向上的时间轴内最近的标签。例如,如果你稍微向下一个标签滚动了一点(然后停止),即使当前滚动位置在技术上最接近当前/最后一个标签,它也会捕捉到该方向上的下一个标签。这可以使用户体验更加直观。
    • 对象- 像snap: {snapTo: "labels", duration: 0.3, delay: 0.1, ease: "power1.inOut"}完全可以通过以下任意属性进行自定义(仅需要"snappTo"):
      • snapTo[数字 | 数组 | 函数 | "标签"] - 确定捕捉逻辑(如上所述)
      • delay[数字] - 最后一次滚动事件与捕捉动画开始之间的延迟(以秒为单位)。默认值是scrub的一半(如果scrub不是数字,则为0.1)
      • directional[布尔值] - 默认情况下(从3.8.0版本开始),捕捉是具有方向性的,意味着它会沿着用户最后一次滚动的方向进行,但你可以通过设置来禁用此功能directional: false.
      • 持续时间[数字 | 对象] - 捕捉动画的持续时间(以秒为单位)。duration: 0.3将始终耗时0.3秒,但你也可以像这样定义一个范围对象duration: {min: 0.2, max: 3}根据速度将其限制在给定范围内。这样,如果用户在靠近捕捉点的位置停止滚动,它所需的时间就会少于自然停止点远离捕捉点的情况。
      • ease[字符串 | 函数] - 捕捉动画应使用的ease缓动方式。默认值是"power3"。
      • inertia[布尔值] - 告诉ScrollTrigger考虑惯性,请设置inertia: false
      • onStart[函数] - 当捕捉开始时调用的函数
      • onInterrupt[函数] - 当捕捉被中断时调用的函数(例如如果用户在捕捉过程中开始滚动)
      • onComplete[函数] - 当捕捉完成时调用的函数
  • start

    字符串 | 数字 | 函数- 确定ScrollTrigger的起始位置。
    查看更多详细信息
    可以是以下任一选项:

    • 字符串- 描述触发器上的某个位置和触发器scroller上的某个位置,这两个位置相遇时就启动ScrollTrigger。例如,"top center"表示的是“当触发器的顶部碰到滚动区域中心的时候”(并且默认情况下滚动区域是视口)。"bottom 80%"表示的是“当触发器的底部碰到视口顶部往下80%的位置”(假设是垂直滚动)。你可以使用诸如“top”、“bottom”、“center”(或者“left”和“right”如果horizontal: true)之类的关键词,或类似“80%”的百分比,或类似“100px”的像素值。百分比和像素总是相对于元素/滚轮的左上角。你甚至可以使用复杂的相对值,例如"top bottom-=100px"表示“当触发器的顶部碰到视口/滚动条底部上方100px的位置”
    • 数字- 一个确切的滚动值,比如200表示当视口/滚动条正好滚动了200像素时触发。
    • 函数- 每次ScrollTrigger计算其位置时都会调用的一个函数(通常是在创建时以及滚动条大小改变时)。它应该返回上述描述的字符串或数字。这让动态计算值变得简单。和其他回调一样,该函数只接收ScrollTrigger实例本身作为参数,因此你可以例如根据前一个ScrollTrigger的结束位置来设定当前位置,比如start: self => self.previous().end

    这是一个静态位置,在ScrollTrigger创建时以及滚动条调整大小时根据正常文档流中的位置进行计算。它不会持续重新计算,因此例如如果你对触发器/endTrigger进行了动画处理,它不会自动更新对应的开始/结束值,因为ScrollTrigger高度优化了性能。你可以调用ScrollTrigger.refresh()来强制重新计算。默认值是"top bottom"除非pin: true设置了,在这种情况下默认值是"top top". clamp() 值 (版本3.12+)在你的起始值外面包裹"clamp()",告诉ScrollTrigger始终保持计算值位于0(页面顶部)和最大滚动位置之间,使其永远不会超出页面边界。实际上,这确保了任何“页面上方”内容(触发器位于页面顶部视口内)不会一开始就出现部分擦除动画。例如,start: "clamp(top bottom)"- 任何常规基于字符串的值都可以放在clamp()里面。比如"clamp(20px 80%)"。这里是进一步解释的视频:

  • toggleActions

    字符串- 决定在四个不同的切换位置如何控制关联的动画 -onEnter, onLeave, onEnterBack,以及onLeaveBack,顺序如上。默认值是play none none none。所以toggleActions: "play pause resume reset"动画在进入时播放,在离开时暂停,在反向再次进入时恢复,滚动完全回到起点之外时重置(倒回到开头)。你可以对每个动作使用以下任意关键字:“play”、“pause”、“resume”、“reset”、“restart”、“complete”、“reverse”和“none”。
  • 切换类

    字符串 | 对象- 当 ScrollTrigger 切换激活/非激活状态时,向元素(或多个元素)添加/移除一个类。它可以是以下之一:
    • 字符串- 要添加到trigger元素的类名,例如toggleClass: "active"
    • 对象- 如果要为除触发器以外的其他元素切换类,请使用对象语法,例如toggleClass: {targets: ".my-selector", className: "active"}。"targets" 可以是选择器文本、对元素的直接引用,或者元素数组。
    注意toggleActions不适用于toggleClass。如需以不同方式切换类名,请使用回调函数(onEnter、onLeave、onLeaveBack 和 onEnterBack)。
  • 触发器

    字符串 | 元素- 在正常文档流中用于计算 ScrollTrigger 开始位置的元素(或该元素的选择器文本)。
寻找平滑滚动功能?

GSAP 自带的ScrollSmoother工具是在 ScrollTrigger 的基础上构建的,因此它完全集成且非常易于使用。它基于原生滚动技术,避免了大多数困扰其他平滑滚动库的可访问性问题。

属性

.animation: 补间动画 | 时间轴 | 未定义

[只读] 与 ScrollTrigger 实例关联的Tween或者时间轴(如果有的话)。

.direction: 数值

[只读] 反映即时滚动方向,其中1是向前,-1是向后。

.end: 数值

[只读] ScrollTrigger 的结束滚动位置(数值型,单位为像素)。

.isActive:布尔值

[只读] 仅当滚动位置在 ScrollTrigger 实例的开始和结束位置之间时才为truetrue。

ScrollTrigger.isTouch: 数值

一种区分当前设备触控能力的方式 —0表示仅支持鼠标/指针操作(无触控),1表示仅支持触控,2表示同时支持两者。

.pin: 元素 | 未定义

[只读] 固定元素(如果已定义)。如果使用的是选择器文本,例如 ".pin",则pin将是实际元素本身(而不是选择器文本)

progress(进度): 数值

[只读] ScrollTrigger 实例的整体进度,其中 0 表示起始位置,0.5 表示中间位置,1 表示结束位置。

scroller: 元素 | window

[只读] 与 ScrollTrigger 关联的 scroller 元素(或 window)。它是其滚动条与 ScrollTrigger 相关联的对象。默认情况下,它是 window(视口)。

start: 数值

[只读] ScrollTrigger 的起始滚动位置(数值型,单位为像素)。

.trigger: 元素 | 未定义

[只读] 触发器元素(如果已定义)。如果使用的是选择器文本,例如 ".trigger",则trigger将是实际元素本身(而不是选择器文本)

.vars: 对象

[只读] 用于创建 ScrollTrigger 实例的 vars 配置对象

方法

.disable( revert:boolean, allowAnimation:Boolean )

禁用 ScrollTrigger 实例,立即取消固定并恢复 ScrollTrigger 对 DOM 所做的任何与固定相关的更改。

.enable( reset:Boolean )

启用 ScrollTrigger 实例

.getTween( snap:Boolean ) : 补间动画

返回scrub默认补间动画(tween),或返回捕捉补间动画(getTween(true))

.getVelocity( ) : Number

获取以像素每秒为单位的滚动速度

.kill( revert:boolean, allowAnimation:Boolean )

销毁 ScrollTrigger 实例,立即取消固定并恢复 ScrollTrigger 对 DOM 所做的任何与固定相关的更改,并移除所有与滚动相关的监听器等,使该实例可以被垃圾回收器回收。如果您只想暂时禁用 ScrollTrigger,请使用disable()方法代替。

.labelToScroll( label:String ) : 数值

将时间轴标签转换为对应的滚动位置(仅适用于“animation”属性为时间轴的 ScrollTriggers)。

.next( ) : ScrollTrigger 实例

返回刷新顺序中的下一个 ScrollTrigger。

.previous( ) : ScrollTrigger 实例

返回刷新顺序中的上一个 ScrollTrigger。

.refresh()

强制 ScrollTrigger 实例重新计算其开始和结束值(即 ScrollTrigger 将被激活的滚动位置)。

.scroll( position:Number ) : 数值 | null

获取/设置关联的 scroller 的滚动位置(数值型)。

ScrollTrigger.addEventListener( type:String, callback:Function ) : null

添加一个监听器以监听以下事件之一:"scrollStart"、"scrollEnd"、"refreshInit"、"revert"、"matchMedia" 或 "refresh",这些事件会在任意此类 ScrollTrigger 相关事件发生时全局派发(不绑定特定实例)。

ScrollTrigger.batch( triggers:选择器文本 | 数组, vars:对象 ) : 数组

创建一组协调的 ScrollTrigger(每个目标元素一个),将它们的回调函数(onEnter、onLeave 等)在一定间隔内批量处理,提供一个整洁的数组,以便您可以轻松地执行类似操作,比如对同时进入视口的所有元素进行交错动画。

ScrollTrigger.clearMatchMedia( query:String )

ScrollTrigger.clearScrollMemory( scrollRestoration:String )

清除 ScrollTrigger 中记录的任何滚动位置,使得 refresh() 后不会恢复任何滚动位置。通常不需要这样做,但在某些以非传统方式处理路由的框架中可能很有用。

ScrollTrigger.config( vars:Object )

允许您配置 ScrollTrigger 某些全局行为,例如limitCallbacks

ScrollTrigger.create( vars:Object ) : ScrollTrigger

创建一个独立的 ScrollTrigger 实例

ScrollTrigger.defaults( config:Object ) : null

允许您设置应用于每个 ScrollTrigger 创建时的默认值,例如toggleActions, markers,等等。

ScrollTrigger.getAll( ) : 数组

返回所有 ScrollTrigger 实例的数组

ScrollTrigger.getById( id:String ) : ScrollTrigger

返回被分配了相应 ScrollTrigger 的实例。id

ScrollTrigger.isInViewport( Element:Element | String, proportion:Number, horizontal:Boolean ) : Boolean

返回true如果元素位于视口中。您可以选择性地指定最小比例,例如ScrollTrigger.isInViewport(element, 0.2)将仅在true元素的至少20%位于视口内时才返回。

ScrollTrigger.isScrolling( ) : Boolean

指示是否有任何与 ScrollTrigger 相关的滚动条正在进行滚动。

ScrollTrigger.killAll( ) ;

立即调用kill()所有ScrollTriggers 上(如果存在主 ScrollSmoother,则不包括它)。

ScrollTrigger.matchMedia( vars:Object )

[已弃用]允许您设置仅适用于特定视口大小的 ScrollTriggers(使用媒体查询)。

ScrollTrigger.maxScroll( scroller:Element | window, horizontal:Boolean ) : Number

获取特定元素/滚动条的最大滚动值的实用函数。例如,如果元素/滚动条的高度为500px且包含800px的内容,则 maxScroll() 将返回300。

ScrollTrigger.normalizeScroll( normalize:Boolean | Object ) : ScrollObserver | null

强制滚动在 JavaScript 线程上执行,确保屏幕更新同步,并防止地址栏在[大多数]移动设备上显示/隐藏。

ScrollTrigger.observe( config:Object ) : Observer

一种超级灵活、统一的方式来感知所有(触摸/鼠标/指针)设备上的有意义事件,而无需处理所有实现细节。触发简单的回调如 onUp、onDown、onLeft、onRight、onChange、onHover、onDrag 等。功能上等同于Observer.create()

ScrollTrigger.positionInViewport( element:Element | String, referencePoint:String | Number, horizontal:Boolean ) : Number

返回一个归一化的值,表示元素相对于视口的位置,其中0表示在视口顶部,0.5表示在中间,1表示在底部。例如,如果元素的顶部在视口顶部下方80%,则以下代码将返回0.8:ScrollTrigger.positionInViewport(element, "top");

ScrollTrigger.refresh( safe:Boolean )

重新计算页面上所有 ScrollTriggers 的位置;当窗口/滚动条调整大小时通常会自动发生此操作,但可以通过调用强制执行ScrollTrigger.refresh()

ScrollTrigger.removeEventListener( type:String, callback:Function ) : null

移除事件监听器

ScrollTrigger.saveStyles( targets:String | Element | Array )

内部记录给定元素当前的内联 CSS 样式,以便当 ScrollTrigger 回滚时(通常用于 refresh() 或 matchMedia() 更改),这些元素也会相应回滚,即使它们有添加/修改内联样式的动画。可以将其视为拍摄内联 CSS 的快照并告诉 ScrollTrigger“当您内部回滚时,请仅重新应用这些内联样式并丢弃其他所有样式”。

ScrollTrigger.scrollerProxy( scroller:String | Element, vars:Object )

允许您劫持scrollTop和/或scrollLeft特定滚动元素的 getter/setter,以便您可以实现平滑滚动或其他自定义效果。

ScrollTrigger.snapDirectional( incrementOrArray:Number | Array ) : Function

返回一个捕捉函数,您可以传入任何值进行捕捉以及方向,其中1表示向前(大于)和-1表示向后(小于)。

ScrollTrigger.sort( func:Function ) : Array

对 ScrollTrigger 实例的内部数组进行排序以控制它们的refresh()(计算它们的开始/结束值)顺序。

ScrollTrigger.update( )

检查滚动条位置并相应更新所有 ScrollTrigger 实例的progressdirection值,控制动画(如果需要的话)并触发适当的回调。

常见问题

ScrollTrigger 是如何工作的?它是否与 IntersectionObserver 类似?

ScrollTrigger 并不会不断监视每个元素并在每次 tick 中检查其在视口中的位置。我们非常关注性能,这将会过于耗费资源。相反,ScrollTrigger 提前进行处理以确定起点/终点在哪里在自然文档流中。换句话说,“这个 ScrollTrigger 在滚动条位于 ___ 和 ____ 之间时处于激活状态”。然后,它会对“scroll”事件进行去抖动,并且只在下一个 requestAnimationFrame 中更新内容,完美地与 GSAP 和屏幕刷新同步。它仅仅监视滚动位置。仅此而已。这意味着它非常快速.

ScrollTrigger 自动侦听视口/滚动条的“resize”事件,并相应地重新计算所有起始/结束位置(onRefresh)。实际上,由于调整大小/刷新是 CPU 密集型的操作,它会在 resize 事件间隔达到 200ms 后才开始工作。是的,我们寻找每一个机会来最大化性能。

IntersectionObserver是大多数现代浏览器中的原生特性,具有以下不同之处:

  • 它会持续“观察”元素,检测它们何时进入或离开视口,而不受滚动的影响。
  • 它对于跟踪元素在两点之间的位置并不用,例如为了根据滚动擦除动画。
  • 它确实允许您观察多个元素,并触发单个回调,该回调可以循环并对刚刚进入的元素触发分步动画。

ScrollTrigger 并未在底层使用 IntersectionObserver,因为它缺乏必要的功能和兼容性。当然,您可以同时使用 IntersectionObserver 和 ScrollTrigger。

固定(pinning)在底层是如何工作的?

  • 被固定的元素会被立即包裹在一个<div>中,并设置了固定的宽度/高度与原元素匹配。此包装器会添加一个名为“pin-spacer”的类。你可以把它想象成一个代理元素,用来撑开DOM中被固定元素原来所在的空间,以便当它切换到position: fixedfixed
  • 默认情况下,会在pin-spacer的底部(或对于horizontal: true的情况是右侧)添加内边距(padding),这样[大多数情况下]下方或右侧的内容会被进一步向下/向右推移。当被固定的元素取消固定时,其下方/右侧的内容应该已经跟上了。比如,如果被固定元素保持固定状态300px,则会添加300px的padding。
操作演示

SnorklTV的ScrollTrigger Express课程中有一段有关固定(pinnging)功能的视频可能有助于你理解。

  • 当ScrollTrigger处于活动状态时(即滚动位置在start和end之间时),它会将被固定元素设置为position: fixedfixedposition: fixed否则的话,如果滚动容器不是视窗(viewport),则从不会使用fixed,pinReparent设置为true(如果可以的话我们建议避免使用该选项),此时被固定元素会被重新放置到<body>body
  • 当ScrollTrigger变为非活动状态时,被固定元素会恢复到原来的positionpositiontransform的transform值来准确定位。
  • 当窗口/滚动容器大小改变时,所有ScrollTrigger都会重新计算它们的起始(start)和结束(end)位置(onRefreshonRefresh

为什么不直接使用transform来规避使用position: fixedfixedposition: fixedfixed定位

在scrub: true的情况下duration是如何工作的?

如果你有一个ScrollTriggerscrub: true并且该ScrollTrigger关联了一个时间线或补间动画(timeline或tween animation),那么动画中每个补间的持续时间(duration)代表了总滚动距离之间的比例。这个比例决定了该补间在整个动画持续时间内所占的距离比例。通过一个示例最容易理解:

假设你有一个包含三个顺序播放补间的时间线:第一个补间为1秒,第二个为3秒,第三个也是1秒。而应用在这个时间线上的ScrollTrigger动画将在整个视口高度距离上播放(可能是触发器使用的参数值为start: "center bottom"end: "center top")。

如果scrub: true"top bottom"

加载中...

如果你将所有补间的持续时间设置为相同的数值,例如1秒,那么各个补间所占的比例就会相等:100% -> 66%,66% -> 33%,33% -> 0%。这是因为整个时间线的总时长是3秒,所以每个部分各占三分之一即33%。

换句话说,duration值本身并不重要,重要的是每个补间相对于整个时间线总时长所占的比例。

如果你想让动画占用更多的滚动距离才能完成,请延长start到end的距离。例如,你可以设置end: "+=4000"end: "+=3000"

如何在我的项目中引入undefined?

参考安装页面,其中列出了所有的选项(CDN、NPM、下载等),甚至还有一个交互式帮助工具可以提供所需的代码。非常简单。不要忘记在你的项目中像下面这样注册undefined

gsap.registerPlugin(undefined)

这个插件是否包含在GSAP核心中?

不,你必须单独加载/导入它

它在开发期间正常工作,但在生产构建中突然停止工作!我该怎么办?

你的构建工具可能在进行tree shaking时删除了这个插件,并且你忘记了注册undefined(这样可以防止被摇树优化移除)。只需像下面这样注册插件即可:

gsap.registerPlugin(undefined)

多次注册一个插件是否有坏处?

没有,完全没有问题。这样做既没有好处,也不会造成伤害。

我该如何在我的项目中引入ScrollTrigger?

参考安装页面,其中列出了所有的选项(CDN、NPM、下载等),甚至还有一个交互式帮助工具可以提供所需的代码。非常简单。不要忘记在你的项目中像下面这样注册 ScrollTrigger

gsap.registerPlugin(ScrollTrigger)

这个插件是否包含在GSAP核心中?

不,你必须单独加载/导入它

它在开发期间正常工作,但在生产构建中突然停止工作!我该怎么办?

你的构建工具可能在进行tree shaking时删除了这个插件,并且你忘记了注册 ScrollTrigger(这样可以防止被摇树优化移除)。只需像下面这样注册插件即可:

gsap.registerPlugin(ScrollTrigger)

多次注册一个插件是否有坏处?

没有,完全没有问题。这样做既没有好处,也不会造成伤害。

示例演示

查看完整系列的滚动动画演示在 CodePen 上。

无噪 Logo
无噪文档
中文文档 · 复刻官网
查看所有 ↗