Ponpon Mania:WebGL和GSAP如何让漫画绵羊的梦想成真
本文带你深入了解Ponpon Mania的背后制作过程,探索插画、动画、WebGL和GSAP如何共同创造互动漫画体验。
项目概述
Ponpon Mania是一部动画漫画,主角Ponpon是一只梦想成为DJ的自大绵羊。我们希望通过结合趣味交互、GSAP驱动的流畅动画和动态视觉效果,探索超越传统漫画的叙事方式。目标是创造一部充满生机的漫画,让读者在跟随叙事的同时直接与Ponpon的世界互动。
团队介绍
我们是Justine Soulié(艺术总监和插画师)和Patrick Heng(创意开发者),一个热衷于通过视觉和交互讲故事的创意二人组。Justine擅长插画、艺术指导和设计,而Patrick专注于创意开发和互动体验。
艺术指导
我们的视觉方向强调简洁的布局、大胆的色彩和有趣的细节。从一开始,我们就希望漫画既充满活力又平易近人,同时通过设计支持故事叙述。
技术方案
技术栈选择
我们使用WebGL是因为它给了我们完全的渲染创意自由。尽管漫画主要是2D外观,但我们希望灵活添加深度和基于着色器的效果。
从Justine的Illustrator文件开始,每个面板的每个图层和视觉元素都作为单独图像导出。这些资源使用Free TexturePacker打包成优化的纹理图集。
导出后,图像进一步压缩为GPU友好格式以减少内存使用。使用打包器生成的数据,我们通过生成正确尺寸的平面在WebGL中重建每个场景。最后,所有内容都放置在3D场景中,应用必要的着色器和动画以实现所需的视觉效果。
动画实现
GSAP使得使用统一语法动画DOM元素和WebGL对象变得容易。其时间线系统为复杂序列带来结构,而将其与ScrollTrigger结合则简化了基于滚动的动画。我们还使用SplitText处理文本动画。
具体实现示例
主页动画
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
// 云朵排斥效果的简单实现
const dx = baseX - mouse.x;
const dy = baseY - mouse.y;
const dist = Math.sqrt(dx * dx + dy * dy);
// 如果鼠标靠近则排斥云朵
const radius = 2; // 交互半径
const strength = 1.5; // 排斥力
const repulsion = Math.max(0, 1 - dist / radius) * strength;
// 应用带有平滑弹簧运动的排斥力
const targetX = basePosX + dx * repulsion;
const targetY = basePosY - Math.abs(dy * repulsion) / 2;
velocity.x += (targetX - position.x) * springStrength * deltaTime;
velocity.y += (targetY - position.y) * springStrength * deltaTime;
position.x += velocity.x;
position.y += velocity.y;
|
面板动画
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
|
// 使用GSAP时间线按顺序动画ponpons
const timeline = gsap.timeline({ repeat: -1, repeatDelay: 0.7 });
const uFlash = { value: 0 };
const flashTimeline = gsap.timeline({ paused: true });
function togglePonponGroup(index) {
ponponsGroups.forEach((g, i) => (g.mesh.visible = i === index));
}
function triggerFlash() {
const flashes = Math.floor(Math.random() * 2) + 1; // 1-2次闪光
const duration = 0.4 / flashes;
flashTimeline.clear();
for (let i = 0; i < flashes; i++) {
flashTimeline
.set(uFlash, { value: 0.6 }, i * duration) // 明亮闪光
.to(uFlash, { value: 0, duration: duration * 0.9 }, i * duration + duration * 0.1); // 淡出
}
flashTimeline.play();
}
ponponMeshes.forEach((ponpon, i) => {
timeline.fromTo(
ponpon.position,
{ y: ponpon.initialY - 0.2 }, // 从稍低位置开始
{
y: ponpon.initialY, // 弹跳起来
duration: 1,
ease: "elastic.out",
onStart: () => {
togglePonponGroup(i); // 显示活动组
triggerFlash(); // 触发闪光
}
},
i * 1.6 // ponpons之间的交错延迟
);
});
|
关于页面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
const sectionUniform = { progress: { value: 0 } };
// 为一个部分创建ScrollTrigger
const sectionTrigger = ScrollTrigger.create({
trigger: ".about-section",
start: "top bottom",
end: "bottom top",
onUpdate: (self) => {
sectionUniform.progress.value = self.progress; // 更新uniform
}
});
// 使用触发值每帧更新场景
function updateScene() {
const progress = sectionTrigger.progress;
const velocity = sectionTrigger.getVelocity();
// 使用滚动进度驱动相机移动
camera.position.y = map(progress, 0.75, 1, -0.4, 3.4);
camera.position.z =
5 + map(progress, 0, 0.3, -4, 0) +
map(progress, 0.75, 1, 0, 2) + velocity * 0.01;
// 对ponpon和相机的微妙速度反馈
ponpon.position.y = ponpon.initialY + velocity * 0.01;
}
|
技术工具栈
设计工具
- Adobe Photoshop & Illustrator - 插画和资源准备
- Figma - 布局和界面设计
开发工具
- ogl - WebGL渲染框架
- Nuxt.js - 前端框架,用于结构和路由
- GSAP - 动画库,用于流畅精确的运动
- Matter.js - 物理引擎,用于关于页面
- Free TexturePacker - 从导出资源创建优化纹理图集
- Tweakpane - GUI工具,用于实时调试和微调参数
页面过渡效果
对于页面过渡,我们希望它们为体验增添趣味感,同时保持导航的迅捷和流畅。每个过渡都设计成符合页面氛围,而不是使用单一的通用效果,我们构建了变体以保持旅程的新鲜感。
技术上,过渡使用自定义着色器将两个WebGL场景混合在一起,其中前一页和下一页实时渲染和混合。混合动画由GSAP补间驱动,这让我们能够精确控制着色器的时序和进度,实现流畅、响应迅速的过渡。
未来展望
Ponpon Mania推动我们超越传统叙事思维。我们很高兴能够致力于为漫画增添趣味和活力的叙事和微交互。
展望未来,我们计划创建新章节,扩展Ponpon的故事,并在我们构建的宇宙中引入小游戏和互动体验。我们期待继续探索Ponpon的世界,并与读者分享更多惊喜。