前面幾天一直在學 GSAP 的不同功能,但如果每次都只是 gsap.to() 然後看一個方塊移動,好像還是有點無聊,所以今天直接來做一個完整的小作品。這次會把前面學過的所有功能以及動畫效果全部放進同一個網頁裡。
這裡我們要製作一個簡易網頁,在進入網頁的同時文字會滑動進入畫面,且我製作了一個按鈕,點擊即可觸發事件,會有一個小視窗跳出來,接著在往下滑的同時,動畫跟文字都會依序出現
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JavaScript 從入門到進階:結合 Three.js × GSAP 打造互動網頁</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<section class="hero">
<div class="hero-content">
<p class="tag">MY JAVASCRIPT WORLD</p>
<h1>
開始我的<br>
<span>JavaScript 冒險</span>
</h1>
<p class="description">
從 JavaScript 到 GSAP,
一步一步打造屬於自己的互動網頁。
</p>
<button class="start-btn" id="startBtn">
開始探索 ↓
</button>
</div>
</section>
<div class="message" id="message">
歡迎來到我的 JavaScript 世界!
</div>
<section class="skills">
<div class="section-title">
<p>WHAT I LEARNED</p>
<h2>我的技能工具箱</h2>
</div>
<div class="skill-container">
<div class="skill-card">
<div class="number">01</div>
<h3>JavaScript</h3>
<p>
用 JavaScript 讓網頁開始具有互動性,
也開始理解 DOM、Event 和資料操作。
</p>
</div>
<div class="skill-card">
<div class="number">02</div>
<h3>GSAP</h3>
<p>
使用 GSAP 控制網頁動畫,
讓原本靜止的元素開始動起來。
</p>
</div>
<div class="skill-card">
<div class="number">03</div>
<h3>ScrollTrigger</h3>
<p>
讓使用者的滾動也可以成為動畫的一部分,
讓網頁開始有互動感。
</p>
</div>
</div>
</section>
<section class="ending">
<div class="ending-content">
<p class="ending-tag">NEXT STEP</p>
<h2>
下一站
<span>Three.js</span>
</h2>
<p>
如果 2D 動畫已經這麼有趣,
那麼 3D 世界又會變成什麼樣子?
</p>
<button class="next-btn">
Enter the 3D World →
</button>
</div>
</section>
<script src="index.js" type="module"></script>
</body>
</html>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
font-family: Arial, sans-serif;
background: #111;
color: white;
}
.hero {
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
background: #111;
}
.hero-content {
max-width: 800px;
}
.tag,
.ending-tag {
font-size: 14px;
letter-spacing: 4px;
margin-bottom: 25px;
}
.hero h1 {
font-size: 72px;
line-height: 1.1;
margin-bottom: 30px;
}
.hero h1 span {
color: #8b5cf6;
}
.description {
font-size: 20px;
line-height: 1.8;
color: #aaa;
margin-bottom: 40px;
}
button {
border: none;
padding: 16px 32px;
border-radius: 50px;
font-size: 16px;
cursor: pointer;
}
.start-btn {
background: #8b5cf6;
color: white;
}
.next-btn {
background: #111;
color: white;
}
.message {
position: fixed;
top: 30px;
left: 50%;
transform: translateX(-50%);
padding: 15px 30px;
border-radius: 50px;
background: white;
color: #111;
opacity: 0;
pointer-events: none;
z-index: 100;
}
.skills {
min-height: 100vh;
padding: 150px 10%;
background: #181818;
}
.section-title {
text-align: center;
margin-bottom: 80px;
}
.section-title p {
color: #8b5cf6;
letter-spacing: 4px;
margin-bottom: 15px;
}
.section-title h2 {
font-size: 48px;
}
.skill-container {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 30px;
}
.skill-card {
padding: 40px;
min-height: 300px;
border-radius: 20px;
background: #222;
border: 1px solid #333;
}
.number {
color: #8b5cf6;
font-size: 18px;
margin-bottom: 50px;
}
.skill-card h3 {
font-size: 30px;
margin-bottom: 20px;
}
.skill-card p {
color: #aaa;
line-height: 1.8;
}
.ending {
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
background: #8b5cf6;
}
.ending-content {
max-width: 700px;
}
.ending h2 {
font-size: 64px;
margin-bottom: 30px;
}
.ending h2 span {
color: #111;
}
.ending-content > p {
font-size: 20px;
line-height: 1.8;
margin-bottom: 40px;
}
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";
gsap.registerPlugin(ScrollTrigger);
const heroTimeline = gsap.timeline();
heroTimeline
.from(".tag", {
y: 30,
opacity: 0,
duration: 0.6,
ease: "power2.out"
})
.from(".hero h1", {
y: 80,
opacity: 0,
duration: 1,
ease: "power3.out"
})
.from(".description", {
y: 30,
opacity: 0,
duration: 0.7,
ease: "power2.out"
})
.from(".start-btn", {
y: 20,
opacity: 0,
scale: 0.8,
duration: 0.6,
ease: "back.out(1.7)"
});
const startBtn = document.querySelector("#startBtn");
const message = document.querySelector("#message");
startBtn.addEventListener("click", function() {
gsap.to(message, {
y: 20,
opacity: 1,
duration: 0.5,
ease: "power2.out" //觸發事件,跳出視窗
});
gsap.to(message, {
y: 0,
opacity: 0,
duration: 0.5,
delay: 2,
ease: "power2.in"
});
});
gsap.from(".section-title", {
y: 60,
opacity: 0,
duration: 1,
ease: "power3.out",
scrollTrigger: {
trigger: ".skills",
start: "top 70%"
}
});
gsap.from(".skill-card", {
y: 100,
opacity: 0,
duration: 0.8,
stagger: 0.2,
ease: "power3.out",
scrollTrigger: {
trigger: ".skill-container",
start: "top 75%"
}
});
const endingTimeline = gsap.timeline({
scrollTrigger: {
trigger: ".ending",
start: "top 70%"
}
});
endingTimeline
.from(".ending-tag", {
y: 30,
opacity: 0,
duration: 0.5
})
.from(".ending h2", {
y: 60,
opacity: 0,
duration: 0.8,
ease: "power3.out"
})
.from(".ending-content > p", {
y: 30,
opacity: 0,
duration: 0.6
})
.from(".next-btn", {
scale: 0.8,
opacity: 0,
duration: 0.6,
ease: "back.out(1.7)"
});

底下那一行字正在往上進入頁面,帶有點透明。
這裡有製作一個觸發事件的按鈕,點擊跳出小視窗
接著往下滑內容都會有依序的動畫進入頁面,因為設定了scrollTrigger
最後做一個結束畫面
做到這裡,可以回頭看一下,我們不是為了使用語法而使用語法,而是每個功能都有自己的工作。而且我們至今所學都有好好用上,是不是很好玩!!
| GSAP功能 | 在做什麼 |
|---|---|
| from() | 設定元素一開始的狀態 |
| timeline() | 控制多個動畫的順序 |
| stagger | 讓技能卡片依序出現 |
| ease | 讓動畫速度更自然 |
| ScrollTrigger | 讓動畫跟著使用者滾動觸發 |
做到這裡,有沒有真的感受到自己將一個網站做出來,然後搭配動畫效果進場,為網頁增添了樂趣,GSAP 不只是讓網頁「動起來」,而是可以開始控制整個網頁的節奏。
而下一站,我想玩的東西就更不一樣了。如果現在的網頁已經可以做出這些動畫,那能不能直接在網頁裡做一個 3D 世界?
下一篇,正式進入 Three.js。