Skip to content

Commit 150e176

Browse files
committed
feat[ptr]: GSAP ScrollTrigger 的 scrub 实现滚动条映射到动画进度
用户滚动 → ScrollTrigger 计算 [0, 1] 进度 → 驱动 GSAP Tween 更新 o.value (0 → 120) → onUpdate 回调调用 canvas.upData(frameIndex) → Canvas drawImage() 绘制对应帧 → 视觉上形成"滚动控制动画"
1 parent 937f098 commit 150e176

2 files changed

Lines changed: 125 additions & 0 deletions

File tree

_reference/NANFU/NANFU Group _ Leading Battery Manufacturer & Power Solutions Provider.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1498,6 +1498,7 @@
14981498
<section class="app MediaTs" data-vh="1" data-src="/templates/assets/home/media.ts"></section>
14991499
</div>
15001500
<div id="fn1" class="clip full">
1501+
<!-- 动画帧图片 -->
15011502
<canvas class="bannerCv active" data-apppath="/templates/assets/home/bannerFm_app/"
15021503
data-path="/templates/assets/home/bannerFm/" data-count="120" width="2750" height="1494"></canvas>
15031504
<!-- <div class="photo" style="background-image: url(/templates/assets/home/banner.jpg)"></div>-->

_reference/NANFU/README.md

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
## 核心实现原理
2+
3+
### 1. Canvas 图像序列播放器
4+
5+
代码中有两个自定义类来处理 Canvas 帧动画:
6+
- `Sr` 类 → 控制 `.bannerCv`(120 帧)
7+
- `Tr` 类 → 控制 `.batteryCv`(150 帧)
8+
9+
它们都暴露了一个 `upData(frameIndex)` 方法,传入帧编号就会绘制对应帧。
10+
11+
---
12+
13+
### 2. GSAP ScrollTrigger + `scrub` — 滚轮驱动动画的核心
14+
15+
**Banner 区域(120帧):**
16+
```javascript
17+
var a = new Sr({ el: ".bannerCv" }); // 创建banner画布播放器
18+
var o = { value: 0 }; // 虚拟帧计数器
19+
20+
gsap.timeline({
21+
scrollTrigger: {
22+
trigger: n.banner,
23+
start: "-=0",
24+
end: "+=" + t.winsize.vh, // 滚动一个屏幕高度
25+
scrub: 0.9 // ← 关键!将滚动条进度绑定到动画播放头
26+
},
27+
onUpdate: function() {
28+
a.upData(parseInt(o.value)); // 每帧更新时,通知画布绘制新帧
29+
}
30+
})
31+
.fromTo(o, { value: 0 }, { value: 120, ease: "none" }); // 将帧号从0变到120
32+
```
33+
34+
**Battery 区域(150帧):**
35+
```javascript
36+
var A = new Tr({ el: ".batteryCv" });
37+
var f = { value: 0 };
38+
39+
gsap.timeline({
40+
scrollTrigger: {
41+
trigger: n.process,
42+
start: "-=" + t.winsize.vh,
43+
end: "+=" + t.winsize.vh * (c + 1),
44+
scrub: 0.8 // ← 同样用scrub绑定滚动
45+
},
46+
onUpdate: function() { A.upData(parseInt(f.value)); }
47+
})
48+
.fromTo(f, { value: 0 }, { value: 150 * c, ease: "none" });
49+
```
50+
51+
---
52+
53+
### 3. `scrub` 属性
54+
55+
`scrub` 是 GSAP ScrollTrigger 的核心配置:
56+
57+
```javascript
58+
gsap.timeline({
59+
scrollTrigger: {
60+
trigger: banner,
61+
scrub: 0.9 // 这一个属性完成了全部"绑定"工作
62+
}
63+
}).fromTo(frameObj, { value: 0 }, { value: 120 })
64+
```
65+
66+
|| 效果 |
67+
|---|---|
68+
| `true` | 滚动条直接映射到动画进度,无缓动 |
69+
| `0.9` | 有 0.9 秒的平滑缓动,使画面过渡更流畅 |
70+
71+
**数据流向:**
72+
```
73+
用户滚动
74+
→ ScrollTrigger 计算 [0, 1] 进度
75+
→ 驱动 GSAP Tween 更新 o.value (0 → 120)
76+
→ onUpdate 回调调用 canvas.upData(frameIndex)
77+
→ Canvas drawImage() 绘制对应帧
78+
→ 视觉上形成"滚动控制动画"
79+
```
80+
81+
---
82+
83+
### 4. 其他元素的视差效果
84+
85+
非 Canvas 元素(文字、图片)使用同样的 `scrub` 机制实现视差:
86+
```javascript
87+
gsap.timeline({
88+
scrollTrigger: {
89+
trigger: e,
90+
start: "-=" + t.winsize.vh,
91+
end: "+=" + (height + t.winsize.vh),
92+
scrub: 0.9 // 同样是scrub
93+
}
94+
})
95+
.fromTo(e, { x: -n, y: -r }, { x: n, y: r }); // 上下/左右视差位移
96+
```
97+
98+
---
99+
100+
**总结:** 整个效果的技术本质是 **GSAP ScrollTrigger 的 `scrub` 属性**,它把滚动条的位置实时映射为动画的播放进度,配合 Canvas 逐帧绘制图像序列
101+
```html
102+
<!-- 动画帧图片来源 -->
103+
<canvas class="bannerCv"
104+
data-path="/templates/assets/home/bannerFm/"
105+
data-count="120">
106+
</canvas>
107+
```
108+
到时候JS运行时读取
109+
```javascript
110+
for (let i = 0; i < 120; i++) {
111+
const img = new Image();
112+
img.src = `/templates/assets/home/bannerFm/${pad(i)}.jpg`;
113+
}
114+
```
115+
116+
117+
手写实现类似效果
118+
```javascript
119+
window.addEventListener('scroll', () => {
120+
const progress = (scrollY - sectionTop) / sectionHeight; // 手算进度
121+
const frame = Math.round(progress * 120);
122+
canvas.drawImage(images[frame], 0, 0);
123+
});
124+
```

0 commit comments

Comments
 (0)