什么是registerEffect
js
<div class="card">
<h1>案例 41:registerEffect 自定义效果</h1>
<p>封装常用动画为可复用效果。</p>
<div class="row">
<div class="box" id="boxA"></div>
<div class="box" id="boxB"></div>
<div class="box" id="boxC"></div>
</div>
<button id="play">播放效果</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.1/dist/gsap.min.js"></script>
<script>
const playButton = document.querySelector("#play");
// 注册自定义效果
gsap.registerEffect({
name: "popIn",
effect: (targets, config) => {
return gsap.fromTo(
targets,
{ scale: 0.6, opacity: 0 },
{
scale: 1,
opacity: 1,
duration: config.duration,
stagger: config.stagger,
ease: config.ease
}
);
},
defaults: { duration: 0.6, stagger: 0.08, ease: "back.out(1.6)" },
extendTimeline: true
});
playButton.addEventListener("click", () => {
gsap.effects.popIn(".box");
});
</script>
gsap.registerEffect() 是 GSAP(GreenSock Animation Platform) 提供的一个强大功能,用于将常用的动画逻辑封装成可复用、可配置的"自定义效果"(custom effect),就像创建自己的动画"函数库"一样。
📌 简单定义:
registerEffect允许你定义一个命名动画模板(如"popIn"),之后通过gsap.effects.effectName(targets, config)一行代码即可在任意地方调用它,实现代码复用、语义化和团队协作标准化。
✅ 核心作用:
1. 封装复杂动画逻辑
把重复的 fromTo、timeline 等逻辑打包成一个"黑盒"。
2. 支持参数配置
通过 config 对象传入自定义参数(如 duration、stagger)。
3. 提供默认值
通过 defaults 设置常用参数的默认值,调用时可省略。
4. 无缝集成 GSAP 生态
- 可用于
Timeline - 支持
stagger - 返回动画实例(可
play/pause/reverse)
🔧 在你的代码中:
js
gsap.registerEffect({
name: "popIn", // 效果名称
effect: (targets, config) => {
return gsap.fromTo(
targets,
{ scale: 0.6, opacity: 0 },
{
scale: 1,
opacity: 1,
duration: config.duration,
stagger: config.stagger,
ease: config.ease
}
);
},
defaults: {
duration: 0.6,
stagger: 0.08,
ease: "back.out(1.6)"
},
extendTimeline: true // 允许在 Timeline 中直接使用 .popIn()
});
然后调用:
js
gsap.effects.popIn(".box"); // 所有 .box 元素执行 popIn 动画
效果:
- 三个盒子依次从小且透明放大到正常尺寸;
- 带有弹性回弹(
back.out缓动); - 每个盒子延迟
0.08s启动(stagger)。
🌟 优势 vs 普通函数封装:
| 普通函数 | registerEffect |
|---|---|
| 需手动管理返回值 | ✅ 自动注册到 gsap.effects 命名空间 |
| 无法在 Timeline 中直接使用 | ✅ 开启 extendTimeline: true 后可用 tl.popIn(...) |
| 参数处理需自己写 | ✅ 自动合并 config 与 defaults |
| 团队协作需文档说明 | ✅ 效果名称即文档(gsap.effects.popIn 语义清晰) |
⚙️ 参数详解:
| 字段 | 说明 |
|---|---|
name |
效果名称(字符串),注册后可通过 gsap.effects[name] 调用 |
effect(targets, config) |
动画工厂函数: - targets: DOM 元素或选择器 - config: 用户传入的配置对象 |
defaults |
默认配置(会被 config 覆盖) |
extendTimeline |
若为 true,可在 Timeline 实例上直接调用该效果: timeline.popIn(".box") |
🛠️ 更多使用方式:
1. 传入自定义参数
js
gsap.effects.popIn(".item", {
duration: 1,
stagger: 0.2,
ease: "elastic.out(1, 0.5)"
});
2. 在 Timeline 中使用(需 extendTimeline: true)
js
const tl = gsap.timeline();
tl.popIn(".box", { duration: 0.5 });
3. 返回 Timeline 实现复杂效果
js
effect: (targets) => {
const tl = gsap.timeline();
tl.from(targets, { x: -100, opacity: 0 })
.to(targets, { rotation: 360 }, "<");
return tl;
}
🎨 典型应用场景:
- UI 组件库:统一按钮点击、卡片入场、提示弹出等动效
- 设计系统:确保全站动画风格一致
- 游戏开发:角色受伤、道具拾取等特效复用
- 快速原型:设计师给效果命名,开发者一键实现
⚠️ 注意事项:
- 效果名称全局唯一,避免冲突;
targets可以是单个元素、数组或 CSS 选择器字符串;- 免费功能 :
registerEffect是 GSAP 核心 API,无需额外插件或会员; - 如果效果内部使用了
ScrollTrigger等插件,需确保已注册。
📚 官方文档:
👉 greensock.com/docs/v3/GSA...
✅ 总结:
gsap.registerEffect()是 GSAP 的"动画组件化"方案------它将零散的动画代码提炼为可命名、可配置、可复用的效果模块,大幅提升开发效率与代码可维护性,是构建大型交互动效项目的最佳实践。
什么是ScrollTrigger
js
<header>
<h1>案例 42:ScrollTrigger Pin 固定</h1>
<p>滚动时固定元素并配合进度动画。</p>
</header>
<div class="spacer"></div>
<section>
<div class="panel">
<div class="pin" id="pin">我被固定了</div>
</div>
</section>
<section>
<div class="panel">继续往下滚动</div>
</section>
<div class="spacer"></div>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.1/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.1/dist/ScrollTrigger.min.js"></script>
<script>
// 注册 ScrollTrigger 插件
gsap.registerPlugin(ScrollTrigger);
// pin + scrub 绑定滚动进度
gsap.to("#pin", {
scale: 0.8,
rotation: 10,
scrollTrigger: {
trigger: "#pin",
start: "top center",
end: "+=400",
scrub: true,
pin: true
}
});
</script>
ScrollTrigger 是 GSAP(GreenSock Animation Platform) 中最强大、最受欢迎的插件之一,它能将滚动行为 (用户上下滑动页面)与GSAP 动画无缝结合,实现如"视差滚动"、"进度条动画"、"元素固定(Pin)"、"滚动触发动画"等高级交互动效。
📌 简单定义:
ScrollTrigger让你把任何 GSAP 动画"绑定"到页面滚动位置上------当用户滚动到某个区域时,动画自动播放、暂停、反向或跟随滚动进度实时更新。
它本质上是一个滚动驱动的动画控制器。
✅ 核心能力:
1. 触发动画(Toggle)
- 当元素进入/离开视口时,播放/暂停动画。
js
scrollTrigger: {
trigger: ".section",
start: "top center", // 当 .section 顶部到达视口中心时触发
toggleActions: "play none none reverse"
}
2. 滚动进度驱动(Scrub)
- 动画进度完全跟随滚动位置,形成"拖拽式"效果。
js
scrollTrigger: {
scrub: true // 滚动多少,动画就播放到多少
}
3. 固定元素(Pin) ← 你的案例重点
- 在滚动过程中将元素"钉"在视口某处,直到滚动结束。
js
scrollTrigger: {
pin: true, // 固定 trigger 元素
// 或 pin: "#otherElement" 固定其他元素
end: "+=400" // 固定持续 400px 的滚动距离
}
4. 标记与指示器(Markers)
- 开发时显示调试线(start/end 位置),方便调整。
js
scrollTrigger: {
markers: true // 显示绿色(start)和红色(end)标记线
}
js
gsap.to("#pin", {
scale: 0.8,
rotation: 10,
scrollTrigger: {
trigger: "#pin", // 监听 #pin 元素的滚动位置
start: "top center", // 当 #pin 顶部到达视口中心时开始
end: "+=400", // 滚动再往下 400px 后结束
scrub: true, // 动画进度随滚动平滑更新
pin: true // 在 start → end 区间内,#pin 被固定住
}
});
用户体验流程:
- 向下滚动,当
#pin到达屏幕中央时 → #pin被固定在当前位置(不再随页面滚动而移动);- 继续滚动的 400px 过程中,
#pin逐渐缩小并旋转 (scale: 0.8,rotation: 10); - 滚动超过 400px 后,固定解除,
#pin随页面继续滚动。
💡 这就是"固定 + 进度动画"的经典组合,常用于产品展示、故事叙述等场景。
🌟 典型应用场景:
| 效果 | 描述 |
|---|---|
| 视差滚动 | 背景图慢速移动,前景快移 |
| 进度条/数字计数器 | 滚动时数字从 0 增长到目标值 |
| 元素入场/离场 | 卡片滑入、标题淡入 |
| 固定导航栏 | 滚动到某区域时导航栏吸顶 |
| 横向滚动画廊 | 垂直滚动驱动水平位移 |
| 3D 视差 | 滚动时多层元素产生景深感 |
⚙️ 关键配置项说明:
| 配置 | 作用 |
|---|---|
trigger |
触发动画的参考元素(默认为动画目标) |
start |
动画开始的滚动位置(如 "top center") |
end |
动画结束的滚动位置(如 "bottom bottom" 或 "+=500") |
scrub |
true = 平滑跟随滚动;number = 延迟秒数 |
pin |
true = 固定 trigger;"#id" = 固定指定元素 |
toggleActions |
控制进入/离开时的播放行为(play pause resume reset) |
📏 位置语法 :
"edge1 edge2"
edge1: trigger 元素的边缘(top/bottom/center)edge2: 视口的边缘(top/bottom/center)
例如:"top bottom"= trigger 顶部碰到视口底部时触发
⚠️ 注意事项:
- 必须注册插件:
gsap.registerPlugin(ScrollTrigger); pin会自动包裹元素 并设置position: sticky或fixed,无需手动写 CSS;- 如果页面高度不足,可能看不到完整效果(需确保有足够滚动空间);
- 免费功能 :
ScrollTrigger是 GSAP 标准插件(无需 Club 会员); - 移动端性能优秀,支持触摸滚动。
📚 官方资源:
✅ 总结:
ScrollTrigger是 GSAP 赋予网页"电影级滚动叙事能力"的核心插件------它将枯燥的滚动转化为精准、流畅、富有表现力的动画触发器,是现代高端网站交互动效的事实标准。
js
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>GSAP 案例 43 - ScrollTrigger toggleClass</title>
<style>
body {
margin: 0;
font-family: "Segoe UI", sans-serif;
background: #0f172a;
color: #e2e8f0;
}
header {
padding: 80px 24px 40px;
text-align: center;
}
section {
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
.panel {
width: 70%;
max-width: 680px;
padding: 32px;
border-radius: 24px;
background: #111827;
box-shadow: 0 25px 60px rgba(15, 23, 42, 0.5);
transition: transform 0.3s ease, background 0.3s ease;
}
.panel.active {
transform: scale(1.03);
background: #1f2937;
}
.spacer {
height: 40vh;
}
</style>
</head>
<body>
<header>
<h1>案例 43:toggleClass 触发样式</h1>
<p>滚动到卡片时添加高亮样式。</p>
</header>
<div class="spacer"></div>
<section>
<div class="panel" id="panel">滚动到这里会高亮</div>
</section>
<div class="spacer"></div>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.1/dist/gsap.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.1/dist/ScrollTrigger.min.js"></script>
<script>
// 注册 ScrollTrigger 插件
gsap.registerPlugin(ScrollTrigger);
ScrollTrigger.create({
trigger: "#panel",
start: "top 70%",
end: "top 40%",
toggleClass: "active"
});
</script>
</body>
</html>
这段代码展示了 GSAP 的 ScrollTrigger 插件 中一个非常实用的功能:toggleClass。它的作用是------
当用户滚动到指定区域时,自动给目标元素添加或移除一个 CSS 类名,从而触发样式变化(如高亮、缩放、变色等),无需手动编写 JavaScript 逻辑。
🔍 逐行解析核心部分:
js
ScrollTrigger.create({
trigger: "#panel", // 监听 #panel 元素的滚动位置
start: "top 70%", // 当 #panel 的顶部进入视口 70% 位置时 → 添加类
end: "top 40%", // 当 #panel 的顶部到达视口 40% 位置时 → 移除类
toggleClass: "active" // 要切换的 CSS 类名
});
📏 滚动位置语法说明:
"top 70%"表示:trigger 元素的顶部 与 视口的 70% 高度线 对齐。- 视口从上到下:
0%(顶部)→100%(底部) - 所以
70%在视口偏下方,40%在视口偏上方。
✅ 效果逻辑:
- 向下滚动,当
#panel进入视口下部(70%) 时 → 添加.active- 继续滚动,当
#panel上升到视口上部(40%) 时 → 移除.active- 向上滚动时行为相反(进入
end区域加类,离开start区域去类)
🎨 CSS 配合实现高亮:
css
.panel {
/* 默认样式 */
background: #111827;
transform: scale(1);
}
.panel.active {
/* 滚动到区域时激活 */
background: #1f2937; /* 背景变亮 */
transform: scale(1.03); /* 轻微放大 */
}
通过 transition 实现了平滑过渡,视觉反馈更自然。
✅ toggleClass 的优势:
| 传统方式 | 使用 toggleClass |
|---|---|
需监听 scroll 事件 + 计算位置 + 手动 classList.add/remove |
✅ 一行配置自动完成 |
| 容易性能差(频繁触发 scroll) | ✅ ScrollTrigger 内部优化(requestAnimationFrame + 节流) |
| 逻辑分散,难维护 | ✅ 声明式写法,意图清晰 |
⚙️ 其他用法示例:
1. 切换多个类
js
toggleClass: "highlight zoom-in"
2. 作用于其他元素
js
ScrollTrigger.create({
trigger: "#section",
toggleClass: { targets: ".nav-item", className: "current" }
});
3. 配合 onToggle 回调
js
ScrollTrigger.create({
trigger: "#panel",
toggleClass: "active",
onToggle: self => console.log("是否激活:", self.isActive)
});
⚠️ 注意事项:
toggleClass是ScrollTrigger的内置功能,无需额外插件;- 类名切换是双向的:进入区间加类,离开区间去类;
- 如果
start和end顺序颠倒(如start: "top 40%",end: "top 70%"),则行为反转(常用于"离开时激活"); - 移动端兼容性良好,支持触摸滚动。
📚 官方文档:
👉 greensock.com/docs/v3/Plu...
✅ 总结:
ScrollTrigger.toggleClass是实现"滚动高亮"、"区域激活"等交互的最简洁方案------它将复杂的滚动监听与 DOM 操作封装成声明式配置,让你专注于 CSS 样式设计,大幅提升开发效率与代码可读性。
getProperty + getVelocity 是什么
js
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>GSAP 案例 44 - velocity / getProperty</title>
<style>
body {
margin: 0;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
font-family: "Segoe UI", sans-serif;
background: #0f172a;
color: #e2e8f0;
}
.card {
width: 620px;
padding: 28px;
border-radius: 20px;
background: #111827;
box-shadow: 0 20px 50px rgba(15, 23, 42, 0.5);
}
.track {
height: 100px;
border-radius: 14px;
background: #0b1220;
position: relative;
margin: 18px 0;
}
.dot {
width: 32px;
height: 32px;
border-radius: 50%;
background: #a3e635;
position: absolute;
top: 34px;
left: 20px;
}
.info {
font-size: 14px;
color: #94a3b8;
}
button {
width: 100%;
margin-top: 12px;
padding: 12px 16px;
border: none;
border-radius: 12px;
font-size: 14px;
cursor: pointer;
background: #a3e635;
color: #0f172a;
font-weight: 600;
}
</style>
</head>
<body>
<div class="card">
<h1>案例 44:getProperty + getVelocity</h1>
<p>读取属性与速度,了解当前运动状态。</p>
<div class="track">
<div class="dot" id="dot"></div>
</div>
<div class="info" id="info">x: 0 | velocity: 0</div>
<button id="play">播放</button>
</div>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.1/dist/gsap.min.js"></script>
<script>
const dot = document.querySelector("#dot");
const info = document.querySelector("#info");
const playButton = document.querySelector("#play");
const tween = gsap.to(dot, {
x: 480,
duration: 2,
ease: "power1.inOut",
paused: true,
onUpdate: () => {
const x = Math.round(gsap.getProperty(dot, "x"));
const velocity = Math.round(tween.getVelocity());
info.textContent = `x: ${x} | velocity: ${velocity}`;
}
});
playButton.addEventListener("click", () => {
tween.restart();
});
</script>
</body>
</html>
在 GSAP(GreenSock Animation Platform) 中,gsap.getProperty() 和 tween.getVelocity() 是两个用于实时读取动画状态的实用工具方法,常用于调试、交互反馈或基于物理状态的逻辑判断。
✅ 一、gsap.getProperty(target, property, unit?)
🔍 作用:
获取目标元素当前被 GSAP 控制的某个 CSS 属性或 transform 值。
即使该属性是通过 transform(如 x, y, rotation)设置的,也能正确返回数值。
📌 语法:
js
gsap.getProperty(element, "propertyName", "unit?");
element:DOM 元素"propertyName":属性名,如"x","opacity","backgroundColor""unit?"(可选):指定返回单位,如"px","deg";默认返回纯数字
js
const x = Math.round(gsap.getProperty(dot, "x"));
- 实时读取小球当前的 水平位移
x值(以像素为单位的数字) - 即使你用
gsap.to(dot, { x: 480 })设置的是"相对位移",getProperty也能返回绝对计算值
⚠️ 注意:它读取的是 GSAP 内部记录的值 ,不是
getComputedStyle()的结果,因此更准确、更高效(尤其对transform属性)。
✅ 二、tween.getVelocity()
🔍 作用:
获取当前动画目标属性的瞬时速度(单位/秒)。
对于多属性动画(如同时动 x 和 y),默认返回第一个属性的速度;也可指定属性:
js
tween.getVelocity("x") // 获取 x 方向速度
📌 特点:
- 速度单位:每秒变化量 (如
px/s,deg/s) - 方向有正负:
+表示正向(如向右),-表示反向(如向左) - 在
onUpdate回调中调用最准确
js
const velocity = Math.round(tween.getVelocity());
- 返回小球在
x方向上的当前速度(px/s) - 动画开始和结束时速度接近
0(因为使用了power1.inOut缓动) - 中间时刻速度最大(约 ±240 px/s)
🔬 动画过程中的典型值(duration: 2s, x: 0 → 480):
| 时间 | x 值 |
velocity (px/s) |
说明 |
|---|---|---|---|
| 0s | 0 | 0 | 起始,静止 |
| 0.5s | ~120 | ~240 | 加速到峰值 |
| 1.0s | 240 | 0 | 中点,瞬时静止(inOut 对称) |
| 1.5s | ~360 | ~-240 | 反向加速(减速阶段) |
| 2.0s | 480 | 0 | 结束,静止 |
📌 注意:
power1.inOut是先加速后减速,在中点速度为 0(这是缓动函数决定的)。
🌟 典型应用场景:
| 场景 | 用途 |
|---|---|
| 物理模拟 | 根据速度决定反弹强度、摩擦力 |
| 交互反馈 | 鼠标松开时根据拖拽速度继续滑动(惯性滚动) |
| 游戏开发 | 判断角色是否在移动、碰撞检测 |
| 动画调试 | 实时监控属性与速度变化 |
| 动态效果 | 速度越大,粒子越多 / 模糊越强 |
⚠️ 注意事项:
getProperty仅能读取 GSAP 已经控制过的属性;getVelocity()必须在 动画进行中 调用才有意义(暂停/结束后返回 0);- 对于
Timeline,需在具体tween上调用getVelocity(); - 这两个方法都是 GSAP 核心 API,无需额外插件。
📚 官方文档:
getProperty: greensock.com/docs/v3/GSA...getVelocity: greensock.com/docs/v3/GSA...
✅ 总结:
gsap.getProperty()和tween.getVelocity()是 GSAP 提供的"动画状态探测器"------它们让你能精确掌握元素当前的位置和运动速度,为构建基于物理、交互或调试需求的高级动画提供了关键数据支持。
什么是utils.random / wrap / interpolate
js
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>GSAP 案例 45 - utils random/wrap/interpolate</title>
<style>
body {
margin: 0;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
font-family: "Segoe UI", sans-serif;
background: #0b1020;
color: #e2e8f0;
}
.card {
width: 640px;
padding: 28px;
border-radius: 20px;
background: #111827;
box-shadow: 0 20px 50px rgba(15, 23, 42, 0.5);
}
.stage {
height: 160px;
border-radius: 16px;
background: #0f172a;
position: relative;
overflow: hidden;
margin: 18px 0;
}
.dot {
width: 30px;
height: 30px;
border-radius: 50%;
background: #38bdf8;
position: absolute;
top: 65px;
left: 20px;
}
button {
width: 100%;
padding: 12px 16px;
border: none;
border-radius: 12px;
font-size: 14px;
cursor: pointer;
background: #38bdf8;
color: #0f172a;
font-weight: 600;
}
.info {
margin-top: 8px;
font-size: 13px;
color: #94a3b8;
}
</style>
</head>
<body>
<div class="card">
<h1>案例 45:utils.random / wrap / interpolate</h1>
<p>快速生成随机值、循环值与插值。</p>
<div class="stage">
<div class="dot" id="dot"></div>
</div>
<button id="play">随机移动</button>
<div class="info" id="info"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.1/dist/gsap.min.js"></script>
<script>
const dot = document.querySelector("#dot");
const info = document.querySelector("#info");
const playButton = document.querySelector("#play");
// random 生成随机数,wrap 限制循环范围,interpolate 计算插值
const randomX = gsap.utils.random(40, 540, 1);
const wrapHue = gsap.utils.wrap(0, 360);
const mix = gsap.utils.interpolate(0, 1);
let step = 0;
playButton.addEventListener("click", () => {
const x = randomX;
const hue = wrapHue(step * 80);
const scale = mix(0.7, 1.4);
gsap.to(dot, {
x,
scale,
backgroundColor: `hsl(${hue}, 90%, 60%)`,
duration: 0.6,
ease: "power2.out"
});
info.textContent = `x: ${x}px | hue: ${hue} | scale: ${scale.toFixed(2)}`;
step += 1;
});
</script>
</body>
</html>
在 GSAP(GreenSock Animation Platform) 中,gsap.utils 是一个内置的实用工具函数集合,提供了许多高效、简洁的辅助方法,用于处理动画中常见的数学和逻辑操作。
你提到的三个方法:
gsap.utils.randomgsap.utils.wrapgsap.utils.interpolate
是其中最常用、最强大的三个工具,分别用于生成随机值 、循环限制范围 和计算插值。它们让复杂逻辑变得简单,且性能优异。
✅ 1. gsap.utils.random(min, max, [step])
🔍 作用:
生成一个指定范围内的随机数(可选步长)。
📌 语法:
js
const rand = gsap.utils.random(min, max, step);
min:最小值max:最大值step(可选):步长(如1表示整数,0.1表示保留一位小数)
⚠️ 注意:它返回的是一个函数!调用该函数才会生成新随机数。
但如果你直接传数字(如你的代码),GSAP 会自动缓存一次结果(等价于 random(40, 540, 1)())。
js
const randomX = gsap.utils.random(40, 540, 1); // 实际返回一个数字(因为未作为函数调用)
- 每次点击按钮,
x都是一个 40~540 之间的整数 - 用于让小球随机水平移动
✅ 更推荐写法(每次点击都新随机):
js
playButton.addEventListener("click", () => {
const x = gsap.utils.random(40, 540, 1)(); // 加 () 才是函数调用
});
✅ 2. gsap.utils.wrap(min, max)
🔍 作用:
将任意数值"包裹"到
[min, max)范围内,实现无缝循环(类似取模%,但支持浮点数和负数)。
📌 语法:
js
const wrap = gsap.utils.wrap(min, max);
const result = wrap(value); // 返回循环后的值
🌰 例子:
js
const wrap360 = gsap.utils.wrap(0, 360);
wrap360(400) // → 40 (400 - 360)
wrap360(-20) // → 340 (-20 + 360)
wrap360(720) // → 0 (720 % 360)
js
const hue = wrapHue(step * 80); // step=0→0, step=1→80, step=2→160, step=3→240, step=4→320, step=5→40→40...
- 实现 HSL 色相(0~360)的循环切换,避免颜色溢出
- 视觉上形成:蓝 → 紫 → 红 → 橙 → 黄 → 绿 → 蓝 ... 的循环
✅ 3. gsap.utils.interpolate(start, end)
🔍 作用:
创建一个插值函数,根据进度值(0~1)计算
start到end之间的中间值。
📌 语法:
js
const interpolator = gsap.utils.interpolate(start, end);
const value = interpolator(progress); // progress ∈ [0, 1]
🌰 例子:
js
const mix = gsap.utils.interpolate(10, 50);
mix(0) // → 10
mix(0.5) // → 30
mix(1) // → 50
它不仅支持数字,还支持颜色、数组、甚至对象!
js
const scale = mix(0.7, 1.4); // ❌ 这里有误!
正确用法应该是:
js
// 先创建插值函数
const scaleInterp = gsap.utils.interpolate(0.7, 1.4);
// 再用 0~1 之间的值去插值(比如用 Math.random())
const scale = scaleInterp(Math.random());
这会导致 scale = 0.7(因为 mix(0.7) ≈ 0.7,第二个参数被忽略)。
✅ 修复建议:
js
// 方案 1:直接随机 scale
const scale = gsap.utils.random(0.7, 1.4, 0.01)();
// 方案 2:用插值 + 随机进度
const getScale = gsap.utils.interpolate(0.7, 1.4);
const scale = getScale(Math.random());
🌟 总结对比:
| 工具 | 用途 | 返回值 | 典型场景 |
|---|---|---|---|
random(min, max, step) |
生成随机数 | 函数(或直接数值) | 随机位置、延迟、颜色 |
wrap(min, max) |
循环限制数值 | 函数 | 色相循环、角度归一化、无限滚动 |
interpolate(a, b) |
计算 a→b 的中间值 | 函数 | 动态缩放、颜色混合、进度映射 |
🎯 高级技巧(Bonus):
支持颜色插值:
js
const colorMix = gsap.utils.interpolate("red", "blue");
colorMix(0.5); // → "rgb(128, 0, 128)"(紫色)
数组插值:
js
const pointMix = gsap.utils.interpolate([0, 0], [100, 200]);
pointMix(0.5); // → [50, 100]
📚 官方文档:
👉 greensock.com/docs/v3/GSA...
✅ 最终总结:
gsap.utils.random、wrap和interpolate是 GSAP 提供的"动画数学工具箱"------它们以极简 API 解决了随机性、循环性和连续性三大常见需求,让你无需手写复杂公式,即可构建丰富、动态、可控的交互动效。
什么是timeScale / yoyoEase
js
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>GSAP 案例 46 - timeScale / yoyoEase</title>
<style>
body {
margin: 0;
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
font-family: "Segoe UI", sans-serif;
background: #0f172a;
color: #e2e8f0;
}
.card {
width: 620px;
padding: 28px;
border-radius: 20px;
background: #111827;
box-shadow: 0 20px 50px rgba(15, 23, 42, 0.5);
}
.track {
height: 90px;
border-radius: 14px;
background: #0b1220;
position: relative;
margin: 18px 0;
}
.ball {
width: 46px;
height: 46px;
border-radius: 50%;
background: #f472b6;
position: absolute;
top: 22px;
left: 20px;
}
.controls {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 10px;
}
button {
padding: 10px 12px;
border: none;
border-radius: 12px;
font-size: 13px;
cursor: pointer;
background: #1f2937;
color: #e5e7eb;
}
button.primary {
background: #f472b6;
color: #0f172a;
font-weight: 600;
}
</style>
</head>
<body>
<div class="card">
<h1>案例 46:timeScale 与 yoyoEase</h1>
<p>调整播放速度,并在往返时使用不同缓动。</p>
<div class="track">
<div class="ball" id="ball"></div>
</div>
<div class="controls">
<button id="slow">慢速</button>
<button class="primary" id="play">播放</button>
<button id="fast">快速</button>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.1/dist/gsap.min.js"></script>
<script>
const ball = document.querySelector("#ball");
const playButton = document.querySelector("#play");
const slowButton = document.querySelector("#slow");
const fastButton = document.querySelector("#fast");
// yoyoEase 可以在回放时使用不同缓动
const tween = gsap.to(ball, {
x: 520,
duration: 1.6,
ease: "power2.out",
yoyo: true,
repeat: -1,
yoyoEase: "power2.in",
paused: true
});
playButton.addEventListener("click", () => {
tween.paused(!tween.paused());
});
slowButton.addEventListener("click", () => {
tween.timeScale(0.6);
});
fastButton.addEventListener("click", () => {
tween.timeScale(1.6);
});
</script>
</body>
</html>
在 GSAP(GreenSock Animation Platform) 中,timeScale 和 yoyoEase 是两个用于精细控制动画播放行为的强大特性:
✅ 一、timeScale:控制动画播放速度
🔍 作用:
调整动画的时间流速,实现快放、慢放、甚至倒放,而不改变
duration。
📌 基本用法:
js
tween.timeScale(1); // 正常速度(默认)
tween.timeScale(0.5); // 半速(慢动作)
tween.timeScale(2); // 2倍速(快进)
tween.timeScale(-1); // 反向播放(倒放)
timeScale是一个乘数因子 :1= 100% 速度0.6= 60% 速度(变慢)1.6= 160% 速度(变快)
js
slowButton.addEventListener("click", () => {
tween.timeScale(0.6); // 慢速
});
fastButton.addEventListener("click", () => {
tween.timeScale(1.6); // 快速
});
- 点击按钮即可实时改变动画速度,无需重新创建动画;
- 即使动画正在播放,也能无缝变速。
⚠️ 注意:
timeScale不影响duration的设定值,只影响实际播放耗时。
✅ 二、yoyoEase:为往返动画(yoyo)指定不同的缓动函数
🔍 背景知识:什么是 yoyo?
- 当设置
repeat: -1(无限重复) +yoyo: true时, - 动画会正向播放 → 反向播放 → 正向播放 → ...,形成"来回"效果。
📌 默认问题:
- 如果只设
ease: "power2.out", - 那么正向和反向都使用同一个缓动 ,导致:
- 正向:先快后慢(out)
- 反向:先慢后快(因为是倒放 out)
但很多时候,我们希望去程和回程有不同的运动感觉!
✅ 解决方案:yoyoEase
yoyoEase允许你为"回程"(反向播放)单独指定一个缓动函数。
💡 在你的代码中:
js
const tween = gsap.to(ball, {
x: 520,
duration: 1.6,
ease: "power2.out", // 去程:先快后慢(弹出感)
yoyo: true,
repeat: -1,
yoyoEase: "power2.in", // 回程:先慢后快(吸入感)
paused: true
});
🎯 视觉效果对比:
| 阶段 | 缓动 | 运动特点 |
|---|---|---|
| 去程(→) | power2.out |
快速冲出去,然后缓缓停下 |
| 回程(←) | power2.in |
缓缓启动,然后快速收回 |
这比单纯用
yoyo: true+ 单一缓动更自然、更有"弹性"!
🔬 技术细节补充:
1. yoyoEase 的工作原理:
- GSAP 不会倒放
ease,而是正向播放yoyoEase来模拟回程。 - 所以
ease: "out"+yoyoEase: "in"= 去程快停 + 回程快启,非常合理。
2. yoyoEase 支持所有缓动类型:
js
yoyoEase: "elastic.out"
yoyoEase: "bounce.inOut"
yoyoEase: CustomEase.create(...)
3. timeScale 是可叠加的:
js
tween.timeScale(2).timeScale(0.5); // 最终 = 1(2 * 0.5)
🌟 典型应用场景:
| 场景 | 用途 |
|---|---|
| UI 微交互动效 | 按钮点击"弹跳":去程快,回程缓 |
| 游戏对象移动 | 敌人巡逻:匀速去,加速回 |
| 视频/音频播放器 | 拖拽预览时慢放,正常播放时快放 |
| 科学可视化 | 模拟不同速度下的物理过程 |
⚠️ 注意事项:
yoyoEase仅在yoyo: true时生效;timeScale影响整个时间线或 tween,包括子动画;- 两者都是 GSAP 核心功能,无需额外插件;
timeScale(0)会暂停动画(等价于paused(true))。
📚 官方文档:
timeScale: greensock.com/docs/v3/GSA...yoyoEase: greensock.com/docs/v3/GSA...
✅ 总结:
timeScale让你像"调速旋钮"一样控制动画节奏,而yoyoEase则赋予往返动画"去程与回程不同性格"的能力------两者结合,可构建出既灵活又富有表现力的交互动效,是 GSAP 高级动画控制的标志性特性。