mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-11-05 05:37:09 +00:00
chore: remove remaining test project data
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
committed by
Vance Ingalls
co-authored by
Claude Opus 4.6
parent
6b3c1410b1
commit
bcdb14cd90
File diff suppressed because it is too large
Load Diff
@@ -1,988 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=1920, height=1080">
|
||||
<title>APEX Fitness Tracker - Rise Above</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { background: #000; display: flex; justify-content: center; align-items: center; min-height: 100vh; }
|
||||
|
||||
.composition {
|
||||
background: #000;
|
||||
font-family: 'Helvetica Neue', 'Arial', sans-serif;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* ── Phase 1: Product Beauty Shots ── */
|
||||
.product-stage {
|
||||
position: absolute; top: 0; left: 0; width: 100%; height: 100%;
|
||||
display: flex; align-items: center; justify-content: center;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.product-glow {
|
||||
position: absolute; width: 500px; height: 500px;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle, rgba(212,175,55,0.25) 0%, rgba(212,175,55,0) 70%);
|
||||
top: 50%; left: 50%; transform: translate(-50%, -50%);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.product-body {
|
||||
position: relative; width: 260px; height: 340px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.watch-case {
|
||||
position: absolute; top: 40px; left: 30px;
|
||||
width: 200px; height: 240px;
|
||||
border-radius: 50px;
|
||||
background: linear-gradient(145deg, #1a1a1a 0%, #0d0d0d 50%, #1a1a1a 100%);
|
||||
border: 2px solid rgba(212,175,55,0.6);
|
||||
box-shadow: 0 0 40px rgba(212,175,55,0.15), inset 0 0 30px rgba(0,0,0,0.8);
|
||||
}
|
||||
|
||||
.watch-screen {
|
||||
position: absolute; top: 65px; left: 55px;
|
||||
width: 150px; height: 190px;
|
||||
border-radius: 35px;
|
||||
background: linear-gradient(180deg, #0a0a0a 0%, #111 100%);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.screen-content {
|
||||
position: absolute; top: 20px; left: 15px; width: 120px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.screen-time {
|
||||
font-size: 42px; font-weight: 200; letter-spacing: 2px;
|
||||
color: #D4AF37; opacity: 0;
|
||||
}
|
||||
|
||||
.screen-hr {
|
||||
font-size: 14px; color: rgba(212,175,55,0.7); margin-top: 8px;
|
||||
letter-spacing: 1px; opacity: 0;
|
||||
}
|
||||
|
||||
.screen-hr-value {
|
||||
font-size: 28px; font-weight: 300; color: #D4AF37; opacity: 0;
|
||||
}
|
||||
|
||||
.watch-crown {
|
||||
position: absolute; top: 130px; right: 20px;
|
||||
width: 12px; height: 35px;
|
||||
border-radius: 6px;
|
||||
background: linear-gradient(180deg, #D4AF37, #8B7225, #D4AF37);
|
||||
box-shadow: 2px 0 8px rgba(212,175,55,0.3);
|
||||
}
|
||||
|
||||
.watch-band-top, .watch-band-bottom {
|
||||
position: absolute; left: 70px; width: 120px; height: 60px;
|
||||
background: linear-gradient(180deg, #1a1a1a, #111);
|
||||
border: 1px solid rgba(212,175,55,0.15);
|
||||
}
|
||||
|
||||
.watch-band-top {
|
||||
top: -15px; border-radius: 10px 10px 0 0;
|
||||
}
|
||||
|
||||
.watch-band-bottom {
|
||||
bottom: -15px; border-radius: 0 0 10px 10px;
|
||||
}
|
||||
|
||||
.product-name-reveal {
|
||||
position: absolute; top: 480px; left: 50%; transform: translateX(-50%);
|
||||
font-size: 72px; font-weight: 100; letter-spacing: 30px;
|
||||
color: #D4AF37; opacity: 0;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.product-tagline-small {
|
||||
position: absolute; top: 560px; left: 50%; transform: translateX(-50%);
|
||||
font-size: 16px; font-weight: 300; letter-spacing: 8px;
|
||||
color: rgba(255,255,255,0.5); opacity: 0;
|
||||
text-transform: uppercase;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* Gold accent lines */
|
||||
.gold-line {
|
||||
position: absolute; height: 1px;
|
||||
background: linear-gradient(90deg, transparent, #D4AF37, transparent);
|
||||
opacity: 0;
|
||||
}
|
||||
.gold-line-1 { top: 200px; left: 0; width: 100%; }
|
||||
.gold-line-2 { bottom: 200px; left: 0; width: 100%; }
|
||||
.gold-line-left { top: 0; left: 300px; width: 1px; height: 100%;
|
||||
background: linear-gradient(180deg, transparent, #D4AF37, transparent); }
|
||||
.gold-line-right { top: 0; right: 300px; width: 1px; height: 100%;
|
||||
background: linear-gradient(180deg, transparent, #D4AF37, transparent); }
|
||||
|
||||
/* ── Phase 2: Spec Highlights ── */
|
||||
.spec-panel {
|
||||
position: absolute; opacity: 0;
|
||||
padding: 20px 30px;
|
||||
border-left: 2px solid #D4AF37;
|
||||
}
|
||||
|
||||
.spec-label {
|
||||
font-size: 11px; font-weight: 400; letter-spacing: 4px;
|
||||
color: rgba(212,175,55,0.6); text-transform: uppercase;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.spec-value {
|
||||
font-size: 36px; font-weight: 200; color: #fff;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
.spec-1 { top: 300px; left: 160px; }
|
||||
.spec-2 { top: 300px; right: 160px; text-align: right; border-left: none; border-right: 2px solid #D4AF37; }
|
||||
.spec-3 { top: 500px; left: 160px; }
|
||||
.spec-4 { top: 500px; right: 160px; text-align: right; border-left: none; border-right: 2px solid #D4AF37; }
|
||||
|
||||
/* ── Phase 3: Athletes in Motion ── */
|
||||
.athlete-stage {
|
||||
position: absolute; top: 0; left: 0; width: 100%; height: 100%;
|
||||
opacity: 0; overflow: hidden;
|
||||
}
|
||||
|
||||
.athlete-panel {
|
||||
position: absolute; top: 0; height: 100%;
|
||||
overflow: hidden; opacity: 0;
|
||||
}
|
||||
|
||||
.athlete-panel-1 { left: 0; width: 33.33%; }
|
||||
.athlete-panel-2 { left: 33.33%; width: 33.34%; }
|
||||
.athlete-panel-3 { left: 66.67%; width: 33.33%; }
|
||||
|
||||
.athlete-silhouette {
|
||||
position: absolute; bottom: 0; width: 100%; height: 85%;
|
||||
display: flex; align-items: flex-end; justify-content: center;
|
||||
}
|
||||
|
||||
/* Runner silhouette */
|
||||
.runner-figure {
|
||||
position: relative; width: 200px; height: 400px;
|
||||
}
|
||||
|
||||
.runner-torso {
|
||||
position: absolute; top: 40px; left: 80px;
|
||||
width: 40px; height: 120px;
|
||||
background: linear-gradient(180deg, #D4AF37, #8B7225);
|
||||
border-radius: 20px;
|
||||
transform: rotate(-15deg);
|
||||
}
|
||||
|
||||
.runner-head {
|
||||
position: absolute; top: 0; left: 82px;
|
||||
width: 36px; height: 36px;
|
||||
background: #D4AF37; border-radius: 50%;
|
||||
}
|
||||
|
||||
.runner-leg-front {
|
||||
position: absolute; top: 140px; left: 60px;
|
||||
width: 20px; height: 160px;
|
||||
background: linear-gradient(180deg, #B8962E, #8B7225);
|
||||
border-radius: 10px;
|
||||
transform: rotate(25deg); transform-origin: top center;
|
||||
}
|
||||
|
||||
.runner-leg-back {
|
||||
position: absolute; top: 140px; left: 110px;
|
||||
width: 20px; height: 160px;
|
||||
background: linear-gradient(180deg, #8B7225, #5C4C19);
|
||||
border-radius: 10px;
|
||||
transform: rotate(-30deg); transform-origin: top center;
|
||||
}
|
||||
|
||||
.runner-arm-front {
|
||||
position: absolute; top: 50px; left: 50px;
|
||||
width: 16px; height: 110px;
|
||||
background: linear-gradient(180deg, #D4AF37, #8B7225);
|
||||
border-radius: 8px;
|
||||
transform: rotate(-35deg); transform-origin: top center;
|
||||
}
|
||||
|
||||
.runner-arm-back {
|
||||
position: absolute; top: 50px; left: 120px;
|
||||
width: 16px; height: 110px;
|
||||
background: linear-gradient(180deg, #8B7225, #5C4C19);
|
||||
border-radius: 8px;
|
||||
transform: rotate(25deg); transform-origin: top center;
|
||||
}
|
||||
|
||||
/* Cyclist silhouette */
|
||||
.cyclist-figure {
|
||||
position: relative; width: 280px; height: 300px;
|
||||
}
|
||||
|
||||
.bike-wheel-front, .bike-wheel-back {
|
||||
position: absolute; bottom: 0;
|
||||
width: 120px; height: 120px;
|
||||
border: 3px solid #D4AF37; border-radius: 50%;
|
||||
}
|
||||
.bike-wheel-back { left: 0; }
|
||||
.bike-wheel-front { right: 0; }
|
||||
|
||||
.bike-frame {
|
||||
position: absolute; bottom: 40px; left: 55px;
|
||||
width: 170px; height: 100px;
|
||||
}
|
||||
|
||||
.bike-frame-line {
|
||||
position: absolute; background: #B8962E; border-radius: 2px;
|
||||
}
|
||||
.frame-top { top: 0; left: 0; width: 170px; height: 4px; transform: rotate(-10deg); }
|
||||
.frame-down { top: 0; left: 0; width: 4px; height: 100px; transform: rotate(15deg); }
|
||||
.frame-seat { top: 0; right: 20px; width: 4px; height: 100px; transform: rotate(-5deg); }
|
||||
|
||||
.cyclist-body {
|
||||
position: absolute; bottom: 140px; left: 80px;
|
||||
width: 30px; height: 90px;
|
||||
background: linear-gradient(180deg, #D4AF37, #8B7225);
|
||||
border-radius: 15px;
|
||||
transform: rotate(-25deg);
|
||||
}
|
||||
|
||||
.cyclist-head {
|
||||
position: absolute; bottom: 225px; left: 65px;
|
||||
width: 30px; height: 30px;
|
||||
background: #D4AF37; border-radius: 50%;
|
||||
}
|
||||
|
||||
/* Lifter silhouette */
|
||||
.lifter-figure {
|
||||
position: relative; width: 260px; height: 380px;
|
||||
}
|
||||
|
||||
.lifter-torso {
|
||||
position: absolute; top: 50px; left: 90px;
|
||||
width: 80px; height: 140px;
|
||||
background: linear-gradient(180deg, #D4AF37, #8B7225);
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.lifter-head {
|
||||
position: absolute; top: 5px; left: 110px;
|
||||
width: 40px; height: 40px;
|
||||
background: #D4AF37; border-radius: 50%;
|
||||
}
|
||||
|
||||
.barbell {
|
||||
position: absolute; top: 30px; left: 10px;
|
||||
width: 240px; height: 6px;
|
||||
background: linear-gradient(90deg, #888, #D4AF37, #888);
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.barbell-plate-left, .barbell-plate-right {
|
||||
position: absolute; top: 15px;
|
||||
width: 14px; height: 50px;
|
||||
background: #D4AF37; border-radius: 4px;
|
||||
}
|
||||
.barbell-plate-left { left: 5px; }
|
||||
.barbell-plate-right { right: 5px; }
|
||||
|
||||
.lifter-leg-left, .lifter-leg-right {
|
||||
position: absolute; top: 180px; width: 30px; height: 150px;
|
||||
background: linear-gradient(180deg, #B8962E, #8B7225);
|
||||
border-radius: 15px;
|
||||
}
|
||||
.lifter-leg-left { left: 90px; }
|
||||
.lifter-leg-right { left: 140px; }
|
||||
|
||||
/* Panel dividers */
|
||||
.panel-divider {
|
||||
position: absolute; top: 0; width: 2px; height: 100%;
|
||||
background: linear-gradient(180deg, transparent, #D4AF37, transparent);
|
||||
opacity: 0; z-index: 10;
|
||||
}
|
||||
.panel-divider-1 { left: 33.33%; }
|
||||
.panel-divider-2 { left: 66.67%; }
|
||||
|
||||
.athlete-label {
|
||||
position: absolute; bottom: 60px; left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 14px; font-weight: 300; letter-spacing: 6px;
|
||||
color: rgba(255,255,255,0.6); text-transform: uppercase;
|
||||
white-space: nowrap; opacity: 0;
|
||||
}
|
||||
|
||||
/* Motion lines behind athletes */
|
||||
.motion-line {
|
||||
position: absolute; height: 2px;
|
||||
background: linear-gradient(90deg, transparent, rgba(212,175,55,0.4), transparent);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* ── Phase 4: Data Visualization / Pulse ── */
|
||||
.data-stage {
|
||||
position: absolute; top: 0; left: 0; width: 100%; height: 100%;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.pulse-ring {
|
||||
position: absolute; top: 50%; left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
border: 1px solid rgba(212,175,55,0.3);
|
||||
border-radius: 50%;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.pulse-ring-1 { width: 200px; height: 200px; }
|
||||
.pulse-ring-2 { width: 400px; height: 400px; }
|
||||
.pulse-ring-3 { width: 600px; height: 600px; }
|
||||
.pulse-ring-4 { width: 800px; height: 800px; }
|
||||
.pulse-ring-5 { width: 1000px; height: 1000px; }
|
||||
|
||||
.heartbeat-line {
|
||||
position: absolute; top: 50%; left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 800px; height: 200px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.heartbeat-line svg { width: 100%; height: 100%; }
|
||||
.heartbeat-path { fill: none; stroke: #D4AF37; stroke-width: 2; }
|
||||
|
||||
.stat-counter {
|
||||
position: absolute; text-align: center; opacity: 0;
|
||||
}
|
||||
|
||||
.stat-number {
|
||||
font-size: 64px; font-weight: 100; color: #D4AF37;
|
||||
letter-spacing: 4px;
|
||||
}
|
||||
|
||||
.stat-unit {
|
||||
font-size: 14px; font-weight: 300; letter-spacing: 4px;
|
||||
color: rgba(255,255,255,0.5); text-transform: uppercase;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.stat-bpm { top: 350px; left: 50%; transform: translateX(-50%); }
|
||||
.stat-steps { top: 350px; left: 200px; }
|
||||
.stat-cals { top: 350px; right: 200px; }
|
||||
|
||||
/* ── Phase 5: Crescendo / Celebration ── */
|
||||
.crescendo-stage {
|
||||
position: absolute; top: 0; left: 0; width: 100%; height: 100%;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.victory-burst {
|
||||
position: absolute; top: 50%; left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 0; height: 0;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle, rgba(212,175,55,0.4) 0%, transparent 70%);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.gold-particle {
|
||||
position: absolute; width: 6px; height: 6px;
|
||||
background: #D4AF37; border-radius: 50%;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.confetti {
|
||||
position: absolute; width: 8px; height: 20px;
|
||||
border-radius: 2px; opacity: 0;
|
||||
}
|
||||
|
||||
/* Tagline */
|
||||
.tagline-container {
|
||||
position: absolute; top: 50%; left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
text-align: center; opacity: 0;
|
||||
}
|
||||
|
||||
.tagline-rise, .tagline-above {
|
||||
font-size: 120px; font-weight: 100; letter-spacing: 20px;
|
||||
text-transform: uppercase; line-height: 1.1;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.tagline-rise { color: #fff; }
|
||||
.tagline-above { color: #D4AF37; }
|
||||
|
||||
.tagline-bar {
|
||||
width: 0; height: 2px;
|
||||
background: #D4AF37;
|
||||
margin: 20px auto;
|
||||
}
|
||||
|
||||
/* ── Phase 6: Logo Lockup ── */
|
||||
.logo-stage {
|
||||
position: absolute; top: 0; left: 0; width: 100%; height: 100%;
|
||||
display: flex; flex-direction: column;
|
||||
align-items: center; justify-content: center;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.logo-apex {
|
||||
font-size: 140px; font-weight: 100; letter-spacing: 40px;
|
||||
color: #D4AF37; text-transform: uppercase;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.logo-divider {
|
||||
width: 0; height: 1px;
|
||||
background: linear-gradient(90deg, transparent, #D4AF37, transparent);
|
||||
margin: 25px 0;
|
||||
}
|
||||
|
||||
.logo-sub {
|
||||
font-size: 18px; font-weight: 300; letter-spacing: 12px;
|
||||
color: rgba(255,255,255,0.6); text-transform: uppercase;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.logo-tagline-final {
|
||||
font-size: 24px; font-weight: 200; letter-spacing: 10px;
|
||||
color: rgba(212,175,55,0.8); text-transform: uppercase;
|
||||
margin-top: 40px; opacity: 0;
|
||||
}
|
||||
|
||||
.corner-accent {
|
||||
position: absolute; width: 60px; height: 60px;
|
||||
border-color: rgba(212,175,55,0.3); border-style: solid;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.corner-tl { top: 40px; left: 40px; border-width: 1px 0 0 1px; }
|
||||
.corner-tr { top: 40px; right: 40px; border-width: 1px 1px 0 0; }
|
||||
.corner-bl { bottom: 40px; left: 40px; border-width: 0 0 1px 1px; }
|
||||
.corner-br { bottom: 40px; right: 40px; border-width: 0 1px 1px 0; }
|
||||
|
||||
/* ── Global overlays ── */
|
||||
.vignette {
|
||||
position: absolute; top: 0; left: 0; width: 100%; height: 100%;
|
||||
background: radial-gradient(ellipse at center, transparent 50%, rgba(0,0,0,0.6) 100%);
|
||||
pointer-events: none; z-index: 100;
|
||||
}
|
||||
|
||||
.scan-line {
|
||||
position: absolute; left: 0; width: 100%; height: 2px;
|
||||
background: linear-gradient(90deg, transparent, rgba(212,175,55,0.15), transparent);
|
||||
opacity: 0; z-index: 50;
|
||||
top: -2px;
|
||||
}
|
||||
|
||||
.flash-overlay {
|
||||
position: absolute; top: 0; left: 0; width: 100%; height: 100%;
|
||||
background: #D4AF37; opacity: 0; z-index: 90;
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="composition" data-composition-id="gradual-escalation-apex" style="width: 1920px; height: 1080px; position: relative; overflow: hidden;">
|
||||
|
||||
<!-- Vignette overlay -->
|
||||
<div class="vignette"></div>
|
||||
<div class="scan-line"></div>
|
||||
<div class="flash-overlay"></div>
|
||||
|
||||
<!-- Phase 1: Product Beauty Shot -->
|
||||
<div class="product-stage">
|
||||
<div class="product-glow"></div>
|
||||
<div class="product-body">
|
||||
<div class="watch-band-top"></div>
|
||||
<div class="watch-case">
|
||||
<div class="watch-crown"></div>
|
||||
</div>
|
||||
<div class="watch-screen">
|
||||
<div class="screen-content">
|
||||
<div class="screen-time">10:24</div>
|
||||
<div class="screen-hr">BPM</div>
|
||||
<div class="screen-hr-value">72</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="watch-band-bottom"></div>
|
||||
</div>
|
||||
<div class="product-name-reveal">APEX</div>
|
||||
<div class="product-tagline-small">Precision Engineered Performance</div>
|
||||
|
||||
<div class="gold-line gold-line-1"></div>
|
||||
<div class="gold-line gold-line-2"></div>
|
||||
<div class="gold-line gold-line-left"></div>
|
||||
<div class="gold-line gold-line-right"></div>
|
||||
|
||||
<!-- Spec panels -->
|
||||
<div class="spec-panel spec-1">
|
||||
<div class="spec-label">Battery Life</div>
|
||||
<div class="spec-value">14 Days</div>
|
||||
</div>
|
||||
<div class="spec-panel spec-2">
|
||||
<div class="spec-label">Water Resist</div>
|
||||
<div class="spec-value">100m</div>
|
||||
</div>
|
||||
<div class="spec-panel spec-3">
|
||||
<div class="spec-label">Weight</div>
|
||||
<div class="spec-value">38g</div>
|
||||
</div>
|
||||
<div class="spec-panel spec-4">
|
||||
<div class="spec-label">Display</div>
|
||||
<div class="spec-value">AMOLED</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Phase 3: Athletes in Motion -->
|
||||
<div class="athlete-stage">
|
||||
<div class="panel-divider panel-divider-1"></div>
|
||||
<div class="panel-divider panel-divider-2"></div>
|
||||
|
||||
<div class="athlete-panel athlete-panel-1">
|
||||
<div class="athlete-silhouette">
|
||||
<div class="runner-figure">
|
||||
<div class="runner-head"></div>
|
||||
<div class="runner-arm-back"></div>
|
||||
<div class="runner-torso"></div>
|
||||
<div class="runner-arm-front"></div>
|
||||
<div class="runner-leg-back"></div>
|
||||
<div class="runner-leg-front"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="athlete-label">Run</div>
|
||||
<!-- Motion lines -->
|
||||
<div class="motion-line" style="top: 300px; left: -200px; width: 500px;"></div>
|
||||
<div class="motion-line" style="top: 450px; left: -100px; width: 400px;"></div>
|
||||
<div class="motion-line" style="top: 600px; left: -150px; width: 450px;"></div>
|
||||
</div>
|
||||
|
||||
<div class="athlete-panel athlete-panel-2">
|
||||
<div class="athlete-silhouette">
|
||||
<div class="cyclist-figure">
|
||||
<div class="cyclist-head"></div>
|
||||
<div class="cyclist-body"></div>
|
||||
<div class="bike-frame">
|
||||
<div class="bike-frame-line frame-top"></div>
|
||||
<div class="bike-frame-line frame-down"></div>
|
||||
<div class="bike-frame-line frame-seat"></div>
|
||||
</div>
|
||||
<div class="bike-wheel-back"></div>
|
||||
<div class="bike-wheel-front"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="athlete-label">Ride</div>
|
||||
<div class="motion-line" style="top: 350px; left: -200px; width: 500px;"></div>
|
||||
<div class="motion-line" style="top: 500px; left: -100px; width: 400px;"></div>
|
||||
</div>
|
||||
|
||||
<div class="athlete-panel athlete-panel-3">
|
||||
<div class="athlete-silhouette">
|
||||
<div class="lifter-figure">
|
||||
<div class="lifter-head"></div>
|
||||
<div class="barbell">
|
||||
<div class="barbell-plate-left"></div>
|
||||
<div class="barbell-plate-right"></div>
|
||||
</div>
|
||||
<div class="lifter-torso"></div>
|
||||
<div class="lifter-leg-left"></div>
|
||||
<div class="lifter-leg-right"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="athlete-label">Lift</div>
|
||||
<div class="motion-line" style="top: 400px; left: -200px; width: 500px;"></div>
|
||||
<div class="motion-line" style="top: 550px; left: -100px; width: 400px;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Phase 4: Data / Pulse -->
|
||||
<div class="data-stage">
|
||||
<div class="pulse-ring pulse-ring-1"></div>
|
||||
<div class="pulse-ring pulse-ring-2"></div>
|
||||
<div class="pulse-ring pulse-ring-3"></div>
|
||||
<div class="pulse-ring pulse-ring-4"></div>
|
||||
<div class="pulse-ring pulse-ring-5"></div>
|
||||
|
||||
<div class="heartbeat-line">
|
||||
<svg viewBox="0 0 800 200" preserveAspectRatio="none">
|
||||
<path class="heartbeat-path" d="M0,100 L150,100 L180,100 L200,30 L220,170 L240,60 L260,130 L280,100 L400,100 L430,100 L450,25 L470,175 L490,55 L510,135 L530,100 L650,100 L680,100 L700,30 L720,170 L740,60 L760,130 L780,100 L800,100" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<div class="stat-counter stat-bpm">
|
||||
<div class="stat-number" id="bpm-number">0</div>
|
||||
<div class="stat-unit">Beats Per Minute</div>
|
||||
</div>
|
||||
<div class="stat-counter stat-steps">
|
||||
<div class="stat-number" id="steps-number">0</div>
|
||||
<div class="stat-unit">Daily Steps</div>
|
||||
</div>
|
||||
<div class="stat-counter stat-cals">
|
||||
<div class="stat-number" id="cals-number">0</div>
|
||||
<div class="stat-unit">Calories Burned</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Phase 5: Crescendo -->
|
||||
<div class="crescendo-stage">
|
||||
<div class="victory-burst"></div>
|
||||
|
||||
<!-- Gold particles -->
|
||||
<div class="gold-particle" style="top: 50%; left: 50%;"></div>
|
||||
<div class="gold-particle" style="top: 50%; left: 50%;"></div>
|
||||
<div class="gold-particle" style="top: 50%; left: 50%;"></div>
|
||||
<div class="gold-particle" style="top: 50%; left: 50%;"></div>
|
||||
<div class="gold-particle" style="top: 50%; left: 50%;"></div>
|
||||
<div class="gold-particle" style="top: 50%; left: 50%;"></div>
|
||||
<div class="gold-particle" style="top: 50%; left: 50%;"></div>
|
||||
<div class="gold-particle" style="top: 50%; left: 50%;"></div>
|
||||
<div class="gold-particle" style="top: 50%; left: 50%;"></div>
|
||||
<div class="gold-particle" style="top: 50%; left: 50%;"></div>
|
||||
<div class="gold-particle" style="top: 50%; left: 50%;"></div>
|
||||
<div class="gold-particle" style="top: 50%; left: 50%;"></div>
|
||||
<div class="gold-particle" style="top: 50%; left: 50%;"></div>
|
||||
<div class="gold-particle" style="top: 50%; left: 50%;"></div>
|
||||
<div class="gold-particle" style="top: 50%; left: 50%;"></div>
|
||||
<div class="gold-particle" style="top: 50%; left: 50%;"></div>
|
||||
<div class="gold-particle" style="top: 50%; left: 50%;"></div>
|
||||
<div class="gold-particle" style="top: 50%; left: 50%;"></div>
|
||||
<div class="gold-particle" style="top: 50%; left: 50%;"></div>
|
||||
<div class="gold-particle" style="top: 50%; left: 50%;"></div>
|
||||
|
||||
<!-- Confetti -->
|
||||
<div class="confetti" style="top: -20px; left: 10%; background: #D4AF37;"></div>
|
||||
<div class="confetti" style="top: -20px; left: 20%; background: #fff;"></div>
|
||||
<div class="confetti" style="top: -20px; left: 30%; background: #D4AF37;"></div>
|
||||
<div class="confetti" style="top: -20px; left: 40%; background: #B8962E;"></div>
|
||||
<div class="confetti" style="top: -20px; left: 50%; background: #D4AF37;"></div>
|
||||
<div class="confetti" style="top: -20px; left: 60%; background: #fff;"></div>
|
||||
<div class="confetti" style="top: -20px; left: 70%; background: #D4AF37;"></div>
|
||||
<div class="confetti" style="top: -20px; left: 80%; background: #B8962E;"></div>
|
||||
<div class="confetti" style="top: -20px; left: 90%; background: #D4AF37;"></div>
|
||||
<div class="confetti" style="top: -20px; left: 15%; background: #fff;"></div>
|
||||
<div class="confetti" style="top: -20px; left: 25%; background: #D4AF37;"></div>
|
||||
<div class="confetti" style="top: -20px; left: 35%; background: #B8962E;"></div>
|
||||
<div class="confetti" style="top: -20px; left: 45%; background: #D4AF37;"></div>
|
||||
<div class="confetti" style="top: -20px; left: 55%; background: #fff;"></div>
|
||||
<div class="confetti" style="top: -20px; left: 65%; background: #D4AF37;"></div>
|
||||
<div class="confetti" style="top: -20px; left: 75%; background: #B8962E;"></div>
|
||||
<div class="confetti" style="top: -20px; left: 85%; background: #D4AF37;"></div>
|
||||
<div class="confetti" style="top: -20px; left: 95%; background: #fff;"></div>
|
||||
|
||||
<!-- Tagline -->
|
||||
<div class="tagline-container">
|
||||
<div class="tagline-rise">Rise</div>
|
||||
<div class="tagline-bar"></div>
|
||||
<div class="tagline-above">Above</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Phase 6: Logo Lockup -->
|
||||
<div class="logo-stage">
|
||||
<div class="logo-apex">APEX</div>
|
||||
<div class="logo-divider"></div>
|
||||
<div class="logo-sub">Fitness Technology</div>
|
||||
<div class="logo-tagline-final">Rise Above</div>
|
||||
<div class="corner-accent corner-tl"></div>
|
||||
<div class="corner-accent corner-tr"></div>
|
||||
<div class="corner-accent corner-bl"></div>
|
||||
<div class="corner-accent corner-br"></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Scripts at end of body -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/CustomEase.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/DrawSVGPlugin.min.js"></script>
|
||||
<script>
|
||||
gsap.registerPlugin(CustomEase, DrawSVGPlugin);
|
||||
|
||||
// Custom eases
|
||||
CustomEase.create("luxuryReveal", "M0,0 C0.14,0 0.242,0.438 0.272,0.561 0.313,0.728 0.354,0.963 0.362,1 0.37,0.985 0.414,0.873 0.455,0.811 0.51,0.726 0.573,0.753 0.626,0.8 0.681,0.849 0.735,0.953 0.775,0.979 0.85,1.03 0.919,1.004 1,1");
|
||||
CustomEase.create("smoothPower", "M0,0 C0.25,0.1 0.25,1 1,1");
|
||||
CustomEase.create("dramaticIn", "M0,0 C0.55,0.055 0.675,0.19 1,1");
|
||||
|
||||
const tl = gsap.timeline({ paused: true });
|
||||
|
||||
// Helper: counter animation
|
||||
function animateCounter(el, target, duration, tl, position) {
|
||||
const obj = { val: 0 };
|
||||
tl.to(obj, {
|
||||
val: target,
|
||||
duration: duration,
|
||||
ease: "power2.out",
|
||||
onUpdate: function() {
|
||||
if (el) {
|
||||
el.textContent = Math.round(obj.val).toLocaleString();
|
||||
}
|
||||
}
|
||||
}, position);
|
||||
}
|
||||
|
||||
const particles = document.querySelectorAll('.gold-particle');
|
||||
const confetti = document.querySelectorAll('.confetti');
|
||||
const motionLines = document.querySelectorAll('.motion-line');
|
||||
|
||||
// ══════════════════════════════════════════════
|
||||
// PHASE 1: PRODUCT BEAUTY SHOT (0s - 12s)
|
||||
// ══════════════════════════════════════════════
|
||||
|
||||
// Fade in from black
|
||||
tl.to('.product-stage', { opacity: 1, duration: 1.5, ease: "power2.inOut" }, 0);
|
||||
|
||||
// Ambient glow breathes in
|
||||
tl.to('.product-glow', { opacity: 1, scale: 1.2, duration: 3, ease: "smoothPower" }, 0.5);
|
||||
|
||||
// Watch fades up with subtle float
|
||||
tl.to('.product-body', { opacity: 1, y: -20, duration: 2.5, ease: "luxuryReveal" }, 1);
|
||||
|
||||
// Screen elements illuminate
|
||||
tl.to('.screen-time', { opacity: 1, duration: 1.2, ease: "power2.out" }, 2.5);
|
||||
tl.to('.screen-hr', { opacity: 1, duration: 1, ease: "power2.out" }, 3.2);
|
||||
tl.to('.screen-hr-value', { opacity: 1, duration: 1, ease: "power2.out" }, 3.5);
|
||||
|
||||
// Product name reveals with letter spacing animation
|
||||
tl.fromTo('.product-name-reveal',
|
||||
{ opacity: 0, letterSpacing: '60px' },
|
||||
{ opacity: 1, letterSpacing: '30px', duration: 2, ease: "power3.out" }, 4);
|
||||
|
||||
// Tagline slides in
|
||||
tl.fromTo('.product-tagline-small',
|
||||
{ opacity: 0, y: 15 },
|
||||
{ opacity: 1, y: 0, duration: 1.5, ease: "power2.out" }, 5.5);
|
||||
|
||||
// Gold accent lines draw in
|
||||
tl.fromTo('.gold-line-1', { opacity: 0, scaleX: 0 }, { opacity: 0.3, scaleX: 1, duration: 2, ease: "power2.inOut" }, 3);
|
||||
tl.fromTo('.gold-line-2', { opacity: 0, scaleX: 0 }, { opacity: 0.3, scaleX: 1, duration: 2, ease: "power2.inOut" }, 3.3);
|
||||
tl.fromTo('.gold-line-left', { opacity: 0, scaleY: 0 }, { opacity: 0.2, scaleY: 1, duration: 2, ease: "power2.inOut" }, 3.6);
|
||||
tl.fromTo('.gold-line-right', { opacity: 0, scaleY: 0 }, { opacity: 0.2, scaleY: 1, duration: 2, ease: "power2.inOut" }, 3.9);
|
||||
|
||||
// Subtle glow pulse
|
||||
tl.to('.product-glow', { scale: 1.4, opacity: 0.8, duration: 2, ease: "sine.inOut", yoyo: true, repeat: 1 }, 5);
|
||||
|
||||
// Spec panels stagger in
|
||||
tl.fromTo('.spec-1', { opacity: 0, x: -30 }, { opacity: 1, x: 0, duration: 1, ease: "power2.out" }, 7);
|
||||
tl.fromTo('.spec-2', { opacity: 0, x: 30 }, { opacity: 1, x: 0, duration: 1, ease: "power2.out" }, 7.3);
|
||||
tl.fromTo('.spec-3', { opacity: 0, x: -30 }, { opacity: 1, x: 0, duration: 1, ease: "power2.out" }, 7.6);
|
||||
tl.fromTo('.spec-4', { opacity: 0, x: 30 }, { opacity: 1, x: 0, duration: 1, ease: "power2.out" }, 7.9);
|
||||
|
||||
// Scan line sweeps
|
||||
tl.fromTo('.scan-line', { opacity: 0, top: 0 }, { opacity: 0.5, top: '100%', duration: 2, ease: "none" }, 6);
|
||||
|
||||
// Hold and breathe
|
||||
tl.to('.product-body', { y: -30, duration: 3, ease: "sine.inOut" }, 9);
|
||||
|
||||
// ══════════════════════════════════════════════
|
||||
// TRANSITION 1→2: Product fades, energy shifts (12s - 14s)
|
||||
// ══════════════════════════════════════════════
|
||||
|
||||
tl.to('.product-stage', { opacity: 0, duration: 1.5, ease: "power2.inOut" }, 12);
|
||||
|
||||
// ══════════════════════════════════════════════
|
||||
// PHASE 2: ATHLETES IN MOTION (13s - 23s)
|
||||
// ══════════════════════════════════════════════
|
||||
|
||||
tl.to('.athlete-stage', { opacity: 1, duration: 1, ease: "power2.inOut" }, 13);
|
||||
|
||||
// Panel dividers slash in
|
||||
tl.fromTo('.panel-divider-1', { opacity: 0, scaleY: 0 }, { opacity: 0.8, scaleY: 1, duration: 0.8, ease: "power3.out" }, 13.5);
|
||||
tl.fromTo('.panel-divider-2', { opacity: 0, scaleY: 0 }, { opacity: 0.8, scaleY: 1, duration: 0.8, ease: "power3.out" }, 13.7);
|
||||
|
||||
// Athletes burst in from below — staggered
|
||||
tl.fromTo('.athlete-panel-1',
|
||||
{ opacity: 0, y: 100 },
|
||||
{ opacity: 1, y: 0, duration: 1.2, ease: "dramaticIn" }, 14);
|
||||
tl.fromTo('.athlete-panel-2',
|
||||
{ opacity: 0, y: 100 },
|
||||
{ opacity: 1, y: 0, duration: 1.2, ease: "dramaticIn" }, 14.3);
|
||||
tl.fromTo('.athlete-panel-3',
|
||||
{ opacity: 0, y: 100 },
|
||||
{ opacity: 1, y: 0, duration: 1.2, ease: "dramaticIn" }, 14.6);
|
||||
|
||||
// Athlete labels
|
||||
tl.to('.athlete-panel-1 .athlete-label', { opacity: 1, duration: 0.8, ease: "power2.out" }, 15.5);
|
||||
tl.to('.athlete-panel-2 .athlete-label', { opacity: 1, duration: 0.8, ease: "power2.out" }, 15.8);
|
||||
tl.to('.athlete-panel-3 .athlete-label', { opacity: 1, duration: 0.8, ease: "power2.out" }, 16.1);
|
||||
|
||||
// Motion lines streak across
|
||||
motionLines.forEach(function(line, i) {
|
||||
tl.fromTo(line,
|
||||
{ opacity: 0, x: -300 },
|
||||
{ opacity: 0.6, x: 200, duration: 1.5, ease: "power2.out" }, 15 + i * 0.15);
|
||||
tl.to(line, { opacity: 0, x: 500, duration: 1, ease: "power2.in" }, 16.5 + i * 0.1);
|
||||
});
|
||||
|
||||
// Runner leg animation
|
||||
tl.to('.runner-leg-front', { rotation: -15, duration: 0.3, ease: "sine.inOut", yoyo: true, repeat: 5 }, 15);
|
||||
tl.to('.runner-leg-back', { rotation: 15, duration: 0.3, ease: "sine.inOut", yoyo: true, repeat: 5 }, 15);
|
||||
tl.to('.runner-arm-front', { rotation: -20, duration: 0.3, ease: "sine.inOut", yoyo: true, repeat: 5 }, 15);
|
||||
tl.to('.runner-arm-back', { rotation: 15, duration: 0.3, ease: "sine.inOut", yoyo: true, repeat: 5 }, 15);
|
||||
|
||||
// Cyclist wheel spin
|
||||
tl.to('.bike-wheel-front', { rotation: 720, duration: 4, ease: "power1.inOut" }, 15);
|
||||
tl.to('.bike-wheel-back', { rotation: 720, duration: 4, ease: "power1.inOut" }, 15);
|
||||
|
||||
// Lifter barbell press up
|
||||
tl.to('.barbell', { y: -40, duration: 0.8, ease: "power3.out", yoyo: true, repeat: 2, repeatDelay: 0.4 }, 15.5);
|
||||
tl.to(['.barbell-plate-left', '.barbell-plate-right'], { y: -40, duration: 0.8, ease: "power3.out", yoyo: true, repeat: 2, repeatDelay: 0.4 }, 15.5);
|
||||
|
||||
// Energy building — subtle camera-style zoom on panels
|
||||
tl.to('.athlete-stage', { scale: 1.05, duration: 4, ease: "power1.in", transformOrigin: "center center" }, 18);
|
||||
|
||||
// Scan line sweep
|
||||
tl.fromTo('.scan-line', { opacity: 0, top: 0 }, { opacity: 0.3, top: '100%', duration: 1.5, ease: "none" }, 17);
|
||||
|
||||
// ══════════════════════════════════════════════
|
||||
// TRANSITION 2→3: Quick wipe (22s - 24s)
|
||||
// ══════════════════════════════════════════════
|
||||
|
||||
tl.to('.athlete-stage', { opacity: 0, duration: 1, ease: "power3.inOut" }, 22);
|
||||
|
||||
// ══════════════════════════════════════════════
|
||||
// PHASE 3: DATA PULSE / HEARTBEAT (23s - 30s)
|
||||
// ══════════════════════════════════════════════
|
||||
|
||||
tl.to('.data-stage', { opacity: 1, duration: 0.8, ease: "power2.inOut" }, 23);
|
||||
|
||||
// Pulse rings expand outward in sequence
|
||||
tl.fromTo('.pulse-ring-1', { opacity: 0, scale: 0.5 }, { opacity: 0.8, scale: 1, duration: 1, ease: "power2.out" }, 23.3);
|
||||
tl.fromTo('.pulse-ring-2', { opacity: 0, scale: 0.5 }, { opacity: 0.6, scale: 1, duration: 1, ease: "power2.out" }, 23.6);
|
||||
tl.fromTo('.pulse-ring-3', { opacity: 0, scale: 0.5 }, { opacity: 0.4, scale: 1, duration: 1, ease: "power2.out" }, 23.9);
|
||||
tl.fromTo('.pulse-ring-4', { opacity: 0, scale: 0.5 }, { opacity: 0.25, scale: 1, duration: 1, ease: "power2.out" }, 24.2);
|
||||
tl.fromTo('.pulse-ring-5', { opacity: 0, scale: 0.5 }, { opacity: 0.15, scale: 1, duration: 1, ease: "power2.out" }, 24.5);
|
||||
|
||||
// Pulse rings throb
|
||||
tl.to('.pulse-ring-1', { scale: 1.15, opacity: 0.5, duration: 0.6, ease: "sine.inOut", yoyo: true, repeat: 5 }, 24.5);
|
||||
tl.to('.pulse-ring-2', { scale: 1.1, opacity: 0.4, duration: 0.6, ease: "sine.inOut", yoyo: true, repeat: 5 }, 24.6);
|
||||
|
||||
// Heartbeat line draws
|
||||
tl.fromTo('.heartbeat-line', { opacity: 0 }, { opacity: 1, duration: 0.5 }, 24);
|
||||
tl.fromTo('.heartbeat-path', { drawSVG: "0%" }, { drawSVG: "100%", duration: 3, ease: "none" }, 24.2);
|
||||
|
||||
// Stat counters
|
||||
tl.to('.stat-bpm', { opacity: 1, duration: 0.5 }, 25);
|
||||
animateCounter(document.getElementById('bpm-number'), 156, 2, tl, 25);
|
||||
|
||||
tl.to('.stat-steps', { opacity: 1, duration: 0.5 }, 25.5);
|
||||
animateCounter(document.getElementById('steps-number'), 12847, 2.5, tl, 25.5);
|
||||
|
||||
tl.to('.stat-cals', { opacity: 1, duration: 0.5 }, 26);
|
||||
animateCounter(document.getElementById('cals-number'), 2340, 2.5, tl, 26);
|
||||
|
||||
// Intensify: rings pulse faster
|
||||
tl.to('.pulse-ring-3', { scale: 1.2, opacity: 0.5, duration: 0.4, ease: "sine.inOut", yoyo: true, repeat: 3 }, 27.5);
|
||||
|
||||
// Scan line
|
||||
tl.fromTo('.scan-line', { opacity: 0, top: 0 }, { opacity: 0.4, top: '100%', duration: 1, ease: "none" }, 27);
|
||||
|
||||
// ══════════════════════════════════════════════
|
||||
// TRANSITION 3→4: Flash into crescendo (29s - 31s)
|
||||
// ══════════════════════════════════════════════
|
||||
|
||||
tl.to('.data-stage', { opacity: 0, duration: 0.5, ease: "power3.in" }, 29.5);
|
||||
tl.to('.flash-overlay', { opacity: 0.6, duration: 0.15, ease: "power4.in" }, 30);
|
||||
tl.to('.flash-overlay', { opacity: 0, duration: 0.5, ease: "power2.out" }, 30.15);
|
||||
|
||||
// ══════════════════════════════════════════════
|
||||
// PHASE 4: CRESCENDO / CELEBRATION (30s - 36s)
|
||||
// ══════════════════════════════════════════════
|
||||
|
||||
tl.to('.crescendo-stage', { opacity: 1, duration: 0.3, ease: "power2.out" }, 30.2);
|
||||
|
||||
// Victory burst expands
|
||||
tl.fromTo('.victory-burst',
|
||||
{ width: 0, height: 0, opacity: 0 },
|
||||
{ width: 1400, height: 1400, opacity: 0.6, duration: 1.5, ease: "power3.out" }, 30.3);
|
||||
tl.to('.victory-burst', { opacity: 0, duration: 1.5, ease: "power2.in" }, 31.5);
|
||||
|
||||
// Gold particles explode outward
|
||||
particles.forEach(function(p, i) {
|
||||
var angle = (i / particles.length) * Math.PI * 2;
|
||||
var dist = 300 + Math.random() * 400;
|
||||
var xDest = Math.cos(angle) * dist;
|
||||
var yDest = Math.sin(angle) * dist;
|
||||
tl.fromTo(p,
|
||||
{ opacity: 0, x: 0, y: 0, scale: 0 },
|
||||
{ opacity: 1, x: xDest, y: yDest, scale: 1 + Math.random() * 2, duration: 1.2, ease: "power3.out" }, 30.3 + Math.random() * 0.3);
|
||||
tl.to(p, { opacity: 0, duration: 0.8, ease: "power2.in" }, 31.5 + Math.random() * 0.3);
|
||||
});
|
||||
|
||||
// Confetti falls
|
||||
confetti.forEach(function(c, i) {
|
||||
tl.fromTo(c,
|
||||
{ opacity: 0, y: -40, rotation: 0 },
|
||||
{ opacity: 0.8, y: 1100, rotation: 360 + Math.random() * 360,
|
||||
duration: 3 + Math.random() * 2, ease: "power1.in" }, 30.5 + Math.random() * 0.5);
|
||||
});
|
||||
|
||||
// TAGLINE: "Rise Above"
|
||||
tl.fromTo('.tagline-container', { opacity: 0 }, { opacity: 1, duration: 0.3 }, 31);
|
||||
|
||||
tl.fromTo('.tagline-rise',
|
||||
{ opacity: 0, y: 60, scale: 0.8 },
|
||||
{ opacity: 1, y: 0, scale: 1, duration: 1.2, ease: "power3.out" }, 31.2);
|
||||
|
||||
tl.fromTo('.tagline-bar',
|
||||
{ width: 0 },
|
||||
{ width: 200, duration: 0.8, ease: "power2.inOut" }, 32);
|
||||
|
||||
tl.fromTo('.tagline-above',
|
||||
{ opacity: 0, y: -60, scale: 0.8 },
|
||||
{ opacity: 1, y: 0, scale: 1, duration: 1.2, ease: "power3.out" }, 32.2);
|
||||
|
||||
// Tagline pulses with gold shimmer
|
||||
tl.to('.tagline-above', { textShadow: "0 0 40px rgba(212,175,55,0.8)", duration: 0.8, ease: "sine.inOut", yoyo: true, repeat: 1 }, 33.5);
|
||||
|
||||
// Flash transition
|
||||
tl.to('.flash-overlay', { opacity: 0.4, duration: 0.1 }, 35);
|
||||
tl.to('.flash-overlay', { opacity: 0, duration: 0.4 }, 35.1);
|
||||
|
||||
// ══════════════════════════════════════════════
|
||||
// PHASE 5: LOGO LOCKUP (35s - 40s)
|
||||
// ══════════════════════════════════════════════
|
||||
|
||||
tl.to('.crescendo-stage', { opacity: 0, duration: 0.8, ease: "power2.inOut" }, 35);
|
||||
|
||||
tl.to('.logo-stage', { opacity: 1, duration: 0.8, ease: "power2.inOut" }, 35.5);
|
||||
|
||||
// APEX logo scales in
|
||||
tl.fromTo('.logo-apex',
|
||||
{ opacity: 0, scale: 1.5, letterSpacing: '80px' },
|
||||
{ opacity: 1, scale: 1, letterSpacing: '40px', duration: 1.8, ease: "power3.out" }, 35.8);
|
||||
|
||||
// Divider line
|
||||
tl.fromTo('.logo-divider',
|
||||
{ width: 0 },
|
||||
{ width: 300, duration: 1, ease: "power2.inOut" }, 36.8);
|
||||
|
||||
// Sub text
|
||||
tl.fromTo('.logo-sub',
|
||||
{ opacity: 0, y: 10 },
|
||||
{ opacity: 1, y: 0, duration: 1, ease: "power2.out" }, 37.3);
|
||||
|
||||
// Final tagline
|
||||
tl.fromTo('.logo-tagline-final',
|
||||
{ opacity: 0, y: 10 },
|
||||
{ opacity: 1, y: 0, duration: 1, ease: "power2.out" }, 37.8);
|
||||
|
||||
// Corner accents
|
||||
tl.to('.corner-tl', { opacity: 1, duration: 0.6, ease: "power2.out" }, 37);
|
||||
tl.to('.corner-tr', { opacity: 1, duration: 0.6, ease: "power2.out" }, 37.15);
|
||||
tl.to('.corner-br', { opacity: 1, duration: 0.6, ease: "power2.out" }, 37.3);
|
||||
tl.to('.corner-bl', { opacity: 1, duration: 0.6, ease: "power2.out" }, 37.45);
|
||||
|
||||
// Gentle breathe on final logo
|
||||
tl.to('.logo-apex', {
|
||||
textShadow: "0 0 60px rgba(212,175,55,0.4)",
|
||||
duration: 1.5, ease: "sine.inOut"
|
||||
}, 38);
|
||||
|
||||
// Hold on final frame through 40s
|
||||
tl.to({}, { duration: 1 }, 39);
|
||||
|
||||
// Register the timeline
|
||||
window.__timelines = window.__timelines || {};
|
||||
window.__timelines["gradual-escalation-apex"] = tl;
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,686 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=1920, height=1080">
|
||||
<title>SHATTERED — Title Sequence</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { background: #000; display: flex; align-items: center; justify-content: center; min-height: 100vh; overflow: hidden; }
|
||||
|
||||
.composition {
|
||||
background: #0a0a0a;
|
||||
font-family: 'Arial Black', 'Helvetica Neue', Arial, sans-serif;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
/* === PHASE 1: Creeping Unease — drifting text fragments === */
|
||||
.vignette {
|
||||
position: absolute; inset: 0;
|
||||
background: radial-gradient(ellipse at center, transparent 30%, rgba(0,0,0,0.85) 100%);
|
||||
opacity: 0;
|
||||
z-index: 50;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.fragment {
|
||||
position: absolute;
|
||||
color: rgba(255, 255, 255, 0.06);
|
||||
font-size: 22px;
|
||||
letter-spacing: 8px;
|
||||
text-transform: uppercase;
|
||||
opacity: 0;
|
||||
will-change: transform, opacity;
|
||||
}
|
||||
.fragment-0 { top: 180px; left: 200px; font-size: 18px; }
|
||||
.fragment-1 { top: 340px; left: 1400px; font-size: 26px; }
|
||||
.fragment-2 { top: 520px; left: 600px; font-size: 16px; }
|
||||
.fragment-3 { top: 700px; left: 1050px; font-size: 20px; }
|
||||
.fragment-4 { top: 260px; left: 850px; font-size: 14px; }
|
||||
.fragment-5 { top: 800px; left: 300px; font-size: 24px; }
|
||||
.fragment-6 { top: 450px; left: 1600px; font-size: 15px; }
|
||||
.fragment-7 { top: 150px; left: 1100px; font-size: 19px; }
|
||||
|
||||
.red-line {
|
||||
position: absolute;
|
||||
background: rgba(180, 20, 20, 0.3);
|
||||
height: 1px;
|
||||
width: 0;
|
||||
opacity: 0;
|
||||
}
|
||||
.red-line-0 { top: 400px; left: 100px; }
|
||||
.red-line-1 { top: 650px; left: 800px; }
|
||||
.red-line-2 { top: 280px; left: 500px; }
|
||||
|
||||
/* === PHASE 2: Title and Impact === */
|
||||
.anticipation-darken {
|
||||
position: absolute; inset: 0;
|
||||
background: #000;
|
||||
opacity: 0;
|
||||
z-index: 55;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.title-container {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 100;
|
||||
opacity: 0;
|
||||
}
|
||||
.title-text {
|
||||
font-size: 180px;
|
||||
font-weight: 900;
|
||||
color: #fff;
|
||||
letter-spacing: 28px;
|
||||
text-transform: uppercase;
|
||||
position: relative;
|
||||
will-change: transform, opacity;
|
||||
}
|
||||
.title-glow {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-size: 180px;
|
||||
font-weight: 900;
|
||||
color: transparent;
|
||||
letter-spacing: 28px;
|
||||
text-transform: uppercase;
|
||||
text-shadow: 0 0 60px rgba(200, 30, 30, 0.6), 0 0 120px rgba(200, 30, 30, 0.3);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
will-change: opacity;
|
||||
}
|
||||
.title-shadow {
|
||||
position: absolute;
|
||||
top: 52%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-size: 180px;
|
||||
font-weight: 900;
|
||||
color: transparent;
|
||||
letter-spacing: 28px;
|
||||
text-transform: uppercase;
|
||||
text-shadow: 0 8px 30px rgba(0,0,0,0.8);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
will-change: opacity, transform;
|
||||
}
|
||||
|
||||
/* Shockwave ring */
|
||||
.shockwave {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 100px;
|
||||
height: 100px;
|
||||
border: 2px solid rgba(200, 30, 30, 0.5);
|
||||
border-radius: 50%;
|
||||
transform: translate(-50%, -50%) scale(0.5);
|
||||
opacity: 0;
|
||||
z-index: 90;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* === Glass Shards === */
|
||||
.shard {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 0; height: 0;
|
||||
opacity: 0;
|
||||
z-index: 80;
|
||||
will-change: transform, opacity;
|
||||
pointer-events: none;
|
||||
}
|
||||
/* Each shard is a CSS triangle */
|
||||
.shard-light {
|
||||
border-left: 8px solid transparent;
|
||||
border-right: 8px solid transparent;
|
||||
border-bottom: 20px solid rgba(255, 255, 255, 0.6);
|
||||
}
|
||||
.shard-red {
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
border-bottom: 16px solid rgba(200, 40, 40, 0.5);
|
||||
}
|
||||
.shard-dim {
|
||||
border-left: 5px solid transparent;
|
||||
border-right: 5px solid transparent;
|
||||
border-bottom: 12px solid rgba(255, 255, 255, 0.2);
|
||||
}
|
||||
|
||||
/* Shard reflections (secondary — drag behind shards) */
|
||||
.shard-reflection {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 3px;
|
||||
height: 3px;
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
border-radius: 50%;
|
||||
opacity: 0;
|
||||
z-index: 79;
|
||||
will-change: transform, opacity;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* === PHASE 3: Floating Debris === */
|
||||
.debris {
|
||||
position: absolute;
|
||||
background: rgba(255, 255, 255, 0.08);
|
||||
border-radius: 1px;
|
||||
opacity: 0;
|
||||
z-index: 60;
|
||||
will-change: transform, opacity;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Subtitle */
|
||||
.subtitle {
|
||||
position: absolute;
|
||||
top: 62%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, 0);
|
||||
font-size: 24px;
|
||||
letter-spacing: 14px;
|
||||
color: rgba(200, 40, 40, 0.7);
|
||||
text-transform: uppercase;
|
||||
opacity: 0;
|
||||
z-index: 100;
|
||||
will-change: opacity;
|
||||
}
|
||||
|
||||
/* Ambient red pulse on background */
|
||||
.bg-pulse {
|
||||
position: absolute; inset: 0;
|
||||
background: radial-gradient(ellipse at center, rgba(120, 10, 10, 0.08) 0%, transparent 70%);
|
||||
opacity: 0;
|
||||
z-index: 5;
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="composition" data-composition-id="high-contrast-arc-shattered-v2" style="width: 1920px; height: 1080px; position: relative; overflow: hidden;">
|
||||
|
||||
<!-- Background pulse -->
|
||||
<div class="bg-pulse" id="bg-pulse"></div>
|
||||
|
||||
<!-- Vignette -->
|
||||
<div class="vignette" id="vignette"></div>
|
||||
|
||||
<!-- Anticipation darkening layer -->
|
||||
<div class="anticipation-darken" id="anticipation-darken"></div>
|
||||
|
||||
<!-- Phase 1: Text fragments -->
|
||||
<div class="fragment fragment-0" id="frag-0">NOTHING</div>
|
||||
<div class="fragment fragment-1" id="frag-1">IS SAFE</div>
|
||||
<div class="fragment fragment-2" id="frag-2">THE TRUTH</div>
|
||||
<div class="fragment fragment-3" id="frag-3">BREAKS</div>
|
||||
<div class="fragment fragment-4" id="frag-4">EVERY MIRROR</div>
|
||||
<div class="fragment fragment-5" id="frag-5">LIES</div>
|
||||
<div class="fragment fragment-6" id="frag-6">BENEATH</div>
|
||||
<div class="fragment fragment-7" id="frag-7">THE SURFACE</div>
|
||||
|
||||
<!-- Red accent lines -->
|
||||
<div class="red-line red-line-0" id="red-line-0"></div>
|
||||
<div class="red-line red-line-1" id="red-line-1"></div>
|
||||
<div class="red-line red-line-2" id="red-line-2"></div>
|
||||
|
||||
<!-- Glass shards (generated positions, scattered from center) -->
|
||||
<div class="shard shard-light" id="shard-0"></div>
|
||||
<div class="shard shard-red" id="shard-1"></div>
|
||||
<div class="shard shard-dim" id="shard-2"></div>
|
||||
<div class="shard shard-light" id="shard-3"></div>
|
||||
<div class="shard shard-red" id="shard-4"></div>
|
||||
<div class="shard shard-dim" id="shard-5"></div>
|
||||
<div class="shard shard-light" id="shard-6"></div>
|
||||
<div class="shard shard-red" id="shard-7"></div>
|
||||
<div class="shard shard-dim" id="shard-8"></div>
|
||||
<div class="shard shard-light" id="shard-9"></div>
|
||||
<div class="shard shard-red" id="shard-10"></div>
|
||||
<div class="shard shard-dim" id="shard-11"></div>
|
||||
<div class="shard shard-light" id="shard-12"></div>
|
||||
<div class="shard shard-red" id="shard-13"></div>
|
||||
<div class="shard shard-light" id="shard-14"></div>
|
||||
<div class="shard shard-dim" id="shard-15"></div>
|
||||
<div class="shard shard-red" id="shard-16"></div>
|
||||
<div class="shard shard-light" id="shard-17"></div>
|
||||
|
||||
<!-- Shard reflections (drag behind shards) -->
|
||||
<div class="shard-reflection" id="ref-0"></div>
|
||||
<div class="shard-reflection" id="ref-1"></div>
|
||||
<div class="shard-reflection" id="ref-2"></div>
|
||||
<div class="shard-reflection" id="ref-3"></div>
|
||||
<div class="shard-reflection" id="ref-4"></div>
|
||||
<div class="shard-reflection" id="ref-5"></div>
|
||||
<div class="shard-reflection" id="ref-6"></div>
|
||||
<div class="shard-reflection" id="ref-7"></div>
|
||||
<div class="shard-reflection" id="ref-8"></div>
|
||||
|
||||
<!-- Shockwave -->
|
||||
<div class="shockwave" id="shockwave"></div>
|
||||
|
||||
<!-- Title -->
|
||||
<div class="title-container" id="title-container">
|
||||
<div class="title-shadow" id="title-shadow">SHATTERED</div>
|
||||
<div class="title-glow" id="title-glow">SHATTERED</div>
|
||||
<div class="title-text" id="title-text">SHATTERED</div>
|
||||
</div>
|
||||
|
||||
<!-- Subtitle -->
|
||||
<div class="subtitle" id="subtitle">A THRILLER</div>
|
||||
|
||||
<!-- Phase 3: Floating debris particles -->
|
||||
<div class="debris" id="debris-0" style="width:4px;height:4px;top:200px;left:300px;"></div>
|
||||
<div class="debris" id="debris-1" style="width:3px;height:6px;top:400px;left:1500px;"></div>
|
||||
<div class="debris" id="debris-2" style="width:5px;height:3px;top:600px;left:900px;"></div>
|
||||
<div class="debris" id="debris-3" style="width:2px;height:5px;top:300px;left:1200px;"></div>
|
||||
<div class="debris" id="debris-4" style="width:6px;height:2px;top:750px;left:500px;"></div>
|
||||
<div class="debris" id="debris-5" style="width:3px;height:3px;top:150px;left:1600px;"></div>
|
||||
<div class="debris" id="debris-6" style="width:4px;height:5px;top:850px;left:800px;"></div>
|
||||
<div class="debris" id="debris-7" style="width:2px;height:4px;top:500px;left:200px;"></div>
|
||||
<div class="debris" id="debris-8" style="width:5px;height:2px;top:680px;left:1350px;"></div>
|
||||
<div class="debris" id="debris-9" style="width:3px;height:4px;top:100px;left:700px;"></div>
|
||||
<div class="debris" id="debris-10" style="width:4px;height:3px;top:920px;left:1100px;"></div>
|
||||
<div class="debris" id="debris-11" style="width:2px;height:6px;top:350px;left:400px;"></div>
|
||||
<div class="debris" id="debris-12" style="width:3px;height:2px;top:550px;left:1700px;background:rgba(200,40,40,0.1);"></div>
|
||||
<div class="debris" id="debris-13" style="width:5px;height:4px;top:780px;left:150px;"></div>
|
||||
<div class="debris" id="debris-14" style="width:2px;height:3px;top:430px;left:1000px;background:rgba(200,40,40,0.1);"></div>
|
||||
<div class="debris" id="debris-15" style="width:4px;height:2px;top:620px;left:1800px;"></div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- GSAP Core + Plugins -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/CustomEase.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/MotionPathPlugin.min.js"></script>
|
||||
<script>
|
||||
gsap.registerPlugin(CustomEase, MotionPathPlugin);
|
||||
|
||||
// ============================================================
|
||||
// TIMING SCALE: 0.08s | 0.4s | 1.2s | 3s
|
||||
// Multiples/fractions used: 0.08, 0.4, 1.2, 2.4, 3, 6, 9, 12
|
||||
// ============================================================
|
||||
|
||||
const tl = gsap.timeline({ paused: true });
|
||||
window.__timelines = window.__timelines || {};
|
||||
window.__timelines["high-contrast-arc-shattered-v2"] = tl;
|
||||
|
||||
// Custom eases
|
||||
// Creep: barely moves for most of the duration, then rushes at the end
|
||||
CustomEase.create("creep", "M0,0 C0.6,0.02 0.65,0.04 0.75,0.08 0.85,0.3 0.92,0.65 1,1");
|
||||
// Tension snap: holds even longer, snaps harder
|
||||
CustomEase.create("tensionSnap", "M0,0 C0.8,0 0.7,0.05 0.8,0.1 0.9,0.4 0.95,0.7 1,1");
|
||||
|
||||
// ============================================================
|
||||
// PHASE 1: CREEPING UNEASE (0s - 18s)
|
||||
// Mood: Tense. Slow reveals, micro-drift, uncomfortable holds.
|
||||
// ============================================================
|
||||
|
||||
// Vignette tightens slowly — staging through light
|
||||
tl.to("#vignette", { opacity: 0.7, duration: 6, ease: "power1.in" }, 0);
|
||||
tl.to("#vignette", { opacity: 0.9, duration: 12, ease: "sine.inOut" }, 6);
|
||||
|
||||
// Background pulse — barely visible red warmth creeping in
|
||||
tl.to("#bg-pulse", { opacity: 0.4, duration: 6, ease: "creep" }, 3);
|
||||
|
||||
// Text fragments — creep in one at a time, long uncomfortable gaps
|
||||
// Each fragment drifts slightly after appearing (moving hold)
|
||||
const fragments = [
|
||||
{ id: "#frag-0", enterAt: 1.2, driftX: 8, driftY: -3 },
|
||||
{ id: "#frag-1", enterAt: 3.6, driftX: -5, driftY: 4 },
|
||||
{ id: "#frag-2", enterAt: 5.4, driftX: 6, driftY: -5 },
|
||||
{ id: "#frag-3", enterAt: 7.2, driftX: -7, driftY: 3 },
|
||||
{ id: "#frag-4", enterAt: 8.4, driftX: 4, driftY: -4 },
|
||||
{ id: "#frag-5", enterAt: 10.2, driftX: -6, driftY: 5 },
|
||||
{ id: "#frag-6", enterAt: 11.4, driftX: 5, driftY: -3 },
|
||||
{ id: "#frag-7", enterAt: 12.6, driftX: -4, driftY: 4 }
|
||||
];
|
||||
|
||||
fragments.forEach((f) => {
|
||||
// Entrance: creeping in from slightly off (barely moves for 80%, then arrives)
|
||||
tl.fromTo(f.id,
|
||||
{ opacity: 0, y: 15 },
|
||||
{ opacity: 1, y: 0, duration: 3, ease: "creep" },
|
||||
f.enterAt
|
||||
);
|
||||
|
||||
// Moving hold: perpetual micro-drift after entrance — nothing is ever still
|
||||
tl.to(f.id, {
|
||||
x: f.driftX, y: f.driftY,
|
||||
duration: 6, ease: "sine.inOut",
|
||||
yoyo: true, repeat: 3
|
||||
}, f.enterAt + 3);
|
||||
});
|
||||
|
||||
// Red accent lines — thin, creeping, ominous
|
||||
tl.to("#red-line-0", { width: 180, opacity: 0.6, duration: 3, ease: "creep" }, 4.8);
|
||||
tl.to("#red-line-1", { width: 120, opacity: 0.4, duration: 3, ease: "creep" }, 7.2);
|
||||
tl.to("#red-line-2", { width: 200, opacity: 0.5, duration: 3, ease: "creep" }, 9.6);
|
||||
|
||||
// Red lines also get a moving hold — slow width oscillation
|
||||
["#red-line-0", "#red-line-1", "#red-line-2"].forEach((id, i) => {
|
||||
tl.to(id, {
|
||||
width: "+=15", opacity: "-=0.05",
|
||||
duration: 3, ease: "sine.inOut",
|
||||
yoyo: true, repeat: 2
|
||||
}, 8 + i * 1.2);
|
||||
});
|
||||
|
||||
// Phase 1 Exit: fragments compress inward (tense exit style)
|
||||
fragments.forEach((f, i) => {
|
||||
tl.to(f.id, {
|
||||
x: 0, y: 0, opacity: 0,
|
||||
duration: 1.2, ease: "power2.in"
|
||||
}, 15.6 + i * 0.08);
|
||||
});
|
||||
|
||||
// Red lines fade
|
||||
tl.to(".red-line", { opacity: 0, width: 0, duration: 1.2, ease: "power2.in" }, 16);
|
||||
|
||||
// ============================================================
|
||||
// PHASE 2: ANTICIPATION + EXPLOSIVE REVEAL (18s - 28s)
|
||||
// Transition: Hard cut from tense to punchy
|
||||
// 2.5s anticipation → 0.08s slam → secondaries AFTER
|
||||
// ============================================================
|
||||
|
||||
// --- ANTICIPATION: 18s - 20.4s (2.4s of near-stillness) ---
|
||||
// The silence IS the anticipation.
|
||||
|
||||
// Scene darkens — a held breath
|
||||
tl.to("#anticipation-darken", {
|
||||
opacity: 0.4, duration: 2.4, ease: "tensionSnap"
|
||||
}, 18);
|
||||
|
||||
// Vignette tightens further
|
||||
tl.to("#vignette", { opacity: 1, duration: 2.4, ease: "power1.in" }, 18);
|
||||
|
||||
// Background pulse dims — the world contracts
|
||||
tl.to("#bg-pulse", { opacity: 0.1, duration: 2.4, ease: "power2.in" }, 18);
|
||||
|
||||
// Barely perceptible camera zoom (via inner wrapper to not break studio)
|
||||
// Omitted — scaling .composition breaks HyperFrames Studio rendering
|
||||
|
||||
// --- THE SLAM: 20.4s — 0.08s duration. NOTHING else moves. ---
|
||||
const slamTime = 20.4;
|
||||
|
||||
// Title container appears
|
||||
tl.set("#title-container", { opacity: 1 }, slamTime);
|
||||
|
||||
// Title text: scale overshoot with squash (volume preserved)
|
||||
// Arrives at scaleY: 0.7, scaleX: 1.43 (volume = 0.7 * 1.43 = 1.001)
|
||||
tl.fromTo("#title-text",
|
||||
{ scaleY: 2.2, scaleX: 0.45, opacity: 0 },
|
||||
{ scaleY: 0.7, scaleX: 1.43, opacity: 1, duration: 0.08, ease: "power4.out" },
|
||||
slamTime
|
||||
);
|
||||
|
||||
// Squash recovery — elastic settle
|
||||
tl.to("#title-text", {
|
||||
scaleY: 1.08, scaleX: 0.93, duration: 0.4, ease: "power3.out"
|
||||
}, slamTime + 0.08);
|
||||
tl.to("#title-text", {
|
||||
scaleY: 0.97, scaleX: 1.03, duration: 0.4, ease: "sine.inOut"
|
||||
}, slamTime + 0.48);
|
||||
tl.to("#title-text", {
|
||||
scaleY: 1, scaleX: 1, duration: 0.4, ease: "sine.out"
|
||||
}, slamTime + 0.88);
|
||||
|
||||
// Camera zoom reset omitted — see above
|
||||
|
||||
// --- SECONDARY ACTIONS: all AFTER the slam (slamTime + 0.08 and later) ---
|
||||
|
||||
// Anticipation darken layer clears
|
||||
tl.to("#anticipation-darken", {
|
||||
opacity: 0, duration: 0.4, ease: "power2.out"
|
||||
}, slamTime + 0.08);
|
||||
|
||||
// Title shadow — DRAGS 0.05s behind the title
|
||||
tl.fromTo("#title-shadow",
|
||||
{ opacity: 0, scaleY: 0.7, scaleX: 1.43 },
|
||||
{ opacity: 0.6, scaleY: 1, scaleX: 1, duration: 0.4, ease: "power2.out" },
|
||||
slamTime + 0.05
|
||||
);
|
||||
|
||||
// Title glow — DRAGS 0.08s behind the title
|
||||
tl.to("#title-glow", {
|
||||
opacity: 0.7, duration: 1.2, ease: "sine.out"
|
||||
}, slamTime + 0.08);
|
||||
|
||||
// Shockwave — expands AFTER slam
|
||||
tl.fromTo("#shockwave",
|
||||
{ scale: 0.5, opacity: 0.7 },
|
||||
{ scale: 15, opacity: 0, duration: 1.2, ease: "power2.out" },
|
||||
slamTime + 0.12
|
||||
);
|
||||
|
||||
// Vignette reacts
|
||||
tl.to("#vignette", {
|
||||
opacity: 0.5, duration: 0.4, ease: "power2.out"
|
||||
}, slamTime + 0.16);
|
||||
|
||||
// Background pulse flares red after impact
|
||||
tl.to("#bg-pulse", {
|
||||
opacity: 0.8, duration: 0.08, ease: "power4.out"
|
||||
}, slamTime + 0.12);
|
||||
tl.to("#bg-pulse", {
|
||||
opacity: 0.2, duration: 1.2, ease: "sine.out"
|
||||
}, slamTime + 0.2);
|
||||
|
||||
// === GLASS SHARDS — scatter on ARCS after slam ===
|
||||
// Faster shards = flatter arcs, lighter = faster
|
||||
// Each shard has a reflection that drags 0.04-0.06s behind
|
||||
|
||||
const shardData = [
|
||||
// { id, refId, angle (degrees), speed (px), arcDepth, dur, rotation }
|
||||
{ id: "#shard-0", ref: "#ref-0", x: -420, y: -280, arc: -90, dur: 1.2, rot: -220 },
|
||||
{ id: "#shard-1", ref: "#ref-1", x: 380, y: -310, arc: -80, dur: 1.2, rot: 195 },
|
||||
{ id: "#shard-2", ref: null, x: -280, y: 200, arc: 60, dur: 0.4, rot: -150 },
|
||||
{ id: "#shard-3", ref: "#ref-2", x: 500, y: -180, arc: -50, dur: 0.4, rot: 280 },
|
||||
{ id: "#shard-4", ref: null, x: -600, y: -120, arc: -30, dur: 0.4, rot: -310 },
|
||||
{ id: "#shard-5", ref: "#ref-3", x: 200, y: 320, arc: 100, dur: 1.2, rot: 160 },
|
||||
{ id: "#shard-6", ref: null, x: -350, y: -400, arc: -110, dur: 1.2, rot: -240 },
|
||||
{ id: "#shard-7", ref: "#ref-4", x: 650, y: 100, arc: 25, dur: 0.4, rot: 190 },
|
||||
{ id: "#shard-8", ref: null, x: -150, y: 350, arc: 80, dur: 1.2, rot: -130 },
|
||||
{ id: "#shard-9", ref: "#ref-5", x: 300, y: -450, arc: -120, dur: 1.2, rot: 260 },
|
||||
{ id: "#shard-10", ref: null, x: -500, y: 250, arc: 70, dur: 0.4, rot: -200 },
|
||||
{ id: "#shard-11", ref: "#ref-6", x: 450, y: 280, arc: 55, dur: 1.2, rot: 175 },
|
||||
{ id: "#shard-12", ref: null, x: -250, y: -350, arc: -95, dur: 0.4, rot: -265 },
|
||||
{ id: "#shard-13", ref: "#ref-7", x: 550, y: -250, arc: -45, dur: 0.4, rot: 230 },
|
||||
{ id: "#shard-14", ref: null, x: -450, y: 150, arc: 40, dur: 1.2, rot: -180 },
|
||||
{ id: "#shard-15", ref: "#ref-8", x: 180, y: -200, arc: -70, dur: 0.4, rot: 145 },
|
||||
{ id: "#shard-16", ref: null, x: -320, y: -150, arc: -35, dur: 0.4, rot: -160 },
|
||||
{ id: "#shard-17", ref: null, x: 400, y: 350, arc: 90, dur: 1.2, rot: 210 }
|
||||
];
|
||||
|
||||
const shardLaunchTime = slamTime + 0.12;
|
||||
|
||||
shardData.forEach((s, i) => {
|
||||
const stagger = i * 0.02; // slight cascade, not simultaneous
|
||||
const launchT = shardLaunchTime + stagger;
|
||||
|
||||
// Shard appears and moves on an arc
|
||||
// Arc: x goes to target, y arcs via a midpoint
|
||||
const midY = s.arc; // arc depth — deeper for slower shards
|
||||
const halfDur = s.dur / 2;
|
||||
|
||||
tl.set(s.id, { opacity: 1 }, launchT);
|
||||
|
||||
// Horizontal + rotation over full duration
|
||||
tl.to(s.id, {
|
||||
x: s.x, rotation: s.rot,
|
||||
duration: s.dur, ease: "power2.out"
|
||||
}, launchT);
|
||||
|
||||
// Vertical arc: up first half, down second half (parabolic arc)
|
||||
tl.to(s.id, {
|
||||
y: midY, duration: halfDur, ease: "power2.out"
|
||||
}, launchT);
|
||||
tl.to(s.id, {
|
||||
y: s.y, duration: halfDur, ease: "power2.in"
|
||||
}, launchT + halfDur);
|
||||
|
||||
// Fade out at end
|
||||
tl.to(s.id, {
|
||||
opacity: 0, duration: 0.4, ease: "power1.in"
|
||||
}, launchT + s.dur - 0.4);
|
||||
|
||||
// Reflection DRAGS behind shard by 0.04-0.06s
|
||||
if (s.ref) {
|
||||
const dragDelay = 0.04 + (i % 3) * 0.01;
|
||||
tl.set(s.ref, { opacity: 0.4 }, launchT + dragDelay);
|
||||
tl.to(s.ref, {
|
||||
x: s.x * 0.9, y: s.y * 0.9, rotation: s.rot * 0.7,
|
||||
duration: s.dur, ease: "power2.out"
|
||||
}, launchT + dragDelay);
|
||||
tl.to(s.ref, {
|
||||
opacity: 0, duration: 0.4, ease: "power1.in"
|
||||
}, launchT + dragDelay + s.dur - 0.4);
|
||||
}
|
||||
});
|
||||
|
||||
// Title moving hold: subtle breathing after it settles (from ~21.8s onward)
|
||||
const titleSettleTime = slamTime + 1.2 + 0.08;
|
||||
tl.to("#title-text", {
|
||||
scaleY: 1.005, scaleX: 0.995,
|
||||
duration: 3, ease: "sine.inOut",
|
||||
yoyo: true, repeat: 3
|
||||
}, titleSettleTime);
|
||||
|
||||
tl.to("#title-shadow", {
|
||||
y: 2, opacity: 0.55,
|
||||
duration: 3, ease: "sine.inOut",
|
||||
yoyo: true, repeat: 3
|
||||
}, titleSettleTime + 0.06);
|
||||
|
||||
tl.to("#title-glow", {
|
||||
opacity: 0.5,
|
||||
duration: 3, ease: "sine.inOut",
|
||||
yoyo: true, repeat: 3
|
||||
}, titleSettleTime + 0.08);
|
||||
|
||||
// Subtitle enters — proportional exaggeration (slight scale overshoot matching title's world)
|
||||
tl.fromTo("#subtitle",
|
||||
{ opacity: 0, y: 15, scaleX: 1.06 },
|
||||
{ opacity: 0.7, y: 0, scaleX: 1, duration: 1.2, ease: "power3.out" },
|
||||
slamTime + 1.6
|
||||
);
|
||||
|
||||
// Subtitle moving hold
|
||||
tl.to("#subtitle", {
|
||||
y: -2, opacity: 0.65,
|
||||
duration: 3, ease: "sine.inOut",
|
||||
yoyo: true, repeat: 3
|
||||
}, slamTime + 2.8);
|
||||
|
||||
// ============================================================
|
||||
// PHASE 3: CALM AFTERMATH (28s - 45s)
|
||||
// Mood: Calm. Floating debris, deep arcs, perpetual moving holds.
|
||||
// Transition: Soft dissolve from impact energy into serenity
|
||||
// ============================================================
|
||||
|
||||
const phase3Start = 28;
|
||||
|
||||
// Vignette softens
|
||||
tl.to("#vignette", {
|
||||
opacity: 0.4, duration: 3, ease: "sine.inOut"
|
||||
}, phase3Start);
|
||||
|
||||
// Background pulse settles to gentle warmth
|
||||
tl.to("#bg-pulse", {
|
||||
opacity: 0.15, duration: 3, ease: "sine.inOut"
|
||||
}, phase3Start);
|
||||
|
||||
// Title glow dims to soft
|
||||
tl.to("#title-glow", {
|
||||
opacity: 0.3, duration: 3, ease: "sine.inOut"
|
||||
}, phase3Start);
|
||||
|
||||
// Floating debris — each on deep, curved arcs (slow = deep arcs)
|
||||
// These drift perpetually with moving holds
|
||||
const debrisData = [
|
||||
{ id: "#debris-0", startAt: 28, driftX: 80, driftY: -40, arcY: -25, dur: 12 },
|
||||
{ id: "#debris-1", startAt: 28.4, driftX: -60, driftY: -50, arcY: -30, dur: 9 },
|
||||
{ id: "#debris-2", startAt: 29.2, driftX: 50, driftY: -35, arcY: -20, dur: 12 },
|
||||
{ id: "#debris-3", startAt: 28.8, driftX: -70, driftY: -45, arcY: -28, dur: 9 },
|
||||
{ id: "#debris-4", startAt: 30, driftX: 90, driftY: -30, arcY: -18, dur: 12 },
|
||||
{ id: "#debris-5", startAt: 29.6, driftX: -40, driftY: -55, arcY: -35, dur: 9 },
|
||||
{ id: "#debris-6", startAt: 30.4, driftX: 65, driftY: -25, arcY: -15, dur: 12 },
|
||||
{ id: "#debris-7", startAt: 28.4, driftX: -55, driftY: -40, arcY: -22, dur: 9 },
|
||||
{ id: "#debris-8", startAt: 31.2, driftX: 45, driftY: -50, arcY: -30, dur: 12 },
|
||||
{ id: "#debris-9", startAt: 29.2, driftX: -75, driftY: -35, arcY: -20, dur: 9 },
|
||||
{ id: "#debris-10", startAt: 30, driftX: 55, driftY: -45, arcY: -25, dur: 12 },
|
||||
{ id: "#debris-11", startAt: 30.8, driftX: -50, driftY: -30, arcY: -18, dur: 9 },
|
||||
{ id: "#debris-12", startAt: 31.2, driftX: 35, driftY: -55, arcY: -32, dur: 12 },
|
||||
{ id: "#debris-13", startAt: 29.6, driftX: -80, driftY: -40, arcY: -24, dur: 9 },
|
||||
{ id: "#debris-14", startAt: 30.4, driftX: 60, driftY: -35, arcY: -20, dur: 12 },
|
||||
{ id: "#debris-15", startAt: 31.6, driftX: -45, driftY: -50, arcY: -28, dur: 9 }
|
||||
];
|
||||
|
||||
debrisData.forEach((d) => {
|
||||
// Fade in gently
|
||||
tl.to(d.id, {
|
||||
opacity: 0.3, duration: 3, ease: "sine.inOut"
|
||||
}, d.startAt);
|
||||
|
||||
// Deep arc drift — x and y as separate tweens for arc shape
|
||||
// Horizontal: slow continuous
|
||||
tl.to(d.id, {
|
||||
x: d.driftX, duration: d.dur, ease: "sine.inOut",
|
||||
yoyo: true, repeat: 3
|
||||
}, d.startAt + 1.2);
|
||||
|
||||
// Vertical: slower, deeper arc (different duration creates curve)
|
||||
tl.to(d.id, {
|
||||
y: d.driftY, duration: d.dur * 0.8, ease: "sine.inOut",
|
||||
yoyo: true, repeat: 3
|
||||
}, d.startAt + 1.2);
|
||||
|
||||
// Subtle rotation drift — nothing moves in a straight line
|
||||
tl.to(d.id, {
|
||||
rotation: d.driftX > 0 ? 15 : -15,
|
||||
duration: d.dur * 1.2, ease: "sine.inOut",
|
||||
yoyo: true, repeat: 3
|
||||
}, d.startAt + 1.2);
|
||||
});
|
||||
|
||||
// Final fade: everything gently recedes at the very end
|
||||
// Title fades last (it has the most mass, the most presence)
|
||||
tl.to(".debris", {
|
||||
opacity: 0, duration: 3, ease: "sine.in"
|
||||
}, 42);
|
||||
|
||||
tl.to("#subtitle", {
|
||||
opacity: 0, y: 10, duration: 3, ease: "sine.in"
|
||||
}, 41.5);
|
||||
|
||||
tl.to("#title-glow", {
|
||||
opacity: 0, duration: 3, ease: "sine.inOut"
|
||||
}, 42);
|
||||
|
||||
tl.to("#title-shadow", {
|
||||
opacity: 0, duration: 3, ease: "sine.inOut"
|
||||
}, 42);
|
||||
|
||||
tl.to("#title-text", {
|
||||
opacity: 0, duration: 3, ease: "sine.in"
|
||||
}, 42.5);
|
||||
|
||||
tl.to("#vignette", {
|
||||
opacity: 0, duration: 3, ease: "sine.in"
|
||||
}, 42);
|
||||
|
||||
tl.to("#bg-pulse", {
|
||||
opacity: 0, duration: 3, ease: "sine.in"
|
||||
}, 42);
|
||||
|
||||
// Ensure timeline reaches 45s
|
||||
tl.set({}, {}, 45);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,702 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=1920, height=1080">
|
||||
<title>SHATTERED - Thriller Title Sequence</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { background: #000; display: flex; justify-content: center; align-items: center; min-height: 100vh; }
|
||||
|
||||
.composition {
|
||||
background: #0a0a0a;
|
||||
font-family: 'Arial Black', 'Helvetica Neue', Arial, sans-serif;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Phase 1: Tense - Drifting text fragments in darkness */
|
||||
.fragment {
|
||||
position: absolute;
|
||||
font-size: 18px;
|
||||
letter-spacing: 4px;
|
||||
text-transform: uppercase;
|
||||
opacity: 0;
|
||||
color: rgba(255, 255, 255, 0.4);
|
||||
}
|
||||
.fragment.red { color: rgba(180, 30, 30, 0.5); }
|
||||
.fragment-1 { top: 200px; left: 300px; font-size: 14px; }
|
||||
.fragment-2 { top: 480px; left: 1200px; font-size: 16px; }
|
||||
.fragment-3 { top: 650px; left: 500px; font-size: 12px; }
|
||||
.fragment-4 { top: 350px; left: 900px; font-size: 20px; }
|
||||
.fragment-5 { top: 750px; left: 150px; font-size: 15px; }
|
||||
.fragment-6 { top: 150px; left: 1400px; font-size: 13px; }
|
||||
.fragment-7 { top: 550px; left: 700px; font-size: 17px; }
|
||||
.fragment-8 { top: 880px; left: 1000px; font-size: 11px; }
|
||||
|
||||
/* Tension line */
|
||||
.tension-line {
|
||||
position: absolute;
|
||||
top: 540px;
|
||||
left: 0;
|
||||
width: 1920px;
|
||||
height: 1px;
|
||||
background: rgba(180, 30, 30, 0.3);
|
||||
transform: scaleX(0);
|
||||
transform-origin: center;
|
||||
}
|
||||
|
||||
/* Phase 2: Punchy - Title reveal */
|
||||
.title-container {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 10;
|
||||
}
|
||||
.title-text {
|
||||
font-size: 180px;
|
||||
font-weight: 900;
|
||||
letter-spacing: 30px;
|
||||
text-transform: uppercase;
|
||||
color: #fff;
|
||||
opacity: 0;
|
||||
text-align: center;
|
||||
line-height: 1;
|
||||
text-shadow: 0 0 60px rgba(180, 30, 30, 0.8), 0 0 120px rgba(180, 30, 30, 0.4);
|
||||
}
|
||||
|
||||
/* Glass shards */
|
||||
.shard {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
z-index: 5;
|
||||
}
|
||||
.shard svg { width: 100%; height: 100%; }
|
||||
.shard-1 { top: 300px; left: 400px; width: 80px; height: 120px; }
|
||||
.shard-2 { top: 200px; left: 800px; width: 60px; height: 90px; }
|
||||
.shard-3 { top: 500px; left: 1200px; width: 100px; height: 70px; }
|
||||
.shard-4 { top: 600px; left: 600px; width: 50px; height: 110px; }
|
||||
.shard-5 { top: 150px; left: 1000px; width: 70px; height: 80px; }
|
||||
.shard-6 { top: 700px; left: 350px; width: 90px; height: 60px; }
|
||||
.shard-7 { top: 400px; left: 1400px; width: 55px; height: 100px; }
|
||||
.shard-8 { top: 800px; left: 900px; width: 75px; height: 85px; }
|
||||
.shard-9 { top: 250px; left: 200px; width: 65px; height: 95px; }
|
||||
.shard-10 { top: 650px; left: 1100px; width: 85px; height: 55px; }
|
||||
.shard-11 { top: 350px; left: 700px; width: 45px; height: 75px; }
|
||||
.shard-12 { top: 500px; left: 300px; width: 70px; height: 65px; }
|
||||
|
||||
/* Impact flash */
|
||||
.impact-flash {
|
||||
position: absolute;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
background: radial-gradient(ellipse at center, rgba(255,255,255,0.9) 0%, rgba(180,30,30,0.3) 40%, transparent 70%);
|
||||
opacity: 0;
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
/* Red pulse ring */
|
||||
.pulse-ring {
|
||||
position: absolute;
|
||||
top: 50%; left: 50%;
|
||||
width: 200px; height: 200px;
|
||||
border: 2px solid rgba(180, 30, 30, 0.6);
|
||||
border-radius: 50%;
|
||||
transform: translate(-50%, -50%) scale(0);
|
||||
opacity: 0;
|
||||
z-index: 8;
|
||||
}
|
||||
|
||||
/* Phase 3: Calm - Floating debris */
|
||||
.debris {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
z-index: 3;
|
||||
}
|
||||
.debris-dot {
|
||||
width: 4px; height: 4px;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
border-radius: 50%;
|
||||
}
|
||||
.debris-line {
|
||||
width: 30px; height: 1px;
|
||||
background: rgba(180, 30, 30, 0.2);
|
||||
transform-origin: left center;
|
||||
}
|
||||
.debris-shard-small {
|
||||
width: 8px; height: 12px;
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
clip-path: polygon(50% 0%, 100% 100%, 0% 80%);
|
||||
}
|
||||
|
||||
.debris-1 { top: 200px; left: 300px; }
|
||||
.debris-2 { top: 400px; left: 700px; }
|
||||
.debris-3 { top: 600px; left: 1100px; }
|
||||
.debris-4 { top: 300px; left: 1500px; }
|
||||
.debris-5 { top: 750px; left: 400px; }
|
||||
.debris-6 { top: 150px; left: 900px; }
|
||||
.debris-7 { top: 850px; left: 1300px; }
|
||||
.debris-8 { top: 500px; left: 200px; }
|
||||
.debris-9 { top: 350px; left: 1050px; }
|
||||
.debris-10 { top: 680px; left: 600px; }
|
||||
.debris-11 { top: 100px; left: 1600px; }
|
||||
.debris-12 { top: 900px; left: 800px; }
|
||||
.debris-13 { top: 450px; left: 1400px; }
|
||||
.debris-14 { top: 250px; left: 500px; }
|
||||
.debris-15 { top: 780px; left: 1000px; }
|
||||
|
||||
/* Settled title for Phase 3 */
|
||||
.title-settled {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
font-size: 160px;
|
||||
font-weight: 900;
|
||||
letter-spacing: 25px;
|
||||
text-transform: uppercase;
|
||||
color: rgba(255, 255, 255, 0.7);
|
||||
opacity: 0;
|
||||
z-index: 6;
|
||||
text-shadow: 0 0 40px rgba(180, 30, 30, 0.3);
|
||||
}
|
||||
|
||||
/* Subtitle */
|
||||
.subtitle {
|
||||
position: absolute;
|
||||
top: calc(50% + 110px);
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 18px;
|
||||
letter-spacing: 12px;
|
||||
text-transform: uppercase;
|
||||
color: rgba(180, 30, 30, 0.5);
|
||||
opacity: 0;
|
||||
z-index: 6;
|
||||
}
|
||||
|
||||
/* Ambient vignette */
|
||||
.vignette {
|
||||
position: absolute;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
background: radial-gradient(ellipse at center, transparent 40%, rgba(0,0,0,0.7) 100%);
|
||||
z-index: 1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Dark overlay for tension */
|
||||
.dark-overlay {
|
||||
position: absolute;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
background: rgba(0, 0, 0, 0.3);
|
||||
opacity: 0;
|
||||
z-index: 2;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="composition" data-composition-id="high-contrast-arc-shattered" style="width: 1920px; height: 1080px; position: relative; overflow: hidden;">
|
||||
|
||||
<!-- Ambient vignette (always visible) -->
|
||||
<div class="vignette"></div>
|
||||
<div class="dark-overlay"></div>
|
||||
|
||||
<!-- Phase 1: Tense - Text fragments drifting in darkness -->
|
||||
<div class="fragment fragment-1">no escape</div>
|
||||
<div class="fragment fragment-2 red">the truth</div>
|
||||
<div class="fragment fragment-3">beneath the surface</div>
|
||||
<div class="fragment fragment-4 red">broken</div>
|
||||
<div class="fragment fragment-5">who are you</div>
|
||||
<div class="fragment fragment-6">silence</div>
|
||||
<div class="fragment fragment-7 red">shattered</div>
|
||||
<div class="fragment fragment-8">in the dark</div>
|
||||
|
||||
<div class="tension-line"></div>
|
||||
|
||||
<!-- Phase 2: Punchy - Title reveal with glass shatter -->
|
||||
<div class="impact-flash"></div>
|
||||
|
||||
<div class="title-container">
|
||||
<div class="title-text">SHATTERED</div>
|
||||
</div>
|
||||
|
||||
<div class="pulse-ring"></div>
|
||||
|
||||
<!-- Glass shards (SVG for angular shattering glass shapes) -->
|
||||
<div class="shard shard-1"><svg viewBox="0 0 80 120"><polygon points="40,0 80,70 50,120 0,90" fill="rgba(255,255,255,0.15)" stroke="rgba(255,255,255,0.3)" stroke-width="0.5"/></svg></div>
|
||||
<div class="shard shard-2"><svg viewBox="0 0 60 90"><polygon points="30,0 60,50 40,90 0,60" fill="rgba(180,30,30,0.1)" stroke="rgba(180,30,30,0.3)" stroke-width="0.5"/></svg></div>
|
||||
<div class="shard shard-3"><svg viewBox="0 0 100 70"><polygon points="50,0 100,40 70,70 0,50" fill="rgba(255,255,255,0.12)" stroke="rgba(255,255,255,0.25)" stroke-width="0.5"/></svg></div>
|
||||
<div class="shard shard-4"><svg viewBox="0 0 50 110"><polygon points="25,0 50,60 30,110 0,80" fill="rgba(180,30,30,0.08)" stroke="rgba(180,30,30,0.2)" stroke-width="0.5"/></svg></div>
|
||||
<div class="shard shard-5"><svg viewBox="0 0 70 80"><polygon points="35,0 70,45 50,80 0,60" fill="rgba(255,255,255,0.1)" stroke="rgba(255,255,255,0.2)" stroke-width="0.5"/></svg></div>
|
||||
<div class="shard shard-6"><svg viewBox="0 0 90 60"><polygon points="45,0 90,35 60,60 0,40" fill="rgba(255,255,255,0.13)" stroke="rgba(255,255,255,0.25)" stroke-width="0.5"/></svg></div>
|
||||
<div class="shard shard-7"><svg viewBox="0 0 55 100"><polygon points="28,0 55,55 35,100 0,75" fill="rgba(180,30,30,0.1)" stroke="rgba(180,30,30,0.25)" stroke-width="0.5"/></svg></div>
|
||||
<div class="shard shard-8"><svg viewBox="0 0 75 85"><polygon points="38,0 75,50 55,85 0,60" fill="rgba(255,255,255,0.11)" stroke="rgba(255,255,255,0.22)" stroke-width="0.5"/></svg></div>
|
||||
<div class="shard shard-9"><svg viewBox="0 0 65 95"><polygon points="33,0 65,55 45,95 0,70" fill="rgba(255,255,255,0.09)" stroke="rgba(255,255,255,0.2)" stroke-width="0.5"/></svg></div>
|
||||
<div class="shard shard-10"><svg viewBox="0 0 85 55"><polygon points="43,0 85,30 60,55 0,40" fill="rgba(180,30,30,0.12)" stroke="rgba(180,30,30,0.25)" stroke-width="0.5"/></svg></div>
|
||||
<div class="shard shard-11"><svg viewBox="0 0 45 75"><polygon points="23,0 45,40 30,75 0,55" fill="rgba(255,255,255,0.08)" stroke="rgba(255,255,255,0.18)" stroke-width="0.5"/></svg></div>
|
||||
<div class="shard shard-12"><svg viewBox="0 0 70 65"><polygon points="35,0 70,35 50,65 0,48" fill="rgba(255,255,255,0.1)" stroke="rgba(255,255,255,0.2)" stroke-width="0.5"/></svg></div>
|
||||
|
||||
<!-- Phase 3: Calm - Floating debris -->
|
||||
<div class="debris debris-1"><div class="debris-dot"></div></div>
|
||||
<div class="debris debris-2"><div class="debris-shard-small"></div></div>
|
||||
<div class="debris debris-3"><div class="debris-line"></div></div>
|
||||
<div class="debris debris-4"><div class="debris-dot"></div></div>
|
||||
<div class="debris debris-5"><div class="debris-shard-small"></div></div>
|
||||
<div class="debris debris-6"><div class="debris-line"></div></div>
|
||||
<div class="debris debris-7"><div class="debris-dot"></div></div>
|
||||
<div class="debris debris-8"><div class="debris-shard-small"></div></div>
|
||||
<div class="debris debris-9"><div class="debris-line"></div></div>
|
||||
<div class="debris debris-10"><div class="debris-dot"></div></div>
|
||||
<div class="debris debris-11"><div class="debris-shard-small"></div></div>
|
||||
<div class="debris debris-12"><div class="debris-line"></div></div>
|
||||
<div class="debris debris-13"><div class="debris-dot"></div></div>
|
||||
<div class="debris debris-14"><div class="debris-shard-small"></div></div>
|
||||
<div class="debris debris-15"><div class="debris-line"></div></div>
|
||||
|
||||
<!-- Settled title for calm phase -->
|
||||
<div class="title-settled">SHATTERED</div>
|
||||
<div class="subtitle">a thriller</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- GSAP 3.14.2 CDN -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/CustomEase.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/CustomWiggle.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/CustomBounce.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/SplitText.min.js"></script>
|
||||
<script>
|
||||
// Register ALL plugins before use
|
||||
gsap.registerPlugin(CustomEase, CustomWiggle, CustomBounce, SplitText);
|
||||
|
||||
// =========================================================================
|
||||
// PHASE DECOMPOSITION (from emotion-to-motion skill)
|
||||
// =========================================================================
|
||||
// Phase 1: TENSE (0s - 15s) — creeping unease
|
||||
// Entrance: slow reveal (25% = 3.75s)
|
||||
// Emphasis: tension hold w/ CustomWiggle micro-tremor (50% = 7.5s)
|
||||
// Exit: compress (25% = 3.75s)
|
||||
//
|
||||
// Transition: tense -> punchy = HARD (0.5s overlap)
|
||||
//
|
||||
// Phase 2: PUNCHY (14.5s - 24.5s) — explosive reveal
|
||||
// Entrance: impact (15% = 1.5s), SplitText chars
|
||||
// Emphasis: pulse (60% = 6s)
|
||||
// Exit: snap out (25% = 2.5s)
|
||||
//
|
||||
// Transition: punchy -> calm = HARD (0.5s overlap)
|
||||
//
|
||||
// Phase 3: CALM (24s - 44s) — aftermath with floating debris
|
||||
// Entrance: ensemble (20% = 4s)
|
||||
// Emphasis: drift (65% = 13s)
|
||||
// Exit: fade recede (15% = 3s)
|
||||
// =========================================================================
|
||||
|
||||
// Custom eases (Principle 6: Slow In, Slow Out — easing is non-negotiable)
|
||||
// Tense: creeping ease — barely moves for 80%, then rushes (tense recipe)
|
||||
CustomEase.create("creepingDread", "M0,0 C0.05,0.01 0.1,0.02 0.2,0.03 0.4,0.05 0.6,0.08 0.75,0.15 0.85,0.3 0.92,0.6 1,1");
|
||||
|
||||
// Micro-tremor for tension hold (tense emphasis: 14 wiggles, uniform type)
|
||||
CustomWiggle.create("microTremor", { wiggles: 14, type: "uniform" });
|
||||
|
||||
// =========================================================================
|
||||
// MASTER TIMELINE
|
||||
// =========================================================================
|
||||
const tl = gsap.timeline({ paused: true });
|
||||
|
||||
// Register for HyperFrames
|
||||
window.__timelines = window.__timelines || {};
|
||||
window.__timelines["high-contrast-arc-shattered"] = tl;
|
||||
|
||||
// =========================================================================
|
||||
// PHASE 1: TENSE (0s - 15s)
|
||||
// Mood recipe: creeping motion, subtle tremor, no release, stagger from end
|
||||
// =========================================================================
|
||||
const phase1 = gsap.timeline();
|
||||
|
||||
// --- Phase 1 Entrance: Slow Reveal (0s - 3.75s) ---
|
||||
// Principle 3 (Staging): elements emerge one at a time with long pauses
|
||||
// Principle 9 (Timing): 1-2s durations = slow, cinematic
|
||||
// Tense recipe: stagger from end — things approach from the unknown
|
||||
var fragments = gsap.utils.toArray(".fragment");
|
||||
|
||||
// Dark overlay creeps in (Principle 8: Secondary Action reinforces mood)
|
||||
phase1.to(".dark-overlay", {
|
||||
opacity: 0.5, duration: 3, ease: "creepingDread"
|
||||
}, 0);
|
||||
|
||||
// Fragments emerge one at a time from end (tense: stagger from end)
|
||||
fragments.forEach(function(el, i) {
|
||||
var reverseIndex = fragments.length - 1 - i;
|
||||
var startTime = reverseIndex * 0.45;
|
||||
// Principle 7 (Arcs): slight curved drift, not straight-line
|
||||
phase1.fromTo(el,
|
||||
{ opacity: 0, y: 15, x: -5 },
|
||||
{ opacity: 0.4, y: 0, x: 0, duration: 1.2, ease: "power2.out" },
|
||||
startTime
|
||||
);
|
||||
});
|
||||
|
||||
// --- Phase 1 Emphasis: Tension Hold (3.75s - 11.25s) ---
|
||||
// Skill pattern: near-stillness with micro-tremor on one element
|
||||
// CustomWiggle 14 wiggles, uniform — no bounce, no elastic (tension doesn't resolve)
|
||||
var emphasisStart1 = 3.75;
|
||||
var emphasisDuration1 = 7.5;
|
||||
|
||||
// Micro-tremor on the key "shattered" fragment (tension hold emphasis pattern)
|
||||
phase1.to(".fragment-7", {
|
||||
x: 2, ease: "microTremor", duration: emphasisDuration1
|
||||
}, emphasisStart1);
|
||||
|
||||
// Very slow drift on other fragments (Principle 11: Solid Drawing — parallax depth)
|
||||
fragments.forEach(function(el, i) {
|
||||
var driftAmount = (i % 2 === 0) ? -3 : 3;
|
||||
var driftY = (i % 3 === 0) ? -2 : 1;
|
||||
phase1.to(el, {
|
||||
x: "+=" + driftAmount,
|
||||
y: "+=" + driftY,
|
||||
duration: emphasisDuration1,
|
||||
ease: "sine.inOut"
|
||||
}, emphasisStart1);
|
||||
});
|
||||
|
||||
// Tension line crawls across (creeping ease — barely moves, then rushes)
|
||||
phase1.to(".tension-line", {
|
||||
scaleX: 1, duration: emphasisDuration1, ease: "creepingDread"
|
||||
}, emphasisStart1);
|
||||
|
||||
// Subtle red pulse on tension line (Principle 8: Secondary Action)
|
||||
phase1.to(".tension-line", {
|
||||
opacity: 0.6, duration: 1.25, ease: "sine.inOut",
|
||||
yoyo: true, repeat: Math.floor(emphasisDuration1 / 2.5)
|
||||
}, emphasisStart1);
|
||||
|
||||
// --- Phase 1 Exit: Compress (11.25s - 15s) ---
|
||||
// Tense exit pattern: elements slowly contract inward before next scene hits
|
||||
var exitStart1 = 11.25;
|
||||
var exitDuration1 = 3.75;
|
||||
|
||||
phase1.to(".fragment", {
|
||||
scale: 0.9, x: 0, y: 0, opacity: 0.15,
|
||||
duration: exitDuration1, ease: "power4.in",
|
||||
stagger: 0.05
|
||||
}, exitStart1);
|
||||
|
||||
phase1.to(".tension-line", {
|
||||
opacity: 0.8, scaleY: 2, duration: exitDuration1, ease: "power4.in"
|
||||
}, exitStart1);
|
||||
|
||||
phase1.to(".dark-overlay", {
|
||||
opacity: 0.8, duration: exitDuration1, ease: "power3.in"
|
||||
}, exitStart1);
|
||||
|
||||
// Add Phase 1 to master
|
||||
tl.add(phase1, 0);
|
||||
|
||||
// =========================================================================
|
||||
// TRANSITION: tense -> punchy = HARD (0.3-0.8s overlap)
|
||||
// Matrix lookup: tense->punchy = hard
|
||||
// Technique: Phase 1 snaps out, Phase 2 slams in simultaneously
|
||||
// =========================================================================
|
||||
var t1Overlap = 0.5;
|
||||
|
||||
// =========================================================================
|
||||
// PHASE 2: PUNCHY (14.5s - 24.5s)
|
||||
// Mood recipe: hard stops, squash on impact, rhythmic timing (0.04-0.08s stagger),
|
||||
// minimal follow-through — things STOP
|
||||
// =========================================================================
|
||||
var phase2Start = 15 - t1Overlap;
|
||||
var phase2 = gsap.timeline();
|
||||
|
||||
// --- Phase 2 Entrance: Impact (0s - 1.5s) ---
|
||||
// Punchy entrance: hero element hits first and HARD, supporting elements follow
|
||||
// Principle 1 (Squash & Stretch): squash on impact, settle to 1
|
||||
// Principle 2 (Anticipation): brief screen compress before the slam
|
||||
// Principle 10 (Exaggeration): scale 1.4 overshoot for impact
|
||||
|
||||
// Hard snap-out of remaining Phase 1 elements (hard transition)
|
||||
phase2.to(".fragment", {
|
||||
opacity: 0, scale: 0.95, duration: 0.15, ease: "power3.in"
|
||||
}, 0);
|
||||
phase2.to(".tension-line", {
|
||||
opacity: 0, duration: 0.1, ease: "none"
|
||||
}, 0);
|
||||
|
||||
// Brief anticipation: screen goes fully dark (Principle 2)
|
||||
phase2.to(".dark-overlay", {
|
||||
opacity: 1, duration: 0.2, ease: "power2.in"
|
||||
}, 0);
|
||||
|
||||
// Impact flash — instantaneous white burst (Principle 10: Exaggeration)
|
||||
phase2.to(".impact-flash", {
|
||||
opacity: 1, duration: 0.06, ease: "power4.out"
|
||||
}, 0.3);
|
||||
phase2.to(".impact-flash", {
|
||||
opacity: 0, duration: 0.5, ease: "power3.out"
|
||||
}, 0.36);
|
||||
|
||||
// SplitText on title for per-character reveal (requested plugin)
|
||||
var titleSplit = new SplitText(".title-text", { type: "chars" });
|
||||
var titleChars = titleSplit.chars;
|
||||
|
||||
// Title container visible
|
||||
phase2.set(".title-text", { opacity: 1 }, 0.3);
|
||||
|
||||
// Characters SLAM in with squash-stretch (Principle 1)
|
||||
// Punchy recipe: rhythmic 0.04s stagger, from center for radial impact
|
||||
phase2.fromTo(titleChars,
|
||||
{ opacity: 0, scaleY: 0.5, scaleX: 1.3, y: 15 },
|
||||
{
|
||||
opacity: 1, scaleY: 1, scaleX: 1, y: 0,
|
||||
duration: 0.15, ease: "power4.out",
|
||||
stagger: { each: 0.04, from: "center" }
|
||||
},
|
||||
0.3
|
||||
);
|
||||
|
||||
// Dark overlay clears after impact
|
||||
phase2.to(".dark-overlay", {
|
||||
opacity: 0, duration: 0.4, ease: "power2.out"
|
||||
}, 0.4);
|
||||
|
||||
// Glass shards explode outward (Principle 5: Follow-through, Principle 8: Secondary Action)
|
||||
// Rhythmic stagger = punchy recipe (consistent 0.04s beat)
|
||||
var shards = gsap.utils.toArray(".shard");
|
||||
|
||||
shards.forEach(function(shard, i) {
|
||||
var angle = (i / shards.length) * Math.PI * 2;
|
||||
var distance = 80 + ((i * 17) % 120);
|
||||
var xBurst = Math.cos(angle) * distance;
|
||||
var yBurst = Math.sin(angle) * distance;
|
||||
var rot = ((i % 2 === 0) ? 1 : -1) * (15 + (i * 5) % 45);
|
||||
|
||||
// Initial burst outward
|
||||
phase2.fromTo(shard,
|
||||
{ opacity: 0, scale: 0.3 },
|
||||
{
|
||||
opacity: 0.7, scale: 1,
|
||||
x: xBurst * 0.3, y: yBurst * 0.3,
|
||||
rotation: rot * 0.3,
|
||||
duration: 0.15, ease: "power4.out"
|
||||
},
|
||||
0.34 + i * 0.04
|
||||
);
|
||||
});
|
||||
|
||||
// Pulse ring expands from center (Principle 8: Secondary Action reinforces impact)
|
||||
phase2.to(".pulse-ring", {
|
||||
opacity: 0.6, scale: 8, duration: 0.6, ease: "power3.out"
|
||||
}, 0.32);
|
||||
phase2.to(".pulse-ring", {
|
||||
opacity: 0, duration: 0.4, ease: "power2.out"
|
||||
}, 0.92);
|
||||
|
||||
// --- Phase 2 Emphasis: Pulse (1.5s - 7.5s) ---
|
||||
// Punchy emphasis: subtle scale oscillation on key element
|
||||
// Principle 9 (Timing): consistent rhythmic beat
|
||||
var emphasisStart2 = 1.5;
|
||||
var emphasisDuration2 = 6;
|
||||
|
||||
// Title pulse — subtle scale throb (punchy pulse pattern)
|
||||
var pulseRepeat2 = Math.floor(emphasisDuration2 / 0.8) - 1;
|
||||
phase2.to(".title-text", {
|
||||
scale: 1.015, duration: 0.4, ease: "sine.inOut",
|
||||
yoyo: true, repeat: pulseRepeat2
|
||||
}, emphasisStart2);
|
||||
|
||||
// Red glow throb on title (Principle 8: Secondary Action)
|
||||
phase2.to(".title-text", {
|
||||
textShadow: "0 0 80px rgba(180,30,30,1), 0 0 160px rgba(180,30,30,0.6)",
|
||||
duration: 0.6, ease: "sine.inOut",
|
||||
yoyo: true, repeat: Math.floor(emphasisDuration2 / 1.2) - 1
|
||||
}, emphasisStart2);
|
||||
|
||||
// Shards continue slow drift outward during emphasis (Principle 5: Follow-through)
|
||||
shards.forEach(function(shard, i) {
|
||||
var angle = (i / shards.length) * Math.PI * 2;
|
||||
var distance = 80 + ((i * 17) % 120);
|
||||
var xDrift = Math.cos(angle) * distance;
|
||||
var yDrift = Math.sin(angle) * distance;
|
||||
var rot = ((i % 2 === 0) ? 1 : -1) * (15 + (i * 5) % 45);
|
||||
|
||||
phase2.to(shard, {
|
||||
x: xDrift, y: yDrift, rotation: rot,
|
||||
duration: emphasisDuration2, ease: "power1.out"
|
||||
}, emphasisStart2);
|
||||
});
|
||||
|
||||
// --- Phase 2 Exit: Snap Out (7.5s - 10s) ---
|
||||
// Punchy exit: hard opacity to 0 — just gone. No ease out.
|
||||
var exitStart2 = 7.5;
|
||||
|
||||
// Title snaps out (snap out pattern: duration 0.08, ease "none")
|
||||
phase2.to(".title-text", {
|
||||
opacity: 0, duration: 0.08, ease: "none"
|
||||
}, exitStart2);
|
||||
|
||||
// Shards snap out in quick rhythmic succession
|
||||
phase2.to(".shard", {
|
||||
opacity: 0, duration: 0.08, ease: "none",
|
||||
stagger: 0.03
|
||||
}, exitStart2 + 0.1);
|
||||
|
||||
// Darkness slams back in (inverted flash for hard transition out)
|
||||
phase2.to(".dark-overlay", {
|
||||
opacity: 0.6, duration: 0.1, ease: "power4.out"
|
||||
}, exitStart2);
|
||||
phase2.to(".dark-overlay", {
|
||||
opacity: 0.2, duration: 1.5, ease: "sine.out"
|
||||
}, exitStart2 + 0.1);
|
||||
|
||||
// Add Phase 2 to master at overlap position
|
||||
tl.add(phase2, phase2Start);
|
||||
|
||||
// =========================================================================
|
||||
// TRANSITION: punchy -> calm = HARD (0.3-0.8s overlap)
|
||||
// Matrix lookup: punchy->calm = hard
|
||||
// =========================================================================
|
||||
var t2Overlap = 0.5;
|
||||
|
||||
// =========================================================================
|
||||
// PHASE 3: CALM (24s - 44s)
|
||||
// Mood recipe: slow and continuous (1.5-3s durations), sine.inOut everything,
|
||||
// no sudden movements, generous spacing (30%+ gap),
|
||||
// breathing motion for holds
|
||||
// =========================================================================
|
||||
var phase3Start = phase2Start + 10 - t2Overlap;
|
||||
var phase3 = gsap.timeline();
|
||||
|
||||
// --- Phase 3 Entrance: Ensemble (0s - 4s) ---
|
||||
// Calm entrance: elements fade in together in 2-3 groups
|
||||
// Principle 3 (Staging): 20-30% overlap between groups — elements breathe
|
||||
// Principle 4 (Pose to Pose): two states + right ease = enough
|
||||
|
||||
// Group 1: settled title emerges softly
|
||||
phase3.fromTo(".title-settled",
|
||||
{ opacity: 0, y: 10, scale: 0.98 },
|
||||
{ opacity: 0.7, y: 0, scale: 1, duration: 2.5, ease: "sine.inOut" },
|
||||
0
|
||||
);
|
||||
|
||||
// Group 2: subtitle follows with slight offset (ensemble: feels unified)
|
||||
phase3.fromTo(".subtitle",
|
||||
{ opacity: 0, y: 8 },
|
||||
{ opacity: 0.5, y: 0, duration: 2, ease: "sine.inOut" },
|
||||
0.8
|
||||
);
|
||||
|
||||
// Group 3: debris particles appear (30%+ gap between groups = calm recipe)
|
||||
var debris = gsap.utils.toArray(".debris");
|
||||
|
||||
// First debris group (even indices)
|
||||
debris.forEach(function(el, i) {
|
||||
if (i % 2 === 0) {
|
||||
phase3.fromTo(el,
|
||||
{ opacity: 0, y: 5 },
|
||||
{ opacity: 0.6, y: 0, duration: 1.5, ease: "sine.inOut" },
|
||||
1.5 + i * 0.12
|
||||
);
|
||||
}
|
||||
});
|
||||
// Second debris group (odd indices)
|
||||
debris.forEach(function(el, i) {
|
||||
if (i % 2 !== 0) {
|
||||
phase3.fromTo(el,
|
||||
{ opacity: 0, y: 5 },
|
||||
{ opacity: 0.5, y: 0, duration: 1.5, ease: "sine.inOut" },
|
||||
2.2 + i * 0.1
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
// Dark overlay settles to subtle
|
||||
phase3.to(".dark-overlay", {
|
||||
opacity: 0.1, duration: 3, ease: "sine.inOut"
|
||||
}, 0);
|
||||
|
||||
// --- Phase 3 Emphasis: Drift (4s - 17s) ---
|
||||
// Calm emphasis: slow continuous motion, gentle float, parallax shift
|
||||
// Principle 7 (Arcs): curved paths on debris for organic feel
|
||||
// Principle 12 (Appeal): nothing jarring, motion matches serene aftermath
|
||||
var emphasisStart3 = 4;
|
||||
var emphasisDuration3 = 13;
|
||||
|
||||
// Debris gentle drift (Principle 11: Solid Drawing — parallax layers create depth)
|
||||
debris.forEach(function(el, i) {
|
||||
var driftX = (i % 3 === 0) ? -15 : (i % 3 === 1) ? 12 : -8;
|
||||
var driftY = (i % 2 === 0) ? -10 : 8;
|
||||
var rotDrift = (i % 2 === 0) ? 15 : -10;
|
||||
var speed = emphasisDuration3 * (0.8 + (i % 4) * 0.1);
|
||||
|
||||
// Slow continuous float (calm: 1.5-3s+ durations, sine.inOut)
|
||||
phase3.to(el, {
|
||||
x: "+=" + driftX, y: "+=" + driftY,
|
||||
rotation: rotDrift,
|
||||
duration: speed, ease: "sine.inOut"
|
||||
}, emphasisStart3);
|
||||
});
|
||||
|
||||
// Title breathing motion (calm recipe: gentle scale oscillation)
|
||||
var breathCount = Math.floor(emphasisDuration3 / 4) - 1;
|
||||
phase3.to(".title-settled", {
|
||||
scale: 1.008, duration: 2, ease: "sine.inOut",
|
||||
yoyo: true, repeat: breathCount
|
||||
}, emphasisStart3);
|
||||
|
||||
// Subtle title glow breathing (Principle 8: Secondary Action reinforces calm)
|
||||
phase3.to(".title-settled", {
|
||||
textShadow: "0 0 50px rgba(180,30,30,0.4), 0 0 100px rgba(180,30,30,0.15)",
|
||||
duration: 3, ease: "sine.inOut",
|
||||
yoyo: true, repeat: Math.floor(emphasisDuration3 / 6) - 1
|
||||
}, emphasisStart3);
|
||||
|
||||
// Subtitle gentle drift (Principle 5: Follow-through on secondary elements)
|
||||
phase3.to(".subtitle", {
|
||||
y: -3, duration: emphasisDuration3, ease: "sine.inOut"
|
||||
}, emphasisStart3);
|
||||
|
||||
// Very slow vignette breathing (atmospheric ambiance)
|
||||
phase3.to(".dark-overlay", {
|
||||
opacity: 0.15, duration: 4, ease: "sine.inOut",
|
||||
yoyo: true, repeat: 1
|
||||
}, emphasisStart3 + 2);
|
||||
|
||||
// --- Phase 3 Exit: Fade Recede (17s - 20s) ---
|
||||
// Calm exit: opacity + slight scale down, elements sink back
|
||||
// Principle 5 (Follow-through): staggered finish times
|
||||
var exitStart3 = 17;
|
||||
var exitDuration3 = 3;
|
||||
|
||||
// Debris slowly recedes
|
||||
phase3.to(".debris", {
|
||||
opacity: 0, scale: 0.95, y: "+=10",
|
||||
duration: exitDuration3, ease: "sine.inOut",
|
||||
stagger: 0.08
|
||||
}, exitStart3);
|
||||
|
||||
// Title slowly fades (Principle 12: Appeal — matches aftermath tone)
|
||||
phase3.to(".title-settled", {
|
||||
opacity: 0, scale: 0.97, y: 5,
|
||||
duration: exitDuration3, ease: "sine.inOut"
|
||||
}, exitStart3);
|
||||
|
||||
// Subtitle fades last (Principle 3: Staging — last thing the eye sees)
|
||||
phase3.to(".subtitle", {
|
||||
opacity: 0, y: 5,
|
||||
duration: exitDuration3 * 0.8, ease: "sine.inOut"
|
||||
}, exitStart3 + 0.5);
|
||||
|
||||
// Final darkness
|
||||
phase3.to(".dark-overlay", {
|
||||
opacity: 0.9, duration: exitDuration3, ease: "sine.inOut"
|
||||
}, exitStart3);
|
||||
|
||||
// Add Phase 3 to master at overlap position
|
||||
tl.add(phase3, phase3Start);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,747 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=1920, height=1080">
|
||||
<title>SHATTERED - Thriller Title Sequence</title>
|
||||
<style>
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { background: #000; display: flex; justify-content: center; align-items: center; min-height: 100vh; }
|
||||
|
||||
.composition {
|
||||
background: #0a0a0a;
|
||||
font-family: 'Arial Black', 'Helvetica Neue', Arial, sans-serif;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* --- Phase 1: Creeping fragments --- */
|
||||
.fragment {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
font-size: 18px;
|
||||
letter-spacing: 6px;
|
||||
text-transform: uppercase;
|
||||
color: rgba(255, 255, 255, 0.15);
|
||||
font-weight: 300;
|
||||
font-family: 'Courier New', monospace;
|
||||
}
|
||||
|
||||
.fragment.red {
|
||||
color: rgba(200, 30, 30, 0.2);
|
||||
}
|
||||
|
||||
/* --- Noise / texture overlay --- */
|
||||
.noise-overlay {
|
||||
position: absolute;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
opacity: 0;
|
||||
background: repeating-linear-gradient(
|
||||
0deg,
|
||||
transparent,
|
||||
transparent 2px,
|
||||
rgba(255,255,255,0.015) 2px,
|
||||
rgba(255,255,255,0.015) 4px
|
||||
);
|
||||
pointer-events: none;
|
||||
z-index: 50;
|
||||
}
|
||||
|
||||
/* --- Vignette --- */
|
||||
.vignette {
|
||||
position: absolute;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
background: radial-gradient(ellipse at center, transparent 40%, rgba(0,0,0,0.8) 100%);
|
||||
pointer-events: none;
|
||||
z-index: 40;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* --- Scan lines --- */
|
||||
.scanlines {
|
||||
position: absolute;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
opacity: 0;
|
||||
background: repeating-linear-gradient(
|
||||
to bottom,
|
||||
transparent 0px,
|
||||
transparent 3px,
|
||||
rgba(0,0,0,0.15) 3px,
|
||||
rgba(0,0,0,0.15) 6px
|
||||
);
|
||||
pointer-events: none;
|
||||
z-index: 45;
|
||||
}
|
||||
|
||||
/* --- Red pulse flash --- */
|
||||
.red-flash {
|
||||
position: absolute;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
background: radial-gradient(ellipse at center, rgba(180,20,20,0.6) 0%, rgba(100,0,0,0.3) 50%, transparent 80%);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
z-index: 30;
|
||||
}
|
||||
|
||||
/* --- White flash for impact --- */
|
||||
.white-flash {
|
||||
position: absolute;
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
background: #fff;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
z-index: 60;
|
||||
}
|
||||
|
||||
/* --- Main title --- */
|
||||
.title-container {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
z-index: 20;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.title-main {
|
||||
font-size: 180px;
|
||||
font-weight: 900;
|
||||
letter-spacing: 30px;
|
||||
text-transform: uppercase;
|
||||
color: #fff;
|
||||
opacity: 0;
|
||||
text-shadow: 0 0 60px rgba(200, 30, 30, 0.8), 0 0 120px rgba(200, 30, 30, 0.4);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
/* --- Subtitle --- */
|
||||
.subtitle {
|
||||
position: absolute;
|
||||
top: 62%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
font-size: 22px;
|
||||
letter-spacing: 18px;
|
||||
text-transform: uppercase;
|
||||
color: rgba(200, 30, 30, 0.9);
|
||||
opacity: 0;
|
||||
font-weight: 300;
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
/* --- Glass shards --- */
|
||||
.shard {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
z-index: 25;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.shard svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* --- Crack lines --- */
|
||||
.crack-line {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
height: 2px;
|
||||
background: linear-gradient(90deg, rgba(255,255,255,0.9), rgba(200,30,30,0.6), transparent);
|
||||
opacity: 0;
|
||||
transform-origin: left center;
|
||||
z-index: 22;
|
||||
}
|
||||
|
||||
/* --- Floating debris --- */
|
||||
.debris {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
z-index: 15;
|
||||
}
|
||||
|
||||
.debris-dot {
|
||||
width: 3px;
|
||||
height: 3px;
|
||||
background: rgba(255, 255, 255, 0.3);
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.debris-dot.red {
|
||||
background: rgba(200, 30, 30, 0.4);
|
||||
}
|
||||
|
||||
.debris-line {
|
||||
width: 20px;
|
||||
height: 1px;
|
||||
background: rgba(255, 255, 255, 0.15);
|
||||
}
|
||||
|
||||
/* --- Horizontal rule accent --- */
|
||||
.hr-accent {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 0;
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg, transparent, rgba(200,30,30,0.8), transparent);
|
||||
opacity: 0;
|
||||
z-index: 18;
|
||||
}
|
||||
|
||||
/* --- Ambient glow --- */
|
||||
.ambient-glow {
|
||||
position: absolute;
|
||||
border-radius: 50%;
|
||||
filter: blur(80px);
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
z-index: 5;
|
||||
}
|
||||
|
||||
.glow-red {
|
||||
width: 400px;
|
||||
height: 400px;
|
||||
background: rgba(180, 20, 20, 0.15);
|
||||
top: 40%;
|
||||
left: 45%;
|
||||
}
|
||||
|
||||
.glow-white {
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
background: rgba(255, 255, 255, 0.05);
|
||||
top: 35%;
|
||||
left: 55%;
|
||||
}
|
||||
|
||||
/* --- Edge light streaks --- */
|
||||
.light-streak {
|
||||
position: absolute;
|
||||
height: 1px;
|
||||
opacity: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.light-streak.top { top: 0; left: 0; width: 100%; background: linear-gradient(90deg, transparent, rgba(200,30,30,0.3), transparent); }
|
||||
.light-streak.bottom { bottom: 0; left: 0; width: 100%; background: linear-gradient(90deg, transparent, rgba(200,30,30,0.3), transparent); }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<div class="composition" data-composition-id="high-contrast-arc-shattered" style="width: 1920px; height: 1080px; position: relative; overflow: hidden;">
|
||||
|
||||
<!-- Atmosphere layers -->
|
||||
<div class="noise-overlay"></div>
|
||||
<div class="vignette"></div>
|
||||
<div class="scanlines"></div>
|
||||
<div class="red-flash"></div>
|
||||
<div class="white-flash"></div>
|
||||
|
||||
<!-- Ambient glows -->
|
||||
<div class="ambient-glow glow-red"></div>
|
||||
<div class="ambient-glow glow-white"></div>
|
||||
|
||||
<!-- Light streaks -->
|
||||
<div class="light-streak top"></div>
|
||||
<div class="light-streak bottom"></div>
|
||||
|
||||
<!-- Horizontal rule accent -->
|
||||
<div class="hr-accent"></div>
|
||||
|
||||
<!-- Phase 1: Drifting text fragments -->
|
||||
<div class="fragment" style="top: 15%; left: 10%;">no escape</div>
|
||||
<div class="fragment" style="top: 30%; left: 75%;">broken</div>
|
||||
<div class="fragment red" style="top: 55%; left: 5%;">fear</div>
|
||||
<div class="fragment" style="top: 70%; left: 65%;">silence</div>
|
||||
<div class="fragment red" style="top: 20%; left: 50%;">shatter</div>
|
||||
<div class="fragment" style="top: 80%; left: 30%;">the truth</div>
|
||||
<div class="fragment" style="top: 45%; left: 85%;">lies</div>
|
||||
<div class="fragment red" style="top: 10%; left: 40%;">blood</div>
|
||||
<div class="fragment" style="top: 65%; left: 15%;">darkness</div>
|
||||
<div class="fragment" style="top: 40%; left: 55%;">hidden</div>
|
||||
<div class="fragment red" style="top: 85%; left: 70%;">revenge</div>
|
||||
<div class="fragment" style="top: 25%; left: 25%;">nothing left</div>
|
||||
|
||||
<!-- Main title -->
|
||||
<div class="title-container">
|
||||
<div class="title-main">SHATTERED</div>
|
||||
</div>
|
||||
|
||||
<!-- Subtitle -->
|
||||
<div class="subtitle">EVERY TRUTH HAS A BREAKING POINT</div>
|
||||
|
||||
<!-- Glass shards (SVG triangles) -->
|
||||
<div class="shard" style="width:120px;height:100px;top:30%;left:20%;">
|
||||
<svg viewBox="0 0 120 100"><polygon points="60,5 115,95 5,95" fill="none" stroke="rgba(255,255,255,0.6)" stroke-width="1"/></svg>
|
||||
</div>
|
||||
<div class="shard" style="width:80px;height:70px;top:25%;left:70%;">
|
||||
<svg viewBox="0 0 80 70"><polygon points="10,5 75,30 40,65" fill="none" stroke="rgba(255,255,255,0.5)" stroke-width="1"/></svg>
|
||||
</div>
|
||||
<div class="shard" style="width:100px;height:90px;top:60%;left:15%;">
|
||||
<svg viewBox="0 0 100 90"><polygon points="50,5 95,85 5,60" fill="none" stroke="rgba(200,30,30,0.5)" stroke-width="1"/></svg>
|
||||
</div>
|
||||
<div class="shard" style="width:90px;height:80px;top:55%;left:75%;">
|
||||
<svg viewBox="0 0 90 80"><polygon points="5,10 85,5 45,75" fill="none" stroke="rgba(255,255,255,0.4)" stroke-width="1"/></svg>
|
||||
</div>
|
||||
<div class="shard" style="width:60px;height:55px;top:20%;left:45%;">
|
||||
<svg viewBox="0 0 60 55"><polygon points="30,3 57,50 3,50" fill="none" stroke="rgba(200,30,30,0.6)" stroke-width="1"/></svg>
|
||||
</div>
|
||||
<div class="shard" style="width:70px;height:65px;top:70%;left:50%;">
|
||||
<svg viewBox="0 0 70 65"><polygon points="5,5 65,20 35,60" fill="none" stroke="rgba(255,255,255,0.5)" stroke-width="1"/></svg>
|
||||
</div>
|
||||
<div class="shard" style="width:110px;height:95px;top:40%;left:35%;">
|
||||
<svg viewBox="0 0 110 95"><polygon points="55,5 105,90 5,70" fill="none" stroke="rgba(200,30,30,0.4)" stroke-width="1"/></svg>
|
||||
</div>
|
||||
<div class="shard" style="width:50px;height:45px;top:15%;left:60%;">
|
||||
<svg viewBox="0 0 50 45"><polygon points="25,3 47,42 3,42" fill="none" stroke="rgba(255,255,255,0.7)" stroke-width="1"/></svg>
|
||||
</div>
|
||||
<div class="shard" style="width:85px;height:75px;top:75%;left:25%;">
|
||||
<svg viewBox="0 0 85 75"><polygon points="42,5 80,70 5,50" fill="none" stroke="rgba(255,255,255,0.3)" stroke-width="1"/></svg>
|
||||
</div>
|
||||
<div class="shard" style="width:65px;height:58px;top:65%;left:80%;">
|
||||
<svg viewBox="0 0 65 58"><polygon points="5,8 60,5 32,55" fill="none" stroke="rgba(200,30,30,0.5)" stroke-width="1"/></svg>
|
||||
</div>
|
||||
|
||||
<!-- Crack lines radiating from center -->
|
||||
<div class="crack-line" style="width: 500px; transform: rotate(-15deg);"></div>
|
||||
<div class="crack-line" style="width: 450px; transform: rotate(25deg);"></div>
|
||||
<div class="crack-line" style="width: 600px; transform: rotate(-45deg);"></div>
|
||||
<div class="crack-line" style="width: 380px; transform: rotate(55deg);"></div>
|
||||
<div class="crack-line" style="width: 520px; transform: rotate(-70deg);"></div>
|
||||
<div class="crack-line" style="width: 470px; transform: rotate(10deg);"></div>
|
||||
<div class="crack-line" style="width: 550px; transform: rotate(80deg);"></div>
|
||||
<div class="crack-line" style="width: 400px; transform: rotate(-35deg);"></div>
|
||||
<div class="crack-line" style="width: 350px; transform: rotate(42deg);"></div>
|
||||
<div class="crack-line" style="width: 480px; transform: rotate(-60deg);"></div>
|
||||
<div class="crack-line" style="width: 420px; transform: rotate(70deg);"></div>
|
||||
<div class="crack-line" style="width: 560px; transform: rotate(-5deg);"></div>
|
||||
|
||||
<!-- Floating debris for aftermath -->
|
||||
<div class="debris" style="top:20%;left:10%;"><div class="debris-dot"></div></div>
|
||||
<div class="debris" style="top:30%;left:85%;"><div class="debris-dot red"></div></div>
|
||||
<div class="debris" style="top:60%;left:25%;"><div class="debris-line"></div></div>
|
||||
<div class="debris" style="top:15%;left:50%;"><div class="debris-dot"></div></div>
|
||||
<div class="debris" style="top:75%;left:70%;"><div class="debris-dot red"></div></div>
|
||||
<div class="debris" style="top:45%;left:5%;"><div class="debris-line"></div></div>
|
||||
<div class="debris" style="top:80%;left:40%;"><div class="debris-dot"></div></div>
|
||||
<div class="debris" style="top:10%;left:30%;"><div class="debris-dot red"></div></div>
|
||||
<div class="debris" style="top:50%;left:90%;"><div class="debris-line"></div></div>
|
||||
<div class="debris" style="top:35%;left:60%;"><div class="debris-dot"></div></div>
|
||||
<div class="debris" style="top:70%;left:15%;"><div class="debris-dot"></div></div>
|
||||
<div class="debris" style="top:25%;left:75%;"><div class="debris-line"></div></div>
|
||||
<div class="debris" style="top:55%;left:45%;"><div class="debris-dot red"></div></div>
|
||||
<div class="debris" style="top:85%;left:55%;"><div class="debris-dot"></div></div>
|
||||
<div class="debris" style="top:40%;left:35%;"><div class="debris-line"></div></div>
|
||||
<div class="debris" style="top:65%;left:80%;"><div class="debris-dot red"></div></div>
|
||||
<div class="debris" style="top:90%;left:20%;"><div class="debris-dot"></div></div>
|
||||
<div class="debris" style="top:5%;left:65%;"><div class="debris-line"></div></div>
|
||||
<div class="debris" style="top:48%;left:12%;"><div class="debris-dot"></div></div>
|
||||
<div class="debris" style="top:72%;left:88%;"><div class="debris-dot red"></div></div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Scripts at end of body -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/CustomEase.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/SplitText.min.js"></script>
|
||||
<script>
|
||||
gsap.registerPlugin(CustomEase, SplitText);
|
||||
|
||||
// Custom eases
|
||||
CustomEase.create("creep", "M0,0 C0.1,0 0.15,0.02 0.2,0.04 0.35,0.1 0.4,0.12 0.5,0.5 0.6,0.88 0.65,0.95 1,1");
|
||||
CustomEase.create("shatterIn", "M0,0 C0,0 0.05,1.2 0.1,1 0.15,0.85 0.18,1.05 0.22,1 0.3,0.95 0.5,1 1,1");
|
||||
CustomEase.create("glassOut", "M0,0 C0.2,0.8 0.3,1 0.5,1 0.7,1 0.8,0.95 1,0.85");
|
||||
CustomEase.create("driftSlow", "M0,0 C0.25,0.1 0.25,1 1,1");
|
||||
|
||||
const tl = gsap.timeline({ paused: true });
|
||||
|
||||
// Register timeline
|
||||
window.__timelines = window.__timelines || {};
|
||||
window.__timelines["high-contrast-arc-shattered"] = tl;
|
||||
|
||||
// --- Element references ---
|
||||
const fragments = document.querySelectorAll(".fragment");
|
||||
const shards = document.querySelectorAll(".shard");
|
||||
const crackLines = document.querySelectorAll(".crack-line");
|
||||
const debrisEls = document.querySelectorAll(".debris");
|
||||
const titleMain = document.querySelector(".title-main");
|
||||
const subtitle = document.querySelector(".subtitle");
|
||||
const noiseOverlay = document.querySelector(".noise-overlay");
|
||||
const vignette = document.querySelector(".vignette");
|
||||
const scanlines = document.querySelector(".scanlines");
|
||||
const redFlash = document.querySelector(".red-flash");
|
||||
const whiteFlash = document.querySelector(".white-flash");
|
||||
const hrAccent = document.querySelector(".hr-accent");
|
||||
const glowRed = document.querySelector(".glow-red");
|
||||
const glowWhite = document.querySelector(".glow-white");
|
||||
const lightStreakTop = document.querySelector(".light-streak.top");
|
||||
const lightStreakBottom = document.querySelector(".light-streak.bottom");
|
||||
|
||||
// Split title text for per-character animation
|
||||
const splitTitle = new SplitText(titleMain, { type: "chars" });
|
||||
const titleChars = splitTitle.chars;
|
||||
|
||||
// Set initial states for title chars
|
||||
gsap.set(titleChars, { opacity: 0, scale: 3, rotationX: -90, rotationY: gsap.utils.wrap([-30, 30]), transformOrigin: "50% 50% -50px" });
|
||||
|
||||
// =====================================================
|
||||
// PHASE 1: CREEPING UNEASE (0s - 18s)
|
||||
// Fragments drift in darkness, slow tension building
|
||||
// =====================================================
|
||||
|
||||
// Fade in atmospheric layers
|
||||
tl.to(vignette, { opacity: 1, duration: 3, ease: "power1.inOut" }, 0);
|
||||
tl.to(noiseOverlay, { opacity: 0.5, duration: 4, ease: "power1.inOut" }, 0.5);
|
||||
tl.to(scanlines, { opacity: 0.4, duration: 5, ease: "power1.inOut" }, 1);
|
||||
|
||||
// Fragments fade in one by one, drifting slowly
|
||||
fragments.forEach(function(frag, i) {
|
||||
var delay = 1 + i * 1.2;
|
||||
var driftX = (Math.random() - 0.5) * 80;
|
||||
var driftY = (Math.random() - 0.5) * 40;
|
||||
|
||||
tl.to(frag, {
|
||||
opacity: frag.classList.contains("red") ? 0.25 : 0.18,
|
||||
duration: 2.5,
|
||||
ease: "creep"
|
||||
}, delay);
|
||||
|
||||
// Slow continuous drift
|
||||
tl.to(frag, {
|
||||
x: driftX,
|
||||
y: driftY,
|
||||
duration: 15,
|
||||
ease: "none"
|
||||
}, delay);
|
||||
});
|
||||
|
||||
// Subtle red ambient glow pulsing
|
||||
tl.to(glowRed, { opacity: 0.12, duration: 6, ease: "power1.inOut" }, 2);
|
||||
tl.to(glowRed, {
|
||||
scale: 1.3,
|
||||
duration: 8,
|
||||
ease: "sine.inOut",
|
||||
yoyo: true,
|
||||
repeat: 1
|
||||
}, 2);
|
||||
|
||||
// Occasional flicker on random fragments (unease)
|
||||
tl.to(fragments[2], { opacity: 0, duration: 0.05 }, 8);
|
||||
tl.to(fragments[2], { opacity: 0.25, duration: 0.05 }, 8.1);
|
||||
tl.to(fragments[2], { opacity: 0, duration: 0.03 }, 8.15);
|
||||
tl.to(fragments[2], { opacity: 0.25, duration: 0.15 }, 8.2);
|
||||
|
||||
tl.to(fragments[7], { opacity: 0, duration: 0.04 }, 11);
|
||||
tl.to(fragments[7], { opacity: 0.18, duration: 0.06 }, 11.08);
|
||||
tl.to(fragments[7], { opacity: 0, duration: 0.03 }, 11.2);
|
||||
tl.to(fragments[7], { opacity: 0.18, duration: 0.1 }, 11.25);
|
||||
|
||||
tl.to(fragments[4], { opacity: 0, duration: 0.05 }, 13.5);
|
||||
tl.to(fragments[4], { opacity: 0.3, duration: 0.03 }, 13.55);
|
||||
tl.to(fragments[4], { opacity: 0, duration: 0.04 }, 13.6);
|
||||
tl.to(fragments[4], { opacity: 0.25, duration: 0.2 }, 13.65);
|
||||
|
||||
// Growing red pulse at ~15s (tension peak)
|
||||
tl.to(redFlash, { opacity: 0.08, duration: 1.5, ease: "power2.in" }, 14);
|
||||
tl.to(redFlash, { opacity: 0, duration: 1, ease: "power2.out" }, 15.5);
|
||||
|
||||
// Slight camera-shake-like movement on fragments pre-impact
|
||||
tl.to(fragments, {
|
||||
x: "+=3",
|
||||
duration: 0.08,
|
||||
ease: "none",
|
||||
stagger: { each: 0.02, from: "random" }
|
||||
}, 16.5);
|
||||
tl.to(fragments, {
|
||||
x: "-=6",
|
||||
duration: 0.08,
|
||||
ease: "none",
|
||||
stagger: { each: 0.02, from: "random" }
|
||||
}, 16.6);
|
||||
tl.to(fragments, {
|
||||
x: "+=3",
|
||||
duration: 0.1,
|
||||
ease: "power2.out",
|
||||
stagger: { each: 0.02, from: "random" }
|
||||
}, 16.7);
|
||||
|
||||
// Second stronger tremor
|
||||
tl.to(fragments, {
|
||||
x: "+=5",
|
||||
y: "+=2",
|
||||
duration: 0.06,
|
||||
ease: "none",
|
||||
stagger: { each: 0.015, from: "random" }
|
||||
}, 17.2);
|
||||
tl.to(fragments, {
|
||||
x: "-=10",
|
||||
y: "-=4",
|
||||
duration: 0.06,
|
||||
ease: "none",
|
||||
stagger: { each: 0.015, from: "random" }
|
||||
}, 17.3);
|
||||
tl.to(fragments, {
|
||||
x: "+=5",
|
||||
y: "+=2",
|
||||
duration: 0.12,
|
||||
ease: "power2.out",
|
||||
stagger: { each: 0.015, from: "random" }
|
||||
}, 17.4);
|
||||
|
||||
// Rapid fragment flicker right before the snap
|
||||
tl.to(fragments, {
|
||||
opacity: 0,
|
||||
duration: 0.03,
|
||||
stagger: { each: 0.01, from: "random" }
|
||||
}, 17.8);
|
||||
tl.to(fragments, {
|
||||
opacity: 0.3,
|
||||
duration: 0.03,
|
||||
stagger: { each: 0.01, from: "random" }
|
||||
}, 17.85);
|
||||
tl.to(fragments, {
|
||||
opacity: 0,
|
||||
duration: 0.03,
|
||||
stagger: { each: 0.01, from: "random" }
|
||||
}, 17.9);
|
||||
|
||||
// =====================================================
|
||||
// PHASE 2: THE SNAP - EXPLOSIVE REVEAL (18s - 26s)
|
||||
// White flash, title slams in, glass shatters
|
||||
// =====================================================
|
||||
|
||||
var snapTime = 18;
|
||||
|
||||
// Kill all fragments
|
||||
tl.to(fragments, { opacity: 0, duration: 0.1 }, snapTime);
|
||||
|
||||
// WHITE FLASH - the snap moment
|
||||
tl.to(whiteFlash, { opacity: 1, duration: 0.08, ease: "power4.in" }, snapTime);
|
||||
tl.to(whiteFlash, { opacity: 0, duration: 0.6, ease: "power2.out" }, snapTime + 0.1);
|
||||
|
||||
// Second flash echo
|
||||
tl.to(whiteFlash, { opacity: 0.4, duration: 0.04 }, snapTime + 0.5);
|
||||
tl.to(whiteFlash, { opacity: 0, duration: 0.3, ease: "power2.out" }, snapTime + 0.55);
|
||||
|
||||
// RED flash hit
|
||||
tl.to(redFlash, { opacity: 0.7, duration: 0.1, ease: "power4.in" }, snapTime + 0.15);
|
||||
tl.to(redFlash, { opacity: 0, duration: 1.5, ease: "power2.out" }, snapTime + 0.3);
|
||||
|
||||
// TITLE CHARS slam in with stagger
|
||||
tl.to(titleChars, {
|
||||
opacity: 1,
|
||||
scale: 1,
|
||||
rotationX: 0,
|
||||
rotationY: 0,
|
||||
duration: 0.6,
|
||||
ease: "shatterIn",
|
||||
stagger: {
|
||||
each: 0.04,
|
||||
from: "center"
|
||||
}
|
||||
}, snapTime + 0.1);
|
||||
|
||||
// Title container slight overshoot scale
|
||||
tl.fromTo(titleMain,
|
||||
{ scale: 1.15 },
|
||||
{ scale: 1, duration: 0.8, ease: "power3.out", opacity: 1 },
|
||||
snapTime + 0.1
|
||||
);
|
||||
|
||||
// Crack lines burst from center
|
||||
crackLines.forEach(function(line, i) {
|
||||
var lineDelay = snapTime + 0.12 + (i * 0.02);
|
||||
tl.to(line, {
|
||||
opacity: 0.9,
|
||||
duration: 0.1,
|
||||
ease: "power4.in"
|
||||
}, lineDelay);
|
||||
tl.fromTo(line,
|
||||
{ width: 0 },
|
||||
{ width: line.style.width, duration: 0.25, ease: "power4.out" },
|
||||
lineDelay
|
||||
);
|
||||
// Fade crack lines out slowly
|
||||
tl.to(line, {
|
||||
opacity: 0,
|
||||
duration: 2,
|
||||
ease: "power1.inOut"
|
||||
}, lineDelay + 0.8);
|
||||
});
|
||||
|
||||
// Glass shards explode outward from center
|
||||
shards.forEach(function(shard, i) {
|
||||
var angle = (i / shards.length) * Math.PI * 2;
|
||||
var distance = 200 + Math.random() * 400;
|
||||
var targetX = Math.cos(angle) * distance;
|
||||
var targetY = Math.sin(angle) * distance;
|
||||
|
||||
// Start at center
|
||||
gsap.set(shard, {
|
||||
top: "50%",
|
||||
left: "50%",
|
||||
xPercent: -50,
|
||||
yPercent: -50,
|
||||
x: 0,
|
||||
y: 0,
|
||||
scale: 0,
|
||||
rotation: 0
|
||||
});
|
||||
|
||||
tl.to(shard, {
|
||||
opacity: 0.8,
|
||||
scale: 1,
|
||||
x: targetX * 0.3,
|
||||
y: targetY * 0.3,
|
||||
rotation: (Math.random() - 0.5) * 90,
|
||||
duration: 0.15,
|
||||
ease: "power4.out"
|
||||
}, snapTime + 0.08 + (i * 0.02));
|
||||
|
||||
// Continue outward and fade
|
||||
tl.to(shard, {
|
||||
x: targetX,
|
||||
y: targetY,
|
||||
rotation: (Math.random() - 0.5) * 180,
|
||||
opacity: 0,
|
||||
duration: 2.5,
|
||||
ease: "glassOut"
|
||||
}, snapTime + 0.25 + (i * 0.02));
|
||||
});
|
||||
|
||||
// HR accent line expands under title
|
||||
tl.to(hrAccent, { opacity: 1, width: 600, duration: 0.8, ease: "power3.out" }, snapTime + 0.3);
|
||||
|
||||
// Light streaks flash
|
||||
tl.to(lightStreakTop, { opacity: 0.8, duration: 0.1 }, snapTime + 0.05);
|
||||
tl.to(lightStreakTop, { opacity: 0, duration: 1.5, ease: "power2.out" }, snapTime + 0.2);
|
||||
tl.to(lightStreakBottom, { opacity: 0.8, duration: 0.1 }, snapTime + 0.1);
|
||||
tl.to(lightStreakBottom, { opacity: 0, duration: 1.5, ease: "power2.out" }, snapTime + 0.25);
|
||||
|
||||
// Scanlines intensify then settle
|
||||
tl.to(scanlines, { opacity: 0.7, duration: 0.1 }, snapTime);
|
||||
tl.to(scanlines, { opacity: 0.3, duration: 2, ease: "power1.out" }, snapTime + 0.3);
|
||||
|
||||
// Title text glow pulse
|
||||
tl.to(titleMain, {
|
||||
textShadow: "0 0 80px rgba(200,30,30,1), 0 0 160px rgba(200,30,30,0.7), 0 0 240px rgba(180,20,20,0.4)",
|
||||
duration: 0.3,
|
||||
ease: "power2.in"
|
||||
}, snapTime + 0.15);
|
||||
tl.to(titleMain, {
|
||||
textShadow: "0 0 40px rgba(200,30,30,0.5), 0 0 80px rgba(200,30,30,0.25), 0 0 120px rgba(180,20,20,0.1)",
|
||||
duration: 3,
|
||||
ease: "power2.out"
|
||||
}, snapTime + 0.5);
|
||||
|
||||
// Second aftershock tremor on title
|
||||
tl.to(".title-container", { x: 8, duration: 0.04, ease: "none" }, snapTime + 0.7);
|
||||
tl.to(".title-container", { x: -12, duration: 0.04, ease: "none" }, snapTime + 0.74);
|
||||
tl.to(".title-container", { x: 6, duration: 0.04, ease: "none" }, snapTime + 0.78);
|
||||
tl.to(".title-container", { x: -3, duration: 0.04, ease: "none" }, snapTime + 0.82);
|
||||
tl.to(".title-container", { x: 0, duration: 0.15, ease: "power2.out" }, snapTime + 0.86);
|
||||
|
||||
// Subtitle fades in
|
||||
tl.to(subtitle, {
|
||||
opacity: 1,
|
||||
duration: 2,
|
||||
ease: "power1.inOut"
|
||||
}, snapTime + 2.5);
|
||||
|
||||
// Ambient glows ignite
|
||||
tl.to(glowRed, { opacity: 0.3, scale: 1.5, duration: 1, ease: "power2.out" }, snapTime + 0.2);
|
||||
tl.to(glowWhite, { opacity: 0.12, scale: 1.3, duration: 1.2, ease: "power2.out" }, snapTime + 0.3);
|
||||
|
||||
// =====================================================
|
||||
// PHASE 3: CALM AFTERMATH (26s - 45s)
|
||||
// Title holds, debris floats gently, everything settles
|
||||
// =====================================================
|
||||
|
||||
var afterTime = 26;
|
||||
|
||||
// HR accent slowly expands further then fades slightly
|
||||
tl.to(hrAccent, { width: 900, opacity: 0.5, duration: 8, ease: "driftSlow" }, afterTime);
|
||||
|
||||
// Floating debris drifts in
|
||||
debrisEls.forEach(function(d, i) {
|
||||
var startDelay = afterTime + (i * 0.4);
|
||||
var floatDuration = 10 + Math.random() * 8;
|
||||
var driftX = (Math.random() - 0.5) * 150;
|
||||
var driftY = -30 - Math.random() * 60;
|
||||
|
||||
tl.to(d, {
|
||||
opacity: d.querySelector(".red") ? 0.4 : 0.25,
|
||||
duration: 2,
|
||||
ease: "power1.inOut"
|
||||
}, startDelay);
|
||||
|
||||
tl.to(d, {
|
||||
x: driftX,
|
||||
y: driftY,
|
||||
rotation: (Math.random() - 0.5) * 60,
|
||||
duration: floatDuration,
|
||||
ease: "sine.inOut"
|
||||
}, startDelay);
|
||||
});
|
||||
|
||||
// Title glow breathes gently
|
||||
tl.to(titleMain, {
|
||||
textShadow: "0 0 50px rgba(200,30,30,0.6), 0 0 100px rgba(200,30,30,0.3)",
|
||||
duration: 4,
|
||||
ease: "sine.inOut",
|
||||
yoyo: true,
|
||||
repeat: 2
|
||||
}, afterTime + 1);
|
||||
|
||||
// Red ambient glow pulses slowly
|
||||
tl.to(glowRed, {
|
||||
opacity: 0.2,
|
||||
scale: 1.8,
|
||||
duration: 6,
|
||||
ease: "sine.inOut",
|
||||
yoyo: true,
|
||||
repeat: 1
|
||||
}, afterTime + 2);
|
||||
|
||||
// Vignette slightly deepens
|
||||
tl.to(vignette, { opacity: 1.2, duration: 8, ease: "power1.inOut" }, afterTime + 3);
|
||||
|
||||
// Occasional subtle flicker on title chars
|
||||
tl.to(titleChars[2], { opacity: 0.5, duration: 0.04 }, afterTime + 5);
|
||||
tl.to(titleChars[2], { opacity: 1, duration: 0.08 }, afterTime + 5.06);
|
||||
tl.to(titleChars[6], { opacity: 0.4, duration: 0.03 }, afterTime + 8);
|
||||
tl.to(titleChars[6], { opacity: 1, duration: 0.1 }, afterTime + 8.05);
|
||||
|
||||
// Light streak whisper
|
||||
tl.to(lightStreakTop, { opacity: 0.15, duration: 0.5, ease: "power1.inOut" }, afterTime + 6);
|
||||
tl.to(lightStreakTop, { opacity: 0, duration: 2, ease: "power1.out" }, afterTime + 6.5);
|
||||
|
||||
// Final fade: everything gently dims as we approach 45s
|
||||
var fadeOutTime = 40;
|
||||
|
||||
tl.to(titleMain, { opacity: 0.7, duration: 5, ease: "power1.inOut" }, fadeOutTime);
|
||||
tl.to(subtitle, { opacity: 0.5, duration: 5, ease: "power1.inOut" }, fadeOutTime);
|
||||
tl.to(hrAccent, { opacity: 0.2, duration: 5, ease: "power1.inOut" }, fadeOutTime);
|
||||
tl.to(debrisEls, {
|
||||
opacity: 0,
|
||||
duration: 4,
|
||||
ease: "power1.inOut",
|
||||
stagger: { each: 0.1, from: "random" }
|
||||
}, fadeOutTime + 1);
|
||||
tl.to(glowRed, { opacity: 0.05, duration: 5, ease: "power1.inOut" }, fadeOutTime);
|
||||
tl.to(glowWhite, { opacity: 0, duration: 4, ease: "power1.inOut" }, fadeOutTime + 1);
|
||||
tl.to(scanlines, { opacity: 0.15, duration: 5, ease: "power1.inOut" }, fadeOutTime);
|
||||
|
||||
// Final hold to ~45s with gentle darkness
|
||||
tl.to({}, { duration: 0.5 }, 45);
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1 +0,0 @@
|
||||
/Users/vanceingalls/src/hyperframes/examples/vignelli
|
||||
Reference in New Issue
Block a user