mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-14 18:01:20 +08:00
ec66d59caa
Caption components are overlays — their root element should not intercept pointer events, allowing clicks to pass through to the underlying composition content in Studio.
314 lines
9.9 KiB
HTML
Vendored
314 lines
9.9 KiB
HTML
Vendored
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=1920, height=1080" />
|
|
<title>Particle Burst</title>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
<link
|
|
href="https://fonts.googleapis.com/css2?family=Outfit:wght@900&display=swap"
|
|
rel="stylesheet"
|
|
/>
|
|
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
|
<style>
|
|
*,
|
|
*::before,
|
|
*::after {
|
|
box-sizing: border-box;
|
|
}
|
|
html,
|
|
body {
|
|
width: 1920px;
|
|
height: 1080px;
|
|
margin: 0;
|
|
overflow: hidden;
|
|
background: transparent;
|
|
}
|
|
#particle-burst {
|
|
pointer-events: none;
|
|
position: relative;
|
|
width: 1920px;
|
|
height: 1080px;
|
|
overflow: hidden;
|
|
background: transparent;
|
|
}
|
|
#bs-video {
|
|
position: absolute;
|
|
inset: 0;
|
|
z-index: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
.bs-overlay {
|
|
position: absolute;
|
|
inset: 0;
|
|
background: transparent;
|
|
z-index: 1;
|
|
pointer-events: none;
|
|
}
|
|
#bs-container {
|
|
position: absolute;
|
|
inset: 0;
|
|
z-index: 10;
|
|
pointer-events: none;
|
|
}
|
|
.bs-group {
|
|
position: absolute;
|
|
bottom: 120px;
|
|
left: 0;
|
|
width: 100%;
|
|
display: flex;
|
|
flex-wrap: wrap;
|
|
align-items: flex-end;
|
|
justify-content: center;
|
|
gap: 24px;
|
|
padding: 0 100px;
|
|
opacity: 0;
|
|
visibility: hidden;
|
|
}
|
|
.bs-word {
|
|
font-family: "Outfit", sans-serif;
|
|
font-weight: 900;
|
|
font-size: 88px;
|
|
text-transform: uppercase;
|
|
color: rgba(255, 255, 255, 0.45);
|
|
display: inline-block;
|
|
letter-spacing: 0.05em;
|
|
line-height: 1;
|
|
}
|
|
.bs-particle {
|
|
position: absolute;
|
|
width: 10px;
|
|
height: 10px;
|
|
border-radius: 50%;
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
z-index: 20;
|
|
left: 960px;
|
|
bottom: 180px;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div
|
|
id="particle-burst"
|
|
data-composition-id="caption-particle-burst"
|
|
data-timeline-locked
|
|
data-start="0"
|
|
data-duration="8"
|
|
data-fps="30"
|
|
data-width="1920"
|
|
data-height="1080"
|
|
>
|
|
<video
|
|
id="bs-video"
|
|
muted
|
|
playsinline
|
|
data-start="0"
|
|
data-duration="8"
|
|
data-track-index="0"
|
|
data-has-audio="false"
|
|
></video>
|
|
<div class="bs-overlay"></div>
|
|
<div id="bs-container"></div>
|
|
</div>
|
|
|
|
<script>
|
|
(function () {
|
|
window.__timelines = window.__timelines || {};
|
|
|
|
var _fitCanvas = document.createElement("canvas");
|
|
var _fitCtx = _fitCanvas.getContext("2d");
|
|
function fitFontSize(text, baseFontSize, fontWeight, fontFamily, maxWidth) {
|
|
var size = baseFontSize;
|
|
var minSize = Math.floor(baseFontSize * 0.45);
|
|
while (size > minSize) {
|
|
_fitCtx.font = fontWeight + " " + size + "px " + fontFamily;
|
|
if (_fitCtx.measureText(text).width <= maxWidth) return size;
|
|
size -= 2;
|
|
}
|
|
return minSize;
|
|
}
|
|
|
|
function mulberry32(seed) {
|
|
return function () {
|
|
seed |= 0;
|
|
seed = (seed + 0x6d2b79f5) | 0;
|
|
var t = Math.imul(seed ^ (seed >>> 15), 1 | seed);
|
|
t = (t + Math.imul(t ^ (t >>> 7), 61 | t)) ^ t;
|
|
return ((t ^ (t >>> 14)) >>> 0) / 4294967296;
|
|
};
|
|
}
|
|
|
|
var WORDS = [
|
|
{ text: "Every", start: 0.0, end: 0.3 },
|
|
{ text: "great", start: 0.3, end: 0.55 },
|
|
{ text: "video", start: 0.55, end: 0.85 },
|
|
{ text: "starts", start: 0.85, end: 1.1 },
|
|
{ text: "with", start: 1.1, end: 1.25 },
|
|
{ text: "a", start: 1.25, end: 1.35 },
|
|
{ text: "single", start: 1.35, end: 1.65 },
|
|
{ text: "frame.", start: 1.65, end: 2.0 },
|
|
{ text: "HyperFrames", start: 2.1, end: 2.65 },
|
|
{ text: "lets", start: 2.65, end: 2.85 },
|
|
{ text: "you", start: 2.85, end: 2.95 },
|
|
{ text: "write", start: 2.95, end: 3.15 },
|
|
{ text: "HTML", start: 3.15, end: 3.55 },
|
|
{ text: "and", start: 3.55, end: 3.65 },
|
|
{ text: "render", start: 3.65, end: 3.95 },
|
|
{ text: "professional", start: 3.95, end: 4.45 },
|
|
{ text: "video.", start: 4.45, end: 4.8 },
|
|
{ text: "No", start: 4.9, end: 5.05 },
|
|
{ text: "timeline.", start: 5.05, end: 5.45 },
|
|
{ text: "No", start: 5.5, end: 5.65 },
|
|
{ text: "drag", start: 5.65, end: 5.85 },
|
|
{ text: "and", start: 5.85, end: 5.95 },
|
|
{ text: "drop.", start: 5.95, end: 6.25 },
|
|
{ text: "Just", start: 6.35, end: 6.55 },
|
|
{ text: "code", start: 6.55, end: 6.8 },
|
|
{ text: "that", start: 6.8, end: 6.95 },
|
|
{ text: "becomes", start: 6.95, end: 7.3 },
|
|
{ text: "cinema.", start: 7.3, end: 7.7 },
|
|
];
|
|
|
|
// Keywords get particle burst + gold color
|
|
var KEYWORDS = new Set([8, 12, 15, 24, 27]); // HyperFrames, HTML, professional, code, cinema.
|
|
|
|
var RAW_GROUPS = [
|
|
[0, 3],
|
|
[4, 7],
|
|
[8, 8],
|
|
[9, 12],
|
|
[13, 15],
|
|
[16, 16],
|
|
[17, 19],
|
|
[20, 22],
|
|
[23, 24],
|
|
[25, 27],
|
|
];
|
|
var GROUPS = RAW_GROUPS.map(function (pair, gi) {
|
|
var ws = pair[0],
|
|
we = pair[1];
|
|
var nextStart = gi + 1 < RAW_GROUPS.length ? WORDS[RAW_GROUPS[gi + 1][0]].start : 8.0;
|
|
var gEnd = Math.min(WORDS[we].end + 0.35, nextStart - 0.05);
|
|
return { wordStart: ws, wordEnd: we, start: WORDS[ws].start, end: gEnd };
|
|
});
|
|
|
|
var PARTICLE_COLORS = [
|
|
"#FFD700",
|
|
"#FF6B35",
|
|
"#FF3C78",
|
|
"#9B5DE5",
|
|
"#00BBF9",
|
|
"#00F5D4",
|
|
"#ffffff",
|
|
"#F15BB5",
|
|
];
|
|
var NUM_PARTICLES = 10;
|
|
|
|
var container = document.getElementById("bs-container");
|
|
var tl = gsap.timeline({ paused: true });
|
|
|
|
GROUPS.forEach(function (g, gi) {
|
|
var groupWords = WORDS.slice(g.wordStart, g.wordEnd + 1);
|
|
var groupText = groupWords
|
|
.map(function (w) {
|
|
return w.text.toUpperCase();
|
|
})
|
|
.join(" ");
|
|
var fontSize = fitFontSize(groupText, 88, "900", "'Outfit', sans-serif", 1720);
|
|
var grp = document.createElement("div");
|
|
grp.className = "bs-group";
|
|
grp.id = "bs-grp-" + gi;
|
|
|
|
groupWords.forEach(function (w, i) {
|
|
var wi = g.wordStart + i;
|
|
var span = document.createElement("span");
|
|
span.className = "bs-word";
|
|
span.id = "bs-w-" + wi;
|
|
span.textContent = w.text.toUpperCase();
|
|
span.style.fontSize = fontSize + "px";
|
|
grp.appendChild(span);
|
|
});
|
|
|
|
container.appendChild(grp);
|
|
});
|
|
|
|
// Create particles for keyword words
|
|
var particleData = {};
|
|
WORDS.forEach(function (w, wi) {
|
|
if (!KEYWORDS.has(wi)) return;
|
|
var rng = mulberry32(wi * 137 + 99);
|
|
particleData[wi] = [];
|
|
for (var p = 0; p < NUM_PARTICLES; p++) {
|
|
var angle = (p / NUM_PARTICLES) * Math.PI * 2 + rng() * 0.6;
|
|
var dist = 120 + rng() * 200;
|
|
var sz = 4 + rng() * 8;
|
|
var dx = Math.cos(angle) * dist;
|
|
var dy = -Math.sin(angle) * dist;
|
|
var dot = document.createElement("div");
|
|
dot.className = "bs-particle";
|
|
dot.id = "bs-p-" + wi + "-" + p;
|
|
dot.style.backgroundColor = PARTICLE_COLORS[p % PARTICLE_COLORS.length];
|
|
dot.style.width = sz.toFixed(1) + "px";
|
|
dot.style.height = sz.toFixed(1) + "px";
|
|
container.appendChild(dot);
|
|
particleData[wi].push({ el: dot, dx: dx, dy: dy });
|
|
}
|
|
});
|
|
|
|
GROUPS.forEach(function (g, gi) {
|
|
var grp = document.getElementById("bs-grp-" + gi);
|
|
var groupWords = WORDS.slice(g.wordStart, g.wordEnd + 1);
|
|
|
|
tl.set(grp, { visibility: "visible" }, g.start);
|
|
tl.fromTo(
|
|
grp,
|
|
{ opacity: 0, scale: 0.92 },
|
|
{ opacity: 1, scale: 1, duration: 0.2, ease: "back.out(1.5)" },
|
|
g.start,
|
|
);
|
|
|
|
groupWords.forEach(function (w, i) {
|
|
var wi = g.wordStart + i;
|
|
var wordEl = document.getElementById("bs-w-" + wi);
|
|
var isKW = KEYWORDS.has(wi);
|
|
|
|
tl.to(
|
|
wordEl,
|
|
{ color: isKW ? "#FFD700" : "#ffffff", scale: isKW ? 1.12 : 1.05, duration: 0.08 },
|
|
w.start,
|
|
);
|
|
tl.to(wordEl, { color: "rgba(255,255,255,0.4)", scale: 1.0, duration: 0.1 }, w.end);
|
|
|
|
if (isKW && particleData[wi]) {
|
|
particleData[wi].forEach(function (pd, pi) {
|
|
tl.set(pd.el, { x: 0, y: 0, opacity: 0 }, w.start);
|
|
tl.to(
|
|
pd.el,
|
|
{ x: pd.dx, y: pd.dy, opacity: 1, duration: 0.12, ease: "power3.out" },
|
|
w.start + pi * 0.018,
|
|
);
|
|
tl.to(
|
|
pd.el,
|
|
{ opacity: 0, duration: 0.45, ease: "power1.in" },
|
|
w.start + 0.12 + pi * 0.018,
|
|
);
|
|
tl.set(pd.el, { opacity: 0, x: 0, y: 0 }, w.start + 0.7);
|
|
});
|
|
}
|
|
});
|
|
|
|
tl.to(grp, { opacity: 0, duration: 0.14, ease: "power2.in" }, g.end - 0.14);
|
|
tl.set(grp, { opacity: 0, visibility: "hidden" }, g.end);
|
|
});
|
|
|
|
tl.seek(0);
|
|
window.__timelines["caption-particle-burst"] = tl;
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|