feat(registry): add 15 caption style catalog components

Add 15 caption overlay components to the registry, covering a wide
range of animation techniques:

Standalone origins (6):
- caption-pill-karaoke: pill container + karaoke word highlight
- caption-neon-accent: multi-color neon glow with wiggle drift
- caption-weight-shift: font-weight transition between lines
- caption-emoji-pop: emoji + stroked text + horizontal squeeze
- caption-editorial-emphasis: dual-font (Inter + Playfair) emphasis
- caption-parallax-layers: 3D text layering with vertical stretch

Gallery picks (9):
- caption-glitch-rgb: RGB chromatic aberration + CRT scanlines
- caption-typewriter: green terminal char-by-char with cursor
- caption-matrix-decode: scramble-reveal character animation
- caption-particle-burst: keyword particle explosions
- caption-texture-lava: flowing lava texture mask
- caption-clip-wipe: clip-path left-to-right wipe reveal
- caption-kinetic-slam: full-screen single-word slam
- caption-gradient-fill: gradient-clipped text + elastic bounce
- caption-neon-glow: cyan/magenta neon glow accents

Each component includes the reusable HTML, a demo composition,
and registry-item.json. All use a generic HyperFrames transcript.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Miguel Ángel
2026-05-17 19:57:25 +00:00
co-authored by Claude Sonnet 4.6
parent 7a1e8769a5
commit 78ddd2602b
47 changed files with 7675 additions and 0 deletions
@@ -0,0 +1,170 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Clip Wipe</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=Poppins:wght@800&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: #0a0a0a;
}
#clip-wipe {
position: relative; width: 1920px; height: 1080px;
overflow: hidden; background: #0a0a0a;
}
#wp-video {
position: absolute; inset: 0; z-index: 0;
width: 100%; height: 100%; object-fit: cover;
}
.wp-overlay {
position: absolute; inset: 0;
background: rgba(0,0,0,0.45); z-index: 1; pointer-events: none;
}
#wp-container {
position: absolute; inset: 0; z-index: 10; pointer-events: none;
}
.wp-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;
visibility: hidden;
}
.wp-word {
font-family: 'Poppins', sans-serif;
font-weight: 800; font-size: 88px;
text-transform: uppercase;
color: #ffffff;
display: inline-block; letter-spacing: 0.04em; line-height: 1;
clip-path: inset(0 100% 0 0);
will-change: clip-path, color, opacity;
}
</style>
</head>
<body>
<div
id="clip-wipe"
data-composition-id="caption-clip-wipe"
data-start="0"
data-duration="8"
data-fps="30"
data-width="1920"
data-height="1080"
>
<video id="wp-video" muted playsinline
data-start="0" data-duration="8" data-track-index="0" data-has-audio="false"></video>
<div class="wp-overlay"></div>
<div id="wp-container"></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
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}
];
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 container = document.getElementById('wp-container');
var tl = gsap.timeline({ paused: true });
GROUPS.forEach(function(g, gi) {
var groupWords = WORDS.slice(g.wordStart, g.wordEnd + 1);
var grp = document.createElement('div');
grp.className = 'wp-group';
grp.id = 'wp-grp-' + gi;
groupWords.forEach(function(w, i) {
var wi = g.wordStart + i;
var span = document.createElement('span');
span.className = 'wp-word';
span.id = 'wp-w-' + wi;
span.textContent = w.text.toUpperCase();
span.style.fontSize = '88px';
grp.appendChild(span);
});
container.appendChild(grp);
tl.set(grp, { visibility: 'visible' }, g.start);
groupWords.forEach(function(w, i) {
var wi = g.wordStart + i;
var wordEl = document.getElementById('wp-w-' + wi);
var isKW = KEYWORDS.has(wi);
tl.to(wordEl, { clipPath: 'inset(0 0% 0 0)', duration: 0.15, ease: 'power3.out' }, w.start);
if (isKW) {
tl.to(wordEl, { color: '#FFD700', duration: 0.05 }, w.start + 0.1);
}
tl.to(wordEl, { color: 'rgba(255,255,255,0.4)', duration: 0.12 }, w.end);
});
var wordEls = grp.querySelectorAll('.wp-word');
tl.to(wordEls, {
clipPath: 'inset(0 0% 0 100%)',
duration: 0.14, stagger: 0.04, ease: 'power2.in'
}, g.end - 0.2);
tl.set(grp, { visibility: 'hidden', opacity: 0 }, g.end);
});
tl.seek(0);
window.__timelines['clip-wipe'] = tl;
})();
</script>
</body>
</html>
+170
View File
@@ -0,0 +1,170 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Clip Wipe</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=Poppins:wght@800&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: #0a0a0a;
}
#clip-wipe {
position: relative; width: 1920px; height: 1080px;
overflow: hidden; background: #0a0a0a;
}
#wp-video {
position: absolute; inset: 0; z-index: 0;
width: 100%; height: 100%; object-fit: cover;
}
.wp-overlay {
position: absolute; inset: 0;
background: rgba(0,0,0,0.45); z-index: 1; pointer-events: none;
}
#wp-container {
position: absolute; inset: 0; z-index: 10; pointer-events: none;
}
.wp-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;
visibility: hidden;
}
.wp-word {
font-family: 'Poppins', sans-serif;
font-weight: 800; font-size: 88px;
text-transform: uppercase;
color: #ffffff;
display: inline-block; letter-spacing: 0.04em; line-height: 1;
clip-path: inset(0 100% 0 0);
will-change: clip-path, color, opacity;
}
</style>
</head>
<body>
<div
id="clip-wipe"
data-composition-id="caption-clip-wipe"
data-start="0"
data-duration="8"
data-fps="30"
data-width="1920"
data-height="1080"
>
<video id="wp-video" muted playsinline
data-start="0" data-duration="8" data-track-index="0" data-has-audio="false"></video>
<div class="wp-overlay"></div>
<div id="wp-container"></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
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}
];
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 container = document.getElementById('wp-container');
var tl = gsap.timeline({ paused: true });
GROUPS.forEach(function(g, gi) {
var groupWords = WORDS.slice(g.wordStart, g.wordEnd + 1);
var grp = document.createElement('div');
grp.className = 'wp-group';
grp.id = 'wp-grp-' + gi;
groupWords.forEach(function(w, i) {
var wi = g.wordStart + i;
var span = document.createElement('span');
span.className = 'wp-word';
span.id = 'wp-w-' + wi;
span.textContent = w.text.toUpperCase();
span.style.fontSize = '88px';
grp.appendChild(span);
});
container.appendChild(grp);
tl.set(grp, { visibility: 'visible' }, g.start);
groupWords.forEach(function(w, i) {
var wi = g.wordStart + i;
var wordEl = document.getElementById('wp-w-' + wi);
var isKW = KEYWORDS.has(wi);
tl.to(wordEl, { clipPath: 'inset(0 0% 0 0)', duration: 0.15, ease: 'power3.out' }, w.start);
if (isKW) {
tl.to(wordEl, { color: '#FFD700', duration: 0.05 }, w.start + 0.1);
}
tl.to(wordEl, { color: 'rgba(255,255,255,0.4)', duration: 0.12 }, w.end);
});
var wordEls = grp.querySelectorAll('.wp-word');
tl.to(wordEls, {
clipPath: 'inset(0 0% 0 100%)',
duration: 0.14, stagger: 0.04, ease: 'power2.in'
}, g.end - 0.2);
tl.set(grp, { visibility: 'hidden', opacity: 0 }, g.end);
});
tl.seek(0);
window.__timelines['clip-wipe'] = tl;
})();
</script>
</body>
</html>
@@ -0,0 +1,15 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "caption-clip-wipe",
"type": "hyperframes:component",
"title": "Clip Wipe",
"description": "Left-to-right clip-path wipe reveal per word",
"tags": ["captions", "caption-style", "wipe", "clip-path", "reveal"],
"files": [
{
"path": "caption-clip-wipe.html",
"target": "compositions/components/caption-clip-wipe.html",
"type": "hyperframes:snippet"
}
]
}
@@ -0,0 +1,303 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Editorial Emphasis</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=Inter:wght@400&family=Playfair+Display:ital,wght@0,800;1,800&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: #000;
font-family: "Inter", sans-serif;
}
#editorial-emphasis {
position: relative;
width: 1920px;
height: 1080px;
overflow: hidden;
background: #000;
}
#bg-video {
position: absolute;
inset: 0;
z-index: 1;
width: 100%;
height: 100%;
object-fit: cover;
}
.caption-layer {
position: absolute;
inset: 0;
z-index: 20;
pointer-events: none;
transform: translateZ(0);
}
.safe-zone {
position: absolute;
top: 580px;
left: 60px;
width: 1800px;
height: 430px;
display: flex;
align-items: flex-start;
justify-content: flex-start;
}
.caption-block {
position: absolute;
left: 0;
top: 0;
opacity: 0;
transform-origin: 0% 0%;
will-change: transform, opacity;
backface-visibility: hidden;
}
.caption-line {
display: flex;
align-items: baseline;
gap: 14px;
line-height: 1.1;
white-space: nowrap;
}
.caption-line + .caption-line {
margin-top: 8px;
}
.word {
display: inline-block;
color: #F5F0D0;
text-shadow: 0 2px 12px rgba(0, 0, 0, 0.6), 0 4px 24px rgba(0, 0, 0, 0.35);
will-change: transform, opacity;
backface-visibility: hidden;
}
.word--normal {
font-family: "Inter", sans-serif;
font-size: 86px;
font-weight: 400;
font-style: normal;
}
.word--italic {
font-family: "Inter", sans-serif;
font-size: 86px;
font-weight: 400;
font-style: italic;
}
.word--emphasis {
font-family: "Playfair Display", serif;
font-size: 180px;
font-weight: 800;
font-style: italic;
line-height: 0.9;
color: #F5F0D0;
}
</style>
</head>
<body>
<div
id="editorial-emphasis"
data-composition-id="caption-editorial-emphasis"
data-start="0"
data-duration="8"
data-fps="30"
data-width="1920"
data-height="1080"
>
<video
id="bg-video"
muted
playsinline
preload="auto"
data-start="0"
data-duration="8"
data-track-index="0"
data-has-audio="false"
></video>
<div class="caption-layer" aria-hidden="true">
<div id="caption-stage" class="safe-zone"></div>
</div>
</div>
<script>
var DURATION = 8;
var SAFE_WIDTH = 1800;
var ENTRY_DUR = 0.1;
var SLIDE_DUR = 0.2;
// Generic transcript words
var W = [
{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}
];
// Block layout: [wordIndex, type] — n=normal, i=italic, e=emphasis(large)
// Emphasis words: HyperFrames(8), HTML(12), professional(15), code(24), cinema.(27)
var BLOCKS = [
{ line1: [[0,"n"],[1,"n"],[2,"n"]], line2: [[3,"n"],[4,"n"]] },
{ line1: [[5,"n"],[6,"n"]], line2: [[7,"e"]] },
{ line1: [[8,"e"]], line2: null },
{ line1: [[9,"n"],[10,"n"],[11,"n"]], line2: [[12,"e"]] },
{ line1: [[13,"n"],[14,"n"]], line2: [[15,"e"]] },
{ line1: [[16,"n"],[17,"n"]], line2: null },
{ line1: [[18,"n"]], line2: [[19,"n"],[20,"n"]] },
{ line1: [[21,"n"],[22,"n"]], line2: null },
{ line1: [[23,"i"],[24,"e"]], line2: null },
{ line1: [[25,"n"],[26,"n"]], line2: [[27,"e"]] },
];
var CLASS_MAP = { n: "word word--normal", i: "word word--italic", e: "word word--emphasis" };
function buildBlocks() {
var stage = document.getElementById("caption-stage");
BLOCKS.forEach(function (block, bi) {
var el = document.createElement("div");
el.className = "caption-block";
el.id = "b" + bi;
var l1 = document.createElement("div");
l1.className = "caption-line";
l1.id = "b" + bi + "L1";
block.line1.forEach(function (pair, wi) {
var span = document.createElement("span");
span.className = CLASS_MAP[pair[1]];
span.id = "b" + bi + "L1w" + wi;
span.textContent = W[pair[0]].text;
l1.appendChild(span);
});
el.appendChild(l1);
if (block.line2) {
var l2 = document.createElement("div");
l2.className = "caption-line";
l2.id = "b" + bi + "L2";
block.line2.forEach(function (pair, wi) {
var span = document.createElement("span");
span.className = CLASS_MAP[pair[1]];
span.id = "b" + bi + "L2w" + wi;
span.textContent = W[pair[0]].text;
l2.appendChild(span);
});
el.appendChild(l2);
}
stage.appendChild(el);
});
}
buildBlocks();
// Scale blocks that exceed safe width
(function fitBlocks() {
BLOCKS.forEach(function (block, bi) {
var el = document.getElementById("b" + bi);
el.style.opacity = "1";
el.style.position = "relative";
var lines = el.querySelectorAll(".caption-line");
var maxW = 0;
lines.forEach(function (line) { if (line.scrollWidth > maxW) maxW = line.scrollWidth; });
if (maxW > SAFE_WIDTH) {
var s = SAFE_WIDTH / maxW;
el.style.transform = "scale(" + s + ")";
}
el.style.opacity = "0";
el.style.position = "";
});
})();
window.__timelines = window.__timelines || {};
var tl = gsap.timeline({ paused: true });
var allEls = BLOCKS.map(function (_, i) { return document.getElementById("b" + i); });
function blockStart(bi) { return W[BLOCKS[bi].line1[0][0]].start; }
BLOCKS.forEach(function (block, bi) {
var el = allEls[bi];
var start = blockStart(bi);
var nextStart = bi < BLOCKS.length - 1 ? blockStart(bi + 1) : DURATION;
allEls.forEach(function (other, oi) { if (oi !== bi) tl.set(other, { opacity: 0 }, start); });
tl.set(el, { opacity: 1 }, start);
block.line1.forEach(function (pair, wi) {
var wordEl = document.getElementById("b" + bi + "L1w" + wi);
var wordStart = W[pair[0]].start;
tl.set(wordEl, { opacity: 0, scale: 1.12, transformOrigin: "0% 100%" }, start);
tl.to(wordEl, { opacity: 1, scale: 1, duration: ENTRY_DUR, ease: "power2.out" }, wordStart);
});
if (block.line2) {
var hasEmphasis = block.line2.some(function (p) { return p[1] === "e"; });
var l2El = document.getElementById("b" + bi + "L2");
if (hasEmphasis) {
var l2Start = W[block.line2[0][0]].start;
tl.set(l2El, { opacity: 0, x: -1920 }, start);
tl.to(l2El, { opacity: 1, x: 0, duration: SLIDE_DUR, ease: "power2.out" }, l2Start);
} else {
tl.set(l2El, { opacity: 1 }, start);
block.line2.forEach(function (pair, wi) {
var wordEl = document.getElementById("b" + bi + "L2w" + wi);
var wordStart = W[pair[0]].start;
tl.set(wordEl, { opacity: 0, scale: 1.12, transformOrigin: "0% 100%" }, start);
tl.to(wordEl, { opacity: 1, scale: 1, duration: ENTRY_DUR, ease: "power2.out" }, wordStart);
});
}
}
tl.set(el, { opacity: 0 }, nextStart);
});
tl.to({}, { duration: DURATION }, 0);
window.__timelines["editorial-emphasis"] = tl;
</script>
</body>
</html>
+303
View File
@@ -0,0 +1,303 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Editorial Emphasis</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=Inter:wght@400&family=Playfair+Display:ital,wght@0,800;1,800&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: #000;
font-family: "Inter", sans-serif;
}
#editorial-emphasis {
position: relative;
width: 1920px;
height: 1080px;
overflow: hidden;
background: #000;
}
#bg-video {
position: absolute;
inset: 0;
z-index: 1;
width: 100%;
height: 100%;
object-fit: cover;
}
.caption-layer {
position: absolute;
inset: 0;
z-index: 20;
pointer-events: none;
transform: translateZ(0);
}
.safe-zone {
position: absolute;
top: 580px;
left: 60px;
width: 1800px;
height: 430px;
display: flex;
align-items: flex-start;
justify-content: flex-start;
}
.caption-block {
position: absolute;
left: 0;
top: 0;
opacity: 0;
transform-origin: 0% 0%;
will-change: transform, opacity;
backface-visibility: hidden;
}
.caption-line {
display: flex;
align-items: baseline;
gap: 14px;
line-height: 1.1;
white-space: nowrap;
}
.caption-line + .caption-line {
margin-top: 8px;
}
.word {
display: inline-block;
color: #F5F0D0;
text-shadow: 0 2px 12px rgba(0, 0, 0, 0.6), 0 4px 24px rgba(0, 0, 0, 0.35);
will-change: transform, opacity;
backface-visibility: hidden;
}
.word--normal {
font-family: "Inter", sans-serif;
font-size: 86px;
font-weight: 400;
font-style: normal;
}
.word--italic {
font-family: "Inter", sans-serif;
font-size: 86px;
font-weight: 400;
font-style: italic;
}
.word--emphasis {
font-family: "Playfair Display", serif;
font-size: 180px;
font-weight: 800;
font-style: italic;
line-height: 0.9;
color: #F5F0D0;
}
</style>
</head>
<body>
<div
id="editorial-emphasis"
data-composition-id="caption-editorial-emphasis"
data-start="0"
data-duration="8"
data-fps="30"
data-width="1920"
data-height="1080"
>
<video
id="bg-video"
muted
playsinline
preload="auto"
data-start="0"
data-duration="8"
data-track-index="0"
data-has-audio="false"
></video>
<div class="caption-layer" aria-hidden="true">
<div id="caption-stage" class="safe-zone"></div>
</div>
</div>
<script>
var DURATION = 8;
var SAFE_WIDTH = 1800;
var ENTRY_DUR = 0.1;
var SLIDE_DUR = 0.2;
// Generic transcript words
var W = [
{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}
];
// Block layout: [wordIndex, type] — n=normal, i=italic, e=emphasis(large)
// Emphasis words: HyperFrames(8), HTML(12), professional(15), code(24), cinema.(27)
var BLOCKS = [
{ line1: [[0,"n"],[1,"n"],[2,"n"]], line2: [[3,"n"],[4,"n"]] },
{ line1: [[5,"n"],[6,"n"]], line2: [[7,"e"]] },
{ line1: [[8,"e"]], line2: null },
{ line1: [[9,"n"],[10,"n"],[11,"n"]], line2: [[12,"e"]] },
{ line1: [[13,"n"],[14,"n"]], line2: [[15,"e"]] },
{ line1: [[16,"n"],[17,"n"]], line2: null },
{ line1: [[18,"n"]], line2: [[19,"n"],[20,"n"]] },
{ line1: [[21,"n"],[22,"n"]], line2: null },
{ line1: [[23,"i"],[24,"e"]], line2: null },
{ line1: [[25,"n"],[26,"n"]], line2: [[27,"e"]] },
];
var CLASS_MAP = { n: "word word--normal", i: "word word--italic", e: "word word--emphasis" };
function buildBlocks() {
var stage = document.getElementById("caption-stage");
BLOCKS.forEach(function (block, bi) {
var el = document.createElement("div");
el.className = "caption-block";
el.id = "b" + bi;
var l1 = document.createElement("div");
l1.className = "caption-line";
l1.id = "b" + bi + "L1";
block.line1.forEach(function (pair, wi) {
var span = document.createElement("span");
span.className = CLASS_MAP[pair[1]];
span.id = "b" + bi + "L1w" + wi;
span.textContent = W[pair[0]].text;
l1.appendChild(span);
});
el.appendChild(l1);
if (block.line2) {
var l2 = document.createElement("div");
l2.className = "caption-line";
l2.id = "b" + bi + "L2";
block.line2.forEach(function (pair, wi) {
var span = document.createElement("span");
span.className = CLASS_MAP[pair[1]];
span.id = "b" + bi + "L2w" + wi;
span.textContent = W[pair[0]].text;
l2.appendChild(span);
});
el.appendChild(l2);
}
stage.appendChild(el);
});
}
buildBlocks();
// Scale blocks that exceed safe width
(function fitBlocks() {
BLOCKS.forEach(function (block, bi) {
var el = document.getElementById("b" + bi);
el.style.opacity = "1";
el.style.position = "relative";
var lines = el.querySelectorAll(".caption-line");
var maxW = 0;
lines.forEach(function (line) { if (line.scrollWidth > maxW) maxW = line.scrollWidth; });
if (maxW > SAFE_WIDTH) {
var s = SAFE_WIDTH / maxW;
el.style.transform = "scale(" + s + ")";
}
el.style.opacity = "0";
el.style.position = "";
});
})();
window.__timelines = window.__timelines || {};
var tl = gsap.timeline({ paused: true });
var allEls = BLOCKS.map(function (_, i) { return document.getElementById("b" + i); });
function blockStart(bi) { return W[BLOCKS[bi].line1[0][0]].start; }
BLOCKS.forEach(function (block, bi) {
var el = allEls[bi];
var start = blockStart(bi);
var nextStart = bi < BLOCKS.length - 1 ? blockStart(bi + 1) : DURATION;
allEls.forEach(function (other, oi) { if (oi !== bi) tl.set(other, { opacity: 0 }, start); });
tl.set(el, { opacity: 1 }, start);
block.line1.forEach(function (pair, wi) {
var wordEl = document.getElementById("b" + bi + "L1w" + wi);
var wordStart = W[pair[0]].start;
tl.set(wordEl, { opacity: 0, scale: 1.12, transformOrigin: "0% 100%" }, start);
tl.to(wordEl, { opacity: 1, scale: 1, duration: ENTRY_DUR, ease: "power2.out" }, wordStart);
});
if (block.line2) {
var hasEmphasis = block.line2.some(function (p) { return p[1] === "e"; });
var l2El = document.getElementById("b" + bi + "L2");
if (hasEmphasis) {
var l2Start = W[block.line2[0][0]].start;
tl.set(l2El, { opacity: 0, x: -1920 }, start);
tl.to(l2El, { opacity: 1, x: 0, duration: SLIDE_DUR, ease: "power2.out" }, l2Start);
} else {
tl.set(l2El, { opacity: 1 }, start);
block.line2.forEach(function (pair, wi) {
var wordEl = document.getElementById("b" + bi + "L2w" + wi);
var wordStart = W[pair[0]].start;
tl.set(wordEl, { opacity: 0, scale: 1.12, transformOrigin: "0% 100%" }, start);
tl.to(wordEl, { opacity: 1, scale: 1, duration: ENTRY_DUR, ease: "power2.out" }, wordStart);
});
}
}
tl.set(el, { opacity: 0 }, nextStart);
});
tl.to({}, { duration: DURATION }, 0);
window.__timelines["editorial-emphasis"] = tl;
</script>
</body>
</html>
@@ -0,0 +1,15 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "caption-editorial-emphasis",
"type": "hyperframes:component",
"title": "Editorial Emphasis",
"description": "Dual-font system with dramatic size contrast for emphasis words",
"tags": ["captions", "caption-style", "editorial", "typography", "emphasis"],
"files": [
{
"path": "caption-editorial-emphasis.html",
"target": "compositions/components/caption-editorial-emphasis.html",
"type": "hyperframes:snippet"
}
]
}
@@ -0,0 +1,367 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Emoji Pop</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=Gabarito: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: #000000;
font-family: "Gabarito", Arial, sans-serif;
}
#emoji-pop {
position: relative;
width: 1920px;
height: 1080px;
overflow: hidden;
background: #000000;
}
#avatar-video {
position: absolute;
inset: 0;
z-index: 1;
width: 100%;
height: 100%;
object-fit: cover;
}
.caption-layer {
position: absolute;
inset: 0;
z-index: 20;
pointer-events: none;
transform: translateZ(0);
}
.safe-zone {
position: absolute;
bottom: 80px;
left: 50%;
transform: translateX(-50%);
width: 1400px;
height: 280px;
display: flex;
align-items: center;
justify-content: center;
}
.caption-group {
position: absolute;
left: 0;
top: 0;
width: 1400px;
height: 280px;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transform-origin: 50% 50%;
will-change: transform;
backface-visibility: hidden;
}
.caption-emoji {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, calc(-50% - 70px));
font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
font-size: 56px;
line-height: 1;
text-align: center;
}
.caption-line {
display: flex;
align-items: center;
justify-content: center;
gap: 14px;
width: max-content;
max-width: 1400px;
line-height: 1.1;
white-space: nowrap;
}
.caption-word {
display: inline-block;
color: #ffffff;
font-family: "Gabarito", Arial, sans-serif;
font-weight: 900;
font-size: 72px;
line-height: 1.1;
letter-spacing: -0.03em;
text-transform: uppercase;
-webkit-text-stroke: 3px #000000;
paint-order: stroke fill;
}
</style>
</head>
<body>
<div
id="emoji-pop"
data-composition-id="caption-emoji-pop"
data-start="0"
data-duration="8"
data-fps="30"
data-width="1920"
data-height="1080"
>
<video
id="avatar-video"
muted
playsinline
data-start="0"
data-duration="8"
data-track-index="0"
data-has-audio="false"
></video>
<div class="caption-layer" aria-hidden="true">
<div id="caption-stage" class="safe-zone"></div>
</div>
</div>
<script>
var DURATION = 8;
var SAFE_ZONE_WIDTH = 1400;
var CAPTION_FONT_SIZE = 72;
var MAX_WORDS_PER_GROUP = 3;
var WORD_SPACING = 14;
var PAUSE_THRESHOLD = 0.1;
var ENTRY_FRAMES = 4;
var EXIT_FRAMES = 3;
var FPS = 30;
var ENTRY_DURATION = ENTRY_FRAMES / FPS;
var EXIT_DURATION = EXIT_FRAMES / FPS;
var FONT_WIDTH_SAFETY = 1.14;
var PRIMARY_COLOR = "#FFFFFF";
var ACCENT_COLORS = ["#FF76FF", "#FF0002", "#B2F7FF"];
var KEYWORDS = new Set(["hyperframes", "html", "professional", "code", "cinema"]);
var TRANSITION_WORDS = new Set([
"the", "a", "an", "and", "or", "but", "is", "are", "was", "were",
"to", "of", "in", "on", "at", "for", "with", "by", "from", "as",
"it", "its", "this", "that", "these", "those", "be", "been",
"have", "has", "had", "do", "does", "did", "will", "would", "could",
"should", "may", "might", "must", "can", "if", "then", "so", "just",
"not", "no", "yes", "up", "out", "about"
]);
// Emoji map for key words
var WORD_EMOJI = {
"hyperframes": "🎬",
"html": "💻",
"professional": "✨",
"code": "⌨️",
"cinema": "🎥",
"video": "📹",
"frame": "🖼️"
};
var TRANSCRIPT = [
{"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}
];
function normalizeWords(rawWords) {
return rawWords.map(function (item) {
var text = String(item.word || item.text || "").trim();
var clean = text.toLowerCase().replace(/[^a-z]/g, "");
return {
text: text,
start: Math.max(0, Number(item.start) || 0),
end: Math.min(DURATION, Math.max(Number(item.start) || 0, Number(item.end) || 0)),
emoji: WORD_EMOJI[clean] || null,
};
}).filter(function (word) { return word.text.length > 0; });
}
function seededRandom(seed) {
var x = Math.sin(seed * 9999) * 10000;
return x - Math.floor(x);
}
function assignWordColors(words, groupIndex) {
return words.map(function (word, index) {
var clean = word.text.toLowerCase().replace(/[^a-z]/g, "");
if (KEYWORDS.has(clean)) {
var ci = Math.floor(seededRandom(groupIndex * 23 + index * 31) * ACCENT_COLORS.length);
return ACCENT_COLORS[ci];
}
if (TRANSITION_WORDS.has(clean)) return PRIMARY_COLOR;
var rand = seededRandom(groupIndex * 13 + index);
if (rand > 0.55) {
var ci2 = Math.floor(rand * ACCENT_COLORS.length) % ACCENT_COLORS.length;
return ACCENT_COLORS[ci2];
}
return PRIMARY_COLOR;
});
}
var measureCanvas = document.createElement("canvas");
var measureContext = measureCanvas.getContext("2d");
function measureWord(word, fontSize) {
measureContext.font = "900 " + fontSize + "px Gabarito";
return measureContext.measureText(word.text.toUpperCase()).width * FONT_WIDTH_SAFETY;
}
function measureLineWidth(words, fontSize) {
return words.reduce(function (total, word, index) {
return total + measureWord(word, fontSize) + (index ? WORD_SPACING : 0);
}, 0);
}
function fitsLine(words) {
return measureLineWidth(words, CAPTION_FONT_SIZE) <= SAFE_ZONE_WIDTH;
}
function makeGroups(words) {
var groups = [];
var current = [];
words.forEach(function (word, index) {
var nextWord = words[index + 1];
var testGroup = current.concat([word]);
if (testGroup.length > MAX_WORDS_PER_GROUP || !fitsLine(testGroup)) {
if (current.length) groups.push(makeGroup(current, groups.length));
current = [word];
return;
}
current.push(word);
var punctuation = /[,.:!?]$/.test(word.text);
var pause = nextWord ? nextWord.start - word.end : 0;
if (!nextWord || punctuation || pause >= PAUSE_THRESHOLD || current.length >= MAX_WORDS_PER_GROUP) {
groups.push(makeGroup(current, groups.length));
current = [];
}
});
if (current.length) groups.push(makeGroup(current, groups.length));
return groups;
}
function makeGroup(words, groupIndex) {
var colors = assignWordColors(words, groupIndex);
var emoji = null;
for (var i = 0; i < words.length; i++) {
if (words[i].emoji) { emoji = words[i].emoji; break; }
}
return { words: words.slice(), colors: colors, emoji: emoji, start: words[0].start, end: words[words.length - 1].end };
}
function shadowForColor(color) {
var hex = color.replace("#", "");
var r = parseInt(hex.slice(0, 2), 16);
var g = parseInt(hex.slice(2, 4), 16);
var b = parseInt(hex.slice(4, 6), 16);
return "0 4px 8px rgba(0,0,0,0.7), 0 0 2px rgba(" + r + "," + g + "," + b + ",1), 0 0 8px rgba(" + r + "," + g + "," + b + ",0.6)";
}
function buildCaptions(groups) {
var stage = document.getElementById("caption-stage");
groups.forEach(function (group, groupIndex) {
var groupEl = document.createElement("div");
groupEl.id = "caption-group-" + groupIndex;
groupEl.className = "caption-group";
if (group.emoji) {
var emojiEl = document.createElement("div");
emojiEl.className = "caption-emoji";
emojiEl.textContent = group.emoji;
groupEl.appendChild(emojiEl);
}
var lineEl = document.createElement("div");
lineEl.className = "caption-line";
group.words.forEach(function (word, wordIndex) {
var color = group.colors[wordIndex] || PRIMARY_COLOR;
var wordEl = document.createElement("span");
wordEl.className = "caption-word";
wordEl.textContent = word.text.toUpperCase();
wordEl.style.color = color;
wordEl.style.textShadow = shadowForColor(color);
lineEl.appendChild(wordEl);
});
groupEl.appendChild(lineEl);
stage.appendChild(groupEl);
});
}
var WORDS = normalizeWords(TRANSCRIPT);
var GROUPS = makeGroups(WORDS);
buildCaptions(GROUPS);
window.__timelines = window.__timelines || {};
var tl = gsap.timeline({ paused: true });
var visibleStarts = GROUPS.map(function (group, groupIndex) {
var prev = GROUPS[groupIndex - 1];
var availableGap = prev ? Math.max(0, group.start - prev.end) : ENTRY_DURATION;
var entryLead = Math.min(ENTRY_DURATION, availableGap);
return Math.max(0, group.start - entryLead);
});
var allGroupEls = GROUPS.map(function (_, i) { return document.getElementById("caption-group-" + i); });
GROUPS.forEach(function (group, groupIndex) {
var groupEl = allGroupEls[groupIndex];
var visibleStart = visibleStarts[groupIndex];
var visibleEnd = groupIndex < GROUPS.length - 1 ? visibleStarts[groupIndex + 1] : DURATION;
var exitStart = Math.max(visibleStart, visibleEnd - EXIT_DURATION);
allGroupEls.forEach(function (otherEl, otherIndex) {
if (otherIndex !== groupIndex) tl.set(otherEl, { opacity: 0 }, visibleStart);
});
tl.set(groupEl, { opacity: 0, scaleX: 0.8, scaleY: 1 }, visibleStart);
tl.to(groupEl, { opacity: 1, scaleX: 1, duration: ENTRY_DURATION, ease: "power3.out", force3D: true }, visibleStart);
tl.to(groupEl, { scaleX: 0.75, opacity: 0.8, duration: EXIT_DURATION, ease: "power2.in", force3D: true }, exitStart);
tl.set(groupEl, { opacity: 0 }, visibleEnd);
});
window.__timelines["emoji-pop"] = tl;
</script>
</body>
</html>
+367
View File
@@ -0,0 +1,367 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Emoji Pop</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=Gabarito: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: #000000;
font-family: "Gabarito", Arial, sans-serif;
}
#emoji-pop {
position: relative;
width: 1920px;
height: 1080px;
overflow: hidden;
background: #000000;
}
#avatar-video {
position: absolute;
inset: 0;
z-index: 1;
width: 100%;
height: 100%;
object-fit: cover;
}
.caption-layer {
position: absolute;
inset: 0;
z-index: 20;
pointer-events: none;
transform: translateZ(0);
}
.safe-zone {
position: absolute;
bottom: 80px;
left: 50%;
transform: translateX(-50%);
width: 1400px;
height: 280px;
display: flex;
align-items: center;
justify-content: center;
}
.caption-group {
position: absolute;
left: 0;
top: 0;
width: 1400px;
height: 280px;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transform-origin: 50% 50%;
will-change: transform;
backface-visibility: hidden;
}
.caption-emoji {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, calc(-50% - 70px));
font-family: "Apple Color Emoji", "Segoe UI Emoji", "Noto Color Emoji", sans-serif;
font-size: 56px;
line-height: 1;
text-align: center;
}
.caption-line {
display: flex;
align-items: center;
justify-content: center;
gap: 14px;
width: max-content;
max-width: 1400px;
line-height: 1.1;
white-space: nowrap;
}
.caption-word {
display: inline-block;
color: #ffffff;
font-family: "Gabarito", Arial, sans-serif;
font-weight: 900;
font-size: 72px;
line-height: 1.1;
letter-spacing: -0.03em;
text-transform: uppercase;
-webkit-text-stroke: 3px #000000;
paint-order: stroke fill;
}
</style>
</head>
<body>
<div
id="emoji-pop"
data-composition-id="caption-emoji-pop"
data-start="0"
data-duration="8"
data-fps="30"
data-width="1920"
data-height="1080"
>
<video
id="avatar-video"
muted
playsinline
data-start="0"
data-duration="8"
data-track-index="0"
data-has-audio="false"
></video>
<div class="caption-layer" aria-hidden="true">
<div id="caption-stage" class="safe-zone"></div>
</div>
</div>
<script>
var DURATION = 8;
var SAFE_ZONE_WIDTH = 1400;
var CAPTION_FONT_SIZE = 72;
var MAX_WORDS_PER_GROUP = 3;
var WORD_SPACING = 14;
var PAUSE_THRESHOLD = 0.1;
var ENTRY_FRAMES = 4;
var EXIT_FRAMES = 3;
var FPS = 30;
var ENTRY_DURATION = ENTRY_FRAMES / FPS;
var EXIT_DURATION = EXIT_FRAMES / FPS;
var FONT_WIDTH_SAFETY = 1.14;
var PRIMARY_COLOR = "#FFFFFF";
var ACCENT_COLORS = ["#FF76FF", "#FF0002", "#B2F7FF"];
var KEYWORDS = new Set(["hyperframes", "html", "professional", "code", "cinema"]);
var TRANSITION_WORDS = new Set([
"the", "a", "an", "and", "or", "but", "is", "are", "was", "were",
"to", "of", "in", "on", "at", "for", "with", "by", "from", "as",
"it", "its", "this", "that", "these", "those", "be", "been",
"have", "has", "had", "do", "does", "did", "will", "would", "could",
"should", "may", "might", "must", "can", "if", "then", "so", "just",
"not", "no", "yes", "up", "out", "about"
]);
// Emoji map for key words
var WORD_EMOJI = {
"hyperframes": "🎬",
"html": "💻",
"professional": "✨",
"code": "⌨️",
"cinema": "🎥",
"video": "📹",
"frame": "🖼️"
};
var TRANSCRIPT = [
{"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}
];
function normalizeWords(rawWords) {
return rawWords.map(function (item) {
var text = String(item.word || item.text || "").trim();
var clean = text.toLowerCase().replace(/[^a-z]/g, "");
return {
text: text,
start: Math.max(0, Number(item.start) || 0),
end: Math.min(DURATION, Math.max(Number(item.start) || 0, Number(item.end) || 0)),
emoji: WORD_EMOJI[clean] || null,
};
}).filter(function (word) { return word.text.length > 0; });
}
function seededRandom(seed) {
var x = Math.sin(seed * 9999) * 10000;
return x - Math.floor(x);
}
function assignWordColors(words, groupIndex) {
return words.map(function (word, index) {
var clean = word.text.toLowerCase().replace(/[^a-z]/g, "");
if (KEYWORDS.has(clean)) {
var ci = Math.floor(seededRandom(groupIndex * 23 + index * 31) * ACCENT_COLORS.length);
return ACCENT_COLORS[ci];
}
if (TRANSITION_WORDS.has(clean)) return PRIMARY_COLOR;
var rand = seededRandom(groupIndex * 13 + index);
if (rand > 0.55) {
var ci2 = Math.floor(rand * ACCENT_COLORS.length) % ACCENT_COLORS.length;
return ACCENT_COLORS[ci2];
}
return PRIMARY_COLOR;
});
}
var measureCanvas = document.createElement("canvas");
var measureContext = measureCanvas.getContext("2d");
function measureWord(word, fontSize) {
measureContext.font = "900 " + fontSize + "px Gabarito";
return measureContext.measureText(word.text.toUpperCase()).width * FONT_WIDTH_SAFETY;
}
function measureLineWidth(words, fontSize) {
return words.reduce(function (total, word, index) {
return total + measureWord(word, fontSize) + (index ? WORD_SPACING : 0);
}, 0);
}
function fitsLine(words) {
return measureLineWidth(words, CAPTION_FONT_SIZE) <= SAFE_ZONE_WIDTH;
}
function makeGroups(words) {
var groups = [];
var current = [];
words.forEach(function (word, index) {
var nextWord = words[index + 1];
var testGroup = current.concat([word]);
if (testGroup.length > MAX_WORDS_PER_GROUP || !fitsLine(testGroup)) {
if (current.length) groups.push(makeGroup(current, groups.length));
current = [word];
return;
}
current.push(word);
var punctuation = /[,.:!?]$/.test(word.text);
var pause = nextWord ? nextWord.start - word.end : 0;
if (!nextWord || punctuation || pause >= PAUSE_THRESHOLD || current.length >= MAX_WORDS_PER_GROUP) {
groups.push(makeGroup(current, groups.length));
current = [];
}
});
if (current.length) groups.push(makeGroup(current, groups.length));
return groups;
}
function makeGroup(words, groupIndex) {
var colors = assignWordColors(words, groupIndex);
var emoji = null;
for (var i = 0; i < words.length; i++) {
if (words[i].emoji) { emoji = words[i].emoji; break; }
}
return { words: words.slice(), colors: colors, emoji: emoji, start: words[0].start, end: words[words.length - 1].end };
}
function shadowForColor(color) {
var hex = color.replace("#", "");
var r = parseInt(hex.slice(0, 2), 16);
var g = parseInt(hex.slice(2, 4), 16);
var b = parseInt(hex.slice(4, 6), 16);
return "0 4px 8px rgba(0,0,0,0.7), 0 0 2px rgba(" + r + "," + g + "," + b + ",1), 0 0 8px rgba(" + r + "," + g + "," + b + ",0.6)";
}
function buildCaptions(groups) {
var stage = document.getElementById("caption-stage");
groups.forEach(function (group, groupIndex) {
var groupEl = document.createElement("div");
groupEl.id = "caption-group-" + groupIndex;
groupEl.className = "caption-group";
if (group.emoji) {
var emojiEl = document.createElement("div");
emojiEl.className = "caption-emoji";
emojiEl.textContent = group.emoji;
groupEl.appendChild(emojiEl);
}
var lineEl = document.createElement("div");
lineEl.className = "caption-line";
group.words.forEach(function (word, wordIndex) {
var color = group.colors[wordIndex] || PRIMARY_COLOR;
var wordEl = document.createElement("span");
wordEl.className = "caption-word";
wordEl.textContent = word.text.toUpperCase();
wordEl.style.color = color;
wordEl.style.textShadow = shadowForColor(color);
lineEl.appendChild(wordEl);
});
groupEl.appendChild(lineEl);
stage.appendChild(groupEl);
});
}
var WORDS = normalizeWords(TRANSCRIPT);
var GROUPS = makeGroups(WORDS);
buildCaptions(GROUPS);
window.__timelines = window.__timelines || {};
var tl = gsap.timeline({ paused: true });
var visibleStarts = GROUPS.map(function (group, groupIndex) {
var prev = GROUPS[groupIndex - 1];
var availableGap = prev ? Math.max(0, group.start - prev.end) : ENTRY_DURATION;
var entryLead = Math.min(ENTRY_DURATION, availableGap);
return Math.max(0, group.start - entryLead);
});
var allGroupEls = GROUPS.map(function (_, i) { return document.getElementById("caption-group-" + i); });
GROUPS.forEach(function (group, groupIndex) {
var groupEl = allGroupEls[groupIndex];
var visibleStart = visibleStarts[groupIndex];
var visibleEnd = groupIndex < GROUPS.length - 1 ? visibleStarts[groupIndex + 1] : DURATION;
var exitStart = Math.max(visibleStart, visibleEnd - EXIT_DURATION);
allGroupEls.forEach(function (otherEl, otherIndex) {
if (otherIndex !== groupIndex) tl.set(otherEl, { opacity: 0 }, visibleStart);
});
tl.set(groupEl, { opacity: 0, scaleX: 0.8, scaleY: 1 }, visibleStart);
tl.to(groupEl, { opacity: 1, scaleX: 1, duration: ENTRY_DURATION, ease: "power3.out", force3D: true }, visibleStart);
tl.to(groupEl, { scaleX: 0.75, opacity: 0.8, duration: EXIT_DURATION, ease: "power2.in", force3D: true }, exitStart);
tl.set(groupEl, { opacity: 0 }, visibleEnd);
});
window.__timelines["emoji-pop"] = tl;
</script>
</body>
</html>
@@ -0,0 +1,15 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "caption-emoji-pop",
"type": "hyperframes:component",
"title": "Emoji Pop",
"description": "Emoji integration with stroked text and horizontal squeeze entrance",
"tags": ["captions", "caption-style", "emoji", "social"],
"files": [
{
"path": "caption-emoji-pop.html",
"target": "compositions/components/caption-emoji-pop.html",
"type": "hyperframes:snippet"
}
]
}
@@ -0,0 +1,187 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Glitch RGB</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=Space+Grotesk:wght@700&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: #0a0a0a;
}
#glitch-rgb {
position: relative; width: 1920px; height: 1080px;
overflow: hidden; background: #0a0a0a;
}
#gl-video {
position: absolute; inset: 0; z-index: 0;
width: 100%; height: 100%; object-fit: cover;
}
.gl-overlay {
position: absolute; inset: 0;
background: rgba(0,0,0,0.45); z-index: 1; pointer-events: none;
}
.gl-scanlines {
position: absolute; inset: 0; z-index: 5; pointer-events: none;
background: repeating-linear-gradient(
to bottom,
transparent 0px, transparent 3px,
rgba(0,0,0,0.08) 3px, rgba(0,0,0,0.08) 4px
);
}
#gl-container {
position: absolute; inset: 0; z-index: 10; pointer-events: none;
}
.gl-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;
}
.gl-word {
font-family: 'Space Grotesk', sans-serif;
font-weight: 700; font-size: 88px;
text-transform: uppercase;
color: rgba(180,230,255,0.45);
display: inline-block; letter-spacing: 0.05em; line-height: 1;
will-change: transform, text-shadow, color;
}
</style>
</head>
<body>
<div
id="glitch-rgb"
data-composition-id="caption-glitch-rgb"
data-start="0"
data-duration="8"
data-fps="30"
data-width="1920"
data-height="1080"
>
<video id="gl-video" muted playsinline
data-start="0" data-duration="8" data-track-index="0" data-has-audio="false"></video>
<div class="gl-overlay"></div>
<div class="gl-scanlines"></div>
<div id="gl-container"></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
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 white + glow on activation
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 BASE_FONT = 88;
var container = document.getElementById('gl-container');
var tl = gsap.timeline({ paused: true });
GROUPS.forEach(function(g, gi) {
var groupWords = WORDS.slice(g.wordStart, g.wordEnd + 1);
var grp = document.createElement('div');
grp.className = 'gl-group';
grp.id = 'gl-grp-' + gi;
groupWords.forEach(function(w, i) {
var wi = g.wordStart + i;
var span = document.createElement('span');
span.className = 'gl-word';
span.id = 'gl-w-' + wi;
span.textContent = w.text.toUpperCase();
span.style.fontSize = BASE_FONT + 'px';
grp.appendChild(span);
});
container.appendChild(grp);
tl.set(grp, { visibility: 'visible' }, g.start);
tl.fromTo(grp,
{ opacity: 0, x: -8 },
{ opacity: 1, x: 0, duration: 0.18, ease: 'power3.out' },
g.start
);
groupWords.forEach(function(w, i) {
var wi = g.wordStart + i;
var wordEl = document.getElementById('gl-w-' + wi);
var isKW = KEYWORDS.has(wi);
var rng = mulberry32(wi * 777 + 13);
var j1 = (rng() - 0.5) * 10;
var j2 = (rng() - 0.5) * 8;
var j3 = (rng() - 0.5) * 6;
var shadowMag = 3 + rng() * 4;
var sm = shadowMag.toFixed(1);
tl.to(wordEl, { x: j1, color: '#ffffff', textShadow: sm + 'px 0 #ff003c, -' + sm + 'px 0 #00e5ff', duration: 0.045, ease: 'none' }, w.start);
tl.to(wordEl, { x: j2, textShadow: '-' + (shadowMag * 0.7).toFixed(1) + 'px 0 #ff003c, ' + (shadowMag * 0.7).toFixed(1) + 'px 0 #00e5ff', duration: 0.038, ease: 'none' }, w.start + 0.045);
tl.to(wordEl, { x: j3, textShadow: sm + 'px 0 #ff003c, -' + sm + 'px 0 #00e5ff', duration: 0.03, ease: 'none' }, w.start + 0.083);
tl.to(wordEl, { x: 0, textShadow: 'none', color: isKW ? '#ffffff' : '#00e5ff', duration: 0.05, ease: 'power2.out' }, w.start + 0.113);
tl.to(wordEl, { color: 'rgba(180,230,255,0.4)', textShadow: 'none', duration: 0.12 }, w.end);
});
tl.to(grp, { opacity: 0, x: 8, duration: 0.15 }, g.end - 0.15);
tl.set(grp, { opacity: 0, visibility: 'hidden' }, g.end);
});
tl.seek(0);
window.__timelines['glitch-rgb'] = tl;
})();
</script>
</body>
</html>
+187
View File
@@ -0,0 +1,187 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Glitch RGB</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=Space+Grotesk:wght@700&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: #0a0a0a;
}
#glitch-rgb {
position: relative; width: 1920px; height: 1080px;
overflow: hidden; background: #0a0a0a;
}
#gl-video {
position: absolute; inset: 0; z-index: 0;
width: 100%; height: 100%; object-fit: cover;
}
.gl-overlay {
position: absolute; inset: 0;
background: rgba(0,0,0,0.45); z-index: 1; pointer-events: none;
}
.gl-scanlines {
position: absolute; inset: 0; z-index: 5; pointer-events: none;
background: repeating-linear-gradient(
to bottom,
transparent 0px, transparent 3px,
rgba(0,0,0,0.08) 3px, rgba(0,0,0,0.08) 4px
);
}
#gl-container {
position: absolute; inset: 0; z-index: 10; pointer-events: none;
}
.gl-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;
}
.gl-word {
font-family: 'Space Grotesk', sans-serif;
font-weight: 700; font-size: 88px;
text-transform: uppercase;
color: rgba(180,230,255,0.45);
display: inline-block; letter-spacing: 0.05em; line-height: 1;
will-change: transform, text-shadow, color;
}
</style>
</head>
<body>
<div
id="glitch-rgb"
data-composition-id="caption-glitch-rgb"
data-start="0"
data-duration="8"
data-fps="30"
data-width="1920"
data-height="1080"
>
<video id="gl-video" muted playsinline
data-start="0" data-duration="8" data-track-index="0" data-has-audio="false"></video>
<div class="gl-overlay"></div>
<div class="gl-scanlines"></div>
<div id="gl-container"></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
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 white + glow on activation
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 BASE_FONT = 88;
var container = document.getElementById('gl-container');
var tl = gsap.timeline({ paused: true });
GROUPS.forEach(function(g, gi) {
var groupWords = WORDS.slice(g.wordStart, g.wordEnd + 1);
var grp = document.createElement('div');
grp.className = 'gl-group';
grp.id = 'gl-grp-' + gi;
groupWords.forEach(function(w, i) {
var wi = g.wordStart + i;
var span = document.createElement('span');
span.className = 'gl-word';
span.id = 'gl-w-' + wi;
span.textContent = w.text.toUpperCase();
span.style.fontSize = BASE_FONT + 'px';
grp.appendChild(span);
});
container.appendChild(grp);
tl.set(grp, { visibility: 'visible' }, g.start);
tl.fromTo(grp,
{ opacity: 0, x: -8 },
{ opacity: 1, x: 0, duration: 0.18, ease: 'power3.out' },
g.start
);
groupWords.forEach(function(w, i) {
var wi = g.wordStart + i;
var wordEl = document.getElementById('gl-w-' + wi);
var isKW = KEYWORDS.has(wi);
var rng = mulberry32(wi * 777 + 13);
var j1 = (rng() - 0.5) * 10;
var j2 = (rng() - 0.5) * 8;
var j3 = (rng() - 0.5) * 6;
var shadowMag = 3 + rng() * 4;
var sm = shadowMag.toFixed(1);
tl.to(wordEl, { x: j1, color: '#ffffff', textShadow: sm + 'px 0 #ff003c, -' + sm + 'px 0 #00e5ff', duration: 0.045, ease: 'none' }, w.start);
tl.to(wordEl, { x: j2, textShadow: '-' + (shadowMag * 0.7).toFixed(1) + 'px 0 #ff003c, ' + (shadowMag * 0.7).toFixed(1) + 'px 0 #00e5ff', duration: 0.038, ease: 'none' }, w.start + 0.045);
tl.to(wordEl, { x: j3, textShadow: sm + 'px 0 #ff003c, -' + sm + 'px 0 #00e5ff', duration: 0.03, ease: 'none' }, w.start + 0.083);
tl.to(wordEl, { x: 0, textShadow: 'none', color: isKW ? '#ffffff' : '#00e5ff', duration: 0.05, ease: 'power2.out' }, w.start + 0.113);
tl.to(wordEl, { color: 'rgba(180,230,255,0.4)', textShadow: 'none', duration: 0.12 }, w.end);
});
tl.to(grp, { opacity: 0, x: 8, duration: 0.15 }, g.end - 0.15);
tl.set(grp, { opacity: 0, visibility: 'hidden' }, g.end);
});
tl.seek(0);
window.__timelines['glitch-rgb'] = tl;
})();
</script>
</body>
</html>
@@ -0,0 +1,15 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "caption-glitch-rgb",
"type": "hyperframes:component",
"title": "Glitch RGB",
"description": "RGB chromatic aberration with CRT scanline overlay",
"tags": ["captions", "caption-style", "glitch", "cyber", "tech"],
"files": [
{
"path": "caption-glitch-rgb.html",
"target": "compositions/components/caption-glitch-rgb.html",
"type": "hyperframes:snippet"
}
]
}
@@ -0,0 +1,187 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Gradient Fill</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=Nunito: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: #0a0a0a;
}
#gradient-fill {
position: relative; width: 1920px; height: 1080px;
overflow: hidden; background: #0a0a0a;
}
#gr-video {
position: absolute; inset: 0; z-index: 0;
width: 100%; height: 100%; object-fit: cover;
}
.gr-overlay {
position: absolute; inset: 0;
background: rgba(0,0,0,0.42); z-index: 1; pointer-events: none;
}
#gr-container {
position: absolute; inset: 0; z-index: 10; pointer-events: none;
}
.gr-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 80px;
opacity: 0; visibility: hidden;
}
.gr-word {
font-family: 'Nunito', sans-serif;
font-weight: 900; font-size: 100px;
display: inline-block; letter-spacing: 0.04em; line-height: 1;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
opacity: 0.3;
}
</style>
</head>
<body>
<div
id="gradient-fill"
data-composition-id="caption-gradient-fill"
data-start="0"
data-duration="8"
data-fps="30"
data-width="1920"
data-height="1080"
>
<video id="gr-video" muted playsinline
data-start="0" data-duration="8" data-track-index="0" data-has-audio="false"></video>
<div class="gr-overlay"></div>
<div id="gr-container"></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
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}
];
var GROUPS = [
{wordStart:0, wordEnd:3, start:0.0, end:1.05},
{wordStart:4, wordEnd:7, start:1.1, end:2.05},
{wordStart:8, wordEnd:8, start:2.1, end:2.6},
{wordStart:9, wordEnd:12, start:2.65, end:3.5},
{wordStart:13, wordEnd:15, start:3.55, end:4.4},
{wordStart:16, wordEnd:16, start:3.95, end:4.75},
{wordStart:17, wordEnd:19, start:4.9, end:5.4},
{wordStart:20, wordEnd:22, start:5.65, end:6.2},
{wordStart:23, wordEnd:24, start:6.35, end:6.75},
{wordStart:25, wordEnd:27, start:6.8, end:7.8},
];
// Enhanced gradient palette with accent for keywords
var GRADS = [
"linear-gradient(135deg,#FF6B6B,#FFD700)",
"linear-gradient(135deg,#4ADE80,#22D3EE)",
"linear-gradient(135deg,#818CF8,#E879F9)",
"linear-gradient(135deg,#FFD700,#FF6B35)",
"linear-gradient(135deg,#22D3EE,#4ADE80)",
"linear-gradient(135deg,#E879F9,#818CF8)",
"linear-gradient(135deg,#FF3366,#FF6B35)"
];
var KEYWORD_GRADS = {
8: "linear-gradient(135deg,#FF6B6B,#FFD700,#FF6B6B)", // HyperFrames
12: "linear-gradient(135deg,#22D3EE,#ffffff,#22D3EE)", // HTML
15: "linear-gradient(135deg,#818CF8,#E879F9,#818CF8)", // professional
24: "linear-gradient(135deg,#00F5D4,#22D3EE,#00F5D4)", // code
27: "linear-gradient(135deg,#FFD700,#FF6B35,#FFD700)" // cinema.
};
var container = document.getElementById('gr-container');
var tl = gsap.timeline({ paused: true });
GROUPS.forEach(function(g, gi) {
var groupWords = WORDS.slice(g.wordStart, g.wordEnd + 1);
var grp = document.createElement('div');
grp.className = 'gr-group';
grp.id = 'gr-grp-' + gi;
groupWords.forEach(function(w, i) {
var wi = g.wordStart + i;
var span = document.createElement('span');
span.className = 'gr-word';
span.id = 'gr-w-' + wi;
span.textContent = w.text;
span.style.fontSize = '100px';
var grad = KEYWORD_GRADS[wi] || GRADS[wi % 7];
span.style.background = grad;
span.style.webkitBackgroundClip = 'text';
span.style.backgroundClip = 'text';
span.style.color = 'transparent';
grp.appendChild(span);
});
container.appendChild(grp);
var gs = g.start;
groupWords.forEach(function(w, i) {
var wi = g.wordStart + i;
var el = document.getElementById('gr-w-' + wi);
tl.set(el, { opacity: 0, y: 55, scale: 0.35 }, gs);
tl.fromTo(el,
{ y: 55, scale: 0.35, opacity: 0 },
{ y: 0, scale: 1, opacity: 0.35, duration: 0.55, ease: 'elastic.out(1.6,0.4)' },
gs + i * 0.05
);
});
tl.set(grp, { visibility: 'visible', opacity: 1 }, gs);
groupWords.forEach(function(w, i) {
var wi = g.wordStart + i;
var el = document.getElementById('gr-w-' + wi);
tl.to(el, { opacity: 1, scale: 1.1, duration: 0.08 }, w.start);
tl.to(el, { opacity: 0.3, scale: 1, duration: 0.08 }, w.end);
});
tl.to(grp, { y: -26, opacity: 0, duration: 0.18 }, g.end - 0.18);
tl.set(grp, { opacity: 0, visibility: 'hidden' }, g.end);
});
window.__timelines['gradient-fill'] = tl;
})();
</script>
</body>
</html>
+187
View File
@@ -0,0 +1,187 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Gradient Fill</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=Nunito: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: #0a0a0a;
}
#gradient-fill {
position: relative; width: 1920px; height: 1080px;
overflow: hidden; background: #0a0a0a;
}
#gr-video {
position: absolute; inset: 0; z-index: 0;
width: 100%; height: 100%; object-fit: cover;
}
.gr-overlay {
position: absolute; inset: 0;
background: rgba(0,0,0,0.42); z-index: 1; pointer-events: none;
}
#gr-container {
position: absolute; inset: 0; z-index: 10; pointer-events: none;
}
.gr-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 80px;
opacity: 0; visibility: hidden;
}
.gr-word {
font-family: 'Nunito', sans-serif;
font-weight: 900; font-size: 100px;
display: inline-block; letter-spacing: 0.04em; line-height: 1;
-webkit-background-clip: text;
background-clip: text;
color: transparent;
opacity: 0.3;
}
</style>
</head>
<body>
<div
id="gradient-fill"
data-composition-id="caption-gradient-fill"
data-start="0"
data-duration="8"
data-fps="30"
data-width="1920"
data-height="1080"
>
<video id="gr-video" muted playsinline
data-start="0" data-duration="8" data-track-index="0" data-has-audio="false"></video>
<div class="gr-overlay"></div>
<div id="gr-container"></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
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}
];
var GROUPS = [
{wordStart:0, wordEnd:3, start:0.0, end:1.05},
{wordStart:4, wordEnd:7, start:1.1, end:2.05},
{wordStart:8, wordEnd:8, start:2.1, end:2.6},
{wordStart:9, wordEnd:12, start:2.65, end:3.5},
{wordStart:13, wordEnd:15, start:3.55, end:4.4},
{wordStart:16, wordEnd:16, start:3.95, end:4.75},
{wordStart:17, wordEnd:19, start:4.9, end:5.4},
{wordStart:20, wordEnd:22, start:5.65, end:6.2},
{wordStart:23, wordEnd:24, start:6.35, end:6.75},
{wordStart:25, wordEnd:27, start:6.8, end:7.8},
];
// Enhanced gradient palette with accent for keywords
var GRADS = [
"linear-gradient(135deg,#FF6B6B,#FFD700)",
"linear-gradient(135deg,#4ADE80,#22D3EE)",
"linear-gradient(135deg,#818CF8,#E879F9)",
"linear-gradient(135deg,#FFD700,#FF6B35)",
"linear-gradient(135deg,#22D3EE,#4ADE80)",
"linear-gradient(135deg,#E879F9,#818CF8)",
"linear-gradient(135deg,#FF3366,#FF6B35)"
];
var KEYWORD_GRADS = {
8: "linear-gradient(135deg,#FF6B6B,#FFD700,#FF6B6B)", // HyperFrames
12: "linear-gradient(135deg,#22D3EE,#ffffff,#22D3EE)", // HTML
15: "linear-gradient(135deg,#818CF8,#E879F9,#818CF8)", // professional
24: "linear-gradient(135deg,#00F5D4,#22D3EE,#00F5D4)", // code
27: "linear-gradient(135deg,#FFD700,#FF6B35,#FFD700)" // cinema.
};
var container = document.getElementById('gr-container');
var tl = gsap.timeline({ paused: true });
GROUPS.forEach(function(g, gi) {
var groupWords = WORDS.slice(g.wordStart, g.wordEnd + 1);
var grp = document.createElement('div');
grp.className = 'gr-group';
grp.id = 'gr-grp-' + gi;
groupWords.forEach(function(w, i) {
var wi = g.wordStart + i;
var span = document.createElement('span');
span.className = 'gr-word';
span.id = 'gr-w-' + wi;
span.textContent = w.text;
span.style.fontSize = '100px';
var grad = KEYWORD_GRADS[wi] || GRADS[wi % 7];
span.style.background = grad;
span.style.webkitBackgroundClip = 'text';
span.style.backgroundClip = 'text';
span.style.color = 'transparent';
grp.appendChild(span);
});
container.appendChild(grp);
var gs = g.start;
groupWords.forEach(function(w, i) {
var wi = g.wordStart + i;
var el = document.getElementById('gr-w-' + wi);
tl.set(el, { opacity: 0, y: 55, scale: 0.35 }, gs);
tl.fromTo(el,
{ y: 55, scale: 0.35, opacity: 0 },
{ y: 0, scale: 1, opacity: 0.35, duration: 0.55, ease: 'elastic.out(1.6,0.4)' },
gs + i * 0.05
);
});
tl.set(grp, { visibility: 'visible', opacity: 1 }, gs);
groupWords.forEach(function(w, i) {
var wi = g.wordStart + i;
var el = document.getElementById('gr-w-' + wi);
tl.to(el, { opacity: 1, scale: 1.1, duration: 0.08 }, w.start);
tl.to(el, { opacity: 0.3, scale: 1, duration: 0.08 }, w.end);
});
tl.to(grp, { y: -26, opacity: 0, duration: 0.18 }, g.end - 0.18);
tl.set(grp, { opacity: 0, visibility: 'hidden' }, g.end);
});
window.__timelines['gradient-fill'] = tl;
})();
</script>
</body>
</html>
@@ -0,0 +1,15 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "caption-gradient-fill",
"type": "hyperframes:component",
"title": "Gradient Fill",
"description": "Gradient-clipped text with elastic bounce entrance",
"tags": ["captions", "caption-style", "gradient", "colorful"],
"files": [
{
"path": "caption-gradient-fill.html",
"target": "compositions/components/caption-gradient-fill.html",
"type": "hyperframes:snippet"
}
]
}
@@ -0,0 +1,157 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Kinetic Slam</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=Anton&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: #0a0a0a;
}
#kinetic-slam {
position: relative; width: 1920px; height: 1080px;
overflow: hidden; background: #0a0a0a;
}
#kt-video {
position: absolute; inset: 0; z-index: 0;
width: 100%; height: 100%; object-fit: cover;
}
.kt-overlay {
position: absolute; inset: 0;
background: rgba(0,0,0,0.45); z-index: 1; pointer-events: none;
}
#kt-container {
position: absolute; inset: 0; z-index: 10; pointer-events: none;
}
.kt-word {
font-family: 'Anton', sans-serif;
font-weight: 400;
text-transform: uppercase;
color: #ffffff;
position: absolute; left: 0; width: 100%;
text-align: center; top: 50%;
letter-spacing: 0.04em; line-height: 1;
opacity: 0; visibility: hidden;
}
</style>
</head>
<body>
<div
id="kinetic-slam"
data-composition-id="caption-kinetic-slam"
data-start="0"
data-duration="8"
data-fps="30"
data-width="1920"
data-height="1080"
>
<video id="kt-video" muted playsinline
data-start="0" data-duration="8" data-track-index="0" data-has-audio="false"></video>
<div class="kt-overlay"></div>
<div id="kt-container"></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
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}
];
var KEYWORDS = new Set([8, 12, 15, 24, 27]); // HyperFrames, HTML, professional, code, cinema.
var container = document.getElementById('kt-container');
var tl = gsap.timeline({ paused: true });
WORDS.forEach(function(w, wi) {
var el = document.createElement('div');
el.className = 'kt-word';
el.id = 'kt-w-' + wi;
el.textContent = w.text.toUpperCase();
el.style.fontSize = '220px';
if (KEYWORDS.has(wi)) el.style.color = '#FFD700';
container.appendChild(el);
gsap.set(el, { yPercent: -50 });
var entranceMode = wi % 4;
tl.set(el, { visibility: 'visible' }, w.start);
if (entranceMode === 0) {
tl.fromTo(el,
{ y: -120, opacity: 0 },
{ y: 0, opacity: 1, duration: 0.22, ease: 'back.out(1.7)' },
w.start
);
} else if (entranceMode === 1) {
tl.fromTo(el,
{ x: -300, opacity: 0 },
{ x: 0, opacity: 1, duration: 0.2, ease: 'expo.out' },
w.start
);
} else if (entranceMode === 2) {
tl.fromTo(el,
{ x: 300, opacity: 0 },
{ x: 0, opacity: 1, duration: 0.2, ease: 'expo.out' },
w.start
);
} else {
tl.fromTo(el,
{ scale: 0.4, opacity: 0 },
{ scale: 1, opacity: 1, duration: 0.24, ease: 'back.out(2.2)' },
w.start
);
}
tl.to(el, { opacity: 0, duration: 0.1, ease: 'power2.in' }, w.end);
tl.set(el, { opacity: 0, visibility: 'hidden' }, w.end + 0.1);
});
tl.seek(0);
window.__timelines['kinetic-slam'] = tl;
})();
</script>
</body>
</html>
+157
View File
@@ -0,0 +1,157 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Kinetic Slam</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=Anton&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: #0a0a0a;
}
#kinetic-slam {
position: relative; width: 1920px; height: 1080px;
overflow: hidden; background: #0a0a0a;
}
#kt-video {
position: absolute; inset: 0; z-index: 0;
width: 100%; height: 100%; object-fit: cover;
}
.kt-overlay {
position: absolute; inset: 0;
background: rgba(0,0,0,0.45); z-index: 1; pointer-events: none;
}
#kt-container {
position: absolute; inset: 0; z-index: 10; pointer-events: none;
}
.kt-word {
font-family: 'Anton', sans-serif;
font-weight: 400;
text-transform: uppercase;
color: #ffffff;
position: absolute; left: 0; width: 100%;
text-align: center; top: 50%;
letter-spacing: 0.04em; line-height: 1;
opacity: 0; visibility: hidden;
}
</style>
</head>
<body>
<div
id="kinetic-slam"
data-composition-id="caption-kinetic-slam"
data-start="0"
data-duration="8"
data-fps="30"
data-width="1920"
data-height="1080"
>
<video id="kt-video" muted playsinline
data-start="0" data-duration="8" data-track-index="0" data-has-audio="false"></video>
<div class="kt-overlay"></div>
<div id="kt-container"></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
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}
];
var KEYWORDS = new Set([8, 12, 15, 24, 27]); // HyperFrames, HTML, professional, code, cinema.
var container = document.getElementById('kt-container');
var tl = gsap.timeline({ paused: true });
WORDS.forEach(function(w, wi) {
var el = document.createElement('div');
el.className = 'kt-word';
el.id = 'kt-w-' + wi;
el.textContent = w.text.toUpperCase();
el.style.fontSize = '220px';
if (KEYWORDS.has(wi)) el.style.color = '#FFD700';
container.appendChild(el);
gsap.set(el, { yPercent: -50 });
var entranceMode = wi % 4;
tl.set(el, { visibility: 'visible' }, w.start);
if (entranceMode === 0) {
tl.fromTo(el,
{ y: -120, opacity: 0 },
{ y: 0, opacity: 1, duration: 0.22, ease: 'back.out(1.7)' },
w.start
);
} else if (entranceMode === 1) {
tl.fromTo(el,
{ x: -300, opacity: 0 },
{ x: 0, opacity: 1, duration: 0.2, ease: 'expo.out' },
w.start
);
} else if (entranceMode === 2) {
tl.fromTo(el,
{ x: 300, opacity: 0 },
{ x: 0, opacity: 1, duration: 0.2, ease: 'expo.out' },
w.start
);
} else {
tl.fromTo(el,
{ scale: 0.4, opacity: 0 },
{ scale: 1, opacity: 1, duration: 0.24, ease: 'back.out(2.2)' },
w.start
);
}
tl.to(el, { opacity: 0, duration: 0.1, ease: 'power2.in' }, w.end);
tl.set(el, { opacity: 0, visibility: 'hidden' }, w.end + 0.1);
});
tl.seek(0);
window.__timelines['kinetic-slam'] = tl;
})();
</script>
</body>
</html>
@@ -0,0 +1,15 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "caption-kinetic-slam",
"type": "hyperframes:component",
"title": "Kinetic Slam",
"description": "Full-screen single-word display with alternating entrance directions",
"tags": ["captions", "caption-style", "kinetic", "typography", "slam"],
"files": [
{
"path": "caption-kinetic-slam.html",
"target": "compositions/components/caption-kinetic-slam.html",
"type": "hyperframes:snippet"
}
]
}
@@ -0,0 +1,197 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Matrix Decode</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=Space+Grotesk:wght@700&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: #000;
}
#matrix-decode {
position: relative; width: 1920px; height: 1080px;
overflow: hidden; background: #000;
}
#sc-video {
position: absolute; inset: 0; z-index: 0;
width: 100%; height: 100%; object-fit: cover;
}
.sc-overlay {
position: absolute; inset: 0;
background: rgba(0,0,0,0.85); z-index: 1; pointer-events: none;
}
#sc-container {
position: absolute; inset: 0; z-index: 10; pointer-events: none;
}
.sc-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 80px;
opacity: 0; visibility: hidden;
}
.sc-word-wrap {
font-family: 'Space Grotesk', sans-serif;
font-weight: 700; font-size: 96px;
color: #00FF41;
display: inline-block; letter-spacing: 0.04em; line-height: 1; position: relative;
}
.sc-real { display: none; }
.sc-scr0 { display: none; }
.sc-scr1 { display: none; }
</style>
</head>
<body>
<div
id="matrix-decode"
data-composition-id="caption-matrix-decode"
data-start="0"
data-duration="8"
data-fps="30"
data-width="1920"
data-height="1080"
>
<video id="sc-video" muted playsinline
data-start="0" data-duration="8" data-track-index="0" data-has-audio="false"></video>
<div class="sc-overlay"></div>
<div id="sc-container"></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
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 ALPHANUM = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
function scrambleChars(seed) {
var rng = mulberry32(seed);
var result = '';
for (var i = 0; i < 4; i++) {
result += ALPHANUM[Math.floor(rng() * ALPHANUM.length)];
}
return result;
}
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}
];
var GROUPS = [
{wordStart:0, wordEnd:3, start:0.0, end:1.05},
{wordStart:4, wordEnd:7, start:1.1, end:2.05},
{wordStart:8, wordEnd:8, start:2.1, end:2.6},
{wordStart:9, wordEnd:12, start:2.65, end:3.5},
{wordStart:13, wordEnd:15, start:3.55, end:4.4},
{wordStart:16, wordEnd:16, start:3.95, end:4.75},
{wordStart:17, wordEnd:19, start:4.9, end:5.4},
{wordStart:20, wordEnd:22, start:5.65, end:6.2},
{wordStart:23, wordEnd:24, start:6.35, end:6.75},
{wordStart:25, wordEnd:27, start:6.8, end:7.8},
];
var container = document.getElementById('sc-container');
var tl = gsap.timeline({ paused: true });
GROUPS.forEach(function(g, gi) {
var groupWords = WORDS.slice(g.wordStart, g.wordEnd + 1);
var grp = document.createElement('div');
grp.className = 'sc-group';
grp.id = 'sc-grp-' + gi;
groupWords.forEach(function(w, i) {
var wi = g.wordStart + i;
var wordWrap = document.createElement('span');
wordWrap.className = 'sc-word-wrap';
wordWrap.id = 'sc-ww-' + wi;
wordWrap.style.fontSize = '96px';
var real = document.createElement('span');
real.className = 'sc-real';
real.id = 'sc-real-' + wi;
real.textContent = w.text;
wordWrap.appendChild(real);
var scr0 = document.createElement('span');
scr0.className = 'sc-scr0';
scr0.id = 'sc-scr0-' + wi;
scr0.textContent = scrambleChars(wi * 1000 + 0);
wordWrap.appendChild(scr0);
var scr1 = document.createElement('span');
scr1.className = 'sc-scr1';
scr1.id = 'sc-scr1-' + wi;
scr1.textContent = scrambleChars(wi * 1000 + 1);
wordWrap.appendChild(scr1);
grp.appendChild(wordWrap);
});
container.appendChild(grp);
tl.set(grp, { visibility: 'visible', opacity: 0 }, g.start);
tl.fromTo(grp, { opacity: 0 }, { opacity: 1, duration: 0.1 }, g.start);
groupWords.forEach(function(w, i) {
var wi = g.wordStart + i;
var realEl = document.getElementById('sc-real-' + wi);
var scr0El = document.getElementById('sc-scr0-' + wi);
var scr1El = document.getElementById('sc-scr1-' + wi);
tl.set(scr0El, { display: 'inline' }, w.start);
tl.set(scr1El, { display: 'inline' }, w.start + 0.05);
tl.set(scr0El, { display: 'none' }, w.start + 0.05);
tl.set(realEl, { display: 'inline' }, w.start + 0.1);
tl.set(scr1El, { display: 'none' }, w.start + 0.1);
});
tl.to(grp, { opacity: 0, duration: 0.12 }, g.end - 0.12);
tl.set(grp, { opacity: 0, visibility: 'hidden' }, g.end);
});
window.__timelines['matrix-decode'] = tl;
})();
</script>
</body>
</html>
+197
View File
@@ -0,0 +1,197 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Matrix Decode</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=Space+Grotesk:wght@700&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: #000;
}
#matrix-decode {
position: relative; width: 1920px; height: 1080px;
overflow: hidden; background: #000;
}
#sc-video {
position: absolute; inset: 0; z-index: 0;
width: 100%; height: 100%; object-fit: cover;
}
.sc-overlay {
position: absolute; inset: 0;
background: rgba(0,0,0,0.85); z-index: 1; pointer-events: none;
}
#sc-container {
position: absolute; inset: 0; z-index: 10; pointer-events: none;
}
.sc-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 80px;
opacity: 0; visibility: hidden;
}
.sc-word-wrap {
font-family: 'Space Grotesk', sans-serif;
font-weight: 700; font-size: 96px;
color: #00FF41;
display: inline-block; letter-spacing: 0.04em; line-height: 1; position: relative;
}
.sc-real { display: none; }
.sc-scr0 { display: none; }
.sc-scr1 { display: none; }
</style>
</head>
<body>
<div
id="matrix-decode"
data-composition-id="caption-matrix-decode"
data-start="0"
data-duration="8"
data-fps="30"
data-width="1920"
data-height="1080"
>
<video id="sc-video" muted playsinline
data-start="0" data-duration="8" data-track-index="0" data-has-audio="false"></video>
<div class="sc-overlay"></div>
<div id="sc-container"></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
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 ALPHANUM = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
function scrambleChars(seed) {
var rng = mulberry32(seed);
var result = '';
for (var i = 0; i < 4; i++) {
result += ALPHANUM[Math.floor(rng() * ALPHANUM.length)];
}
return result;
}
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}
];
var GROUPS = [
{wordStart:0, wordEnd:3, start:0.0, end:1.05},
{wordStart:4, wordEnd:7, start:1.1, end:2.05},
{wordStart:8, wordEnd:8, start:2.1, end:2.6},
{wordStart:9, wordEnd:12, start:2.65, end:3.5},
{wordStart:13, wordEnd:15, start:3.55, end:4.4},
{wordStart:16, wordEnd:16, start:3.95, end:4.75},
{wordStart:17, wordEnd:19, start:4.9, end:5.4},
{wordStart:20, wordEnd:22, start:5.65, end:6.2},
{wordStart:23, wordEnd:24, start:6.35, end:6.75},
{wordStart:25, wordEnd:27, start:6.8, end:7.8},
];
var container = document.getElementById('sc-container');
var tl = gsap.timeline({ paused: true });
GROUPS.forEach(function(g, gi) {
var groupWords = WORDS.slice(g.wordStart, g.wordEnd + 1);
var grp = document.createElement('div');
grp.className = 'sc-group';
grp.id = 'sc-grp-' + gi;
groupWords.forEach(function(w, i) {
var wi = g.wordStart + i;
var wordWrap = document.createElement('span');
wordWrap.className = 'sc-word-wrap';
wordWrap.id = 'sc-ww-' + wi;
wordWrap.style.fontSize = '96px';
var real = document.createElement('span');
real.className = 'sc-real';
real.id = 'sc-real-' + wi;
real.textContent = w.text;
wordWrap.appendChild(real);
var scr0 = document.createElement('span');
scr0.className = 'sc-scr0';
scr0.id = 'sc-scr0-' + wi;
scr0.textContent = scrambleChars(wi * 1000 + 0);
wordWrap.appendChild(scr0);
var scr1 = document.createElement('span');
scr1.className = 'sc-scr1';
scr1.id = 'sc-scr1-' + wi;
scr1.textContent = scrambleChars(wi * 1000 + 1);
wordWrap.appendChild(scr1);
grp.appendChild(wordWrap);
});
container.appendChild(grp);
tl.set(grp, { visibility: 'visible', opacity: 0 }, g.start);
tl.fromTo(grp, { opacity: 0 }, { opacity: 1, duration: 0.1 }, g.start);
groupWords.forEach(function(w, i) {
var wi = g.wordStart + i;
var realEl = document.getElementById('sc-real-' + wi);
var scr0El = document.getElementById('sc-scr0-' + wi);
var scr1El = document.getElementById('sc-scr1-' + wi);
tl.set(scr0El, { display: 'inline' }, w.start);
tl.set(scr1El, { display: 'inline' }, w.start + 0.05);
tl.set(scr0El, { display: 'none' }, w.start + 0.05);
tl.set(realEl, { display: 'inline' }, w.start + 0.1);
tl.set(scr1El, { display: 'none' }, w.start + 0.1);
});
tl.to(grp, { opacity: 0, duration: 0.12 }, g.end - 0.12);
tl.set(grp, { opacity: 0, visibility: 'hidden' }, g.end);
});
window.__timelines['matrix-decode'] = tl;
})();
</script>
</body>
</html>
@@ -0,0 +1,15 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "caption-matrix-decode",
"type": "hyperframes:component",
"title": "Matrix Decode",
"description": "Character scramble animation before text reveal",
"tags": ["captions", "caption-style", "matrix", "scramble", "decode"],
"files": [
{
"path": "caption-matrix-decode.html",
"target": "compositions/components/caption-matrix-decode.html",
"type": "hyperframes:snippet"
}
]
}
@@ -0,0 +1,384 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Neon Accent</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=Montserrat:wght@800&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: #000000;
font-family: "Montserrat", Arial, sans-serif;
}
#neon-accent {
position: relative;
width: 1920px;
height: 1080px;
overflow: hidden;
background: #000000;
}
#avatar-video {
position: absolute;
inset: 0;
z-index: 1;
width: 100%;
height: 100%;
object-fit: cover;
}
.caption-layer {
position: absolute;
inset: 0;
z-index: 20;
pointer-events: none;
transform: translateZ(0);
}
.safe-zone {
position: absolute;
bottom: 100px;
left: 50%;
transform: translateX(-50%);
width: 1400px;
height: 260px;
display: flex;
align-items: center;
justify-content: center;
}
.caption-group {
position: absolute;
left: 0;
top: 0;
width: 1400px;
height: 260px;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transform-origin: 50% 50%;
will-change: transform;
backface-visibility: hidden;
}
.caption-copy {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
max-width: 1400px;
text-align: center;
}
.caption-line {
display: flex;
align-items: center;
justify-content: center;
gap: 16px;
width: max-content;
max-width: 1400px;
line-height: 1.1;
white-space: nowrap;
}
.caption-word {
display: inline-block;
color: #ffffff;
font-family: "Montserrat", Arial, sans-serif;
font-weight: 800;
font-size: 88px;
line-height: 1.1;
letter-spacing: 0;
text-transform: uppercase;
}
.caption-word.accent {
font-size: 106px;
}
</style>
</head>
<body>
<div
id="neon-accent"
data-composition-id="caption-neon-accent"
data-start="0"
data-duration="8"
data-fps="30"
data-width="1920"
data-height="1080"
>
<video
id="avatar-video"
muted
playsinline
data-start="0"
data-duration="8"
data-track-index="0"
data-has-audio="false"
></video>
<div class="caption-layer" aria-hidden="true">
<div id="caption-stage" class="safe-zone"></div>
</div>
</div>
<script>
var DURATION = 8;
var SAFE_ZONE_WIDTH = 1400;
var BASE_FONT_SIZE = 88;
var ACCENT_FONT_SIZE = 106;
var MIN_FONT_SIZE = 62;
var MAX_WORDS_PER_GROUP = 4;
var WORD_SPACING = 16;
var PAUSE_THRESHOLD = 0.1;
var ENTRY_DURATION = 7 / 30;
var WIGGLE_DISTANCE = 14;
var WIGGLE_CYCLE = 2 / 1.3;
var FONT_WIDTH_SAFETY = 1.14;
var PRIMARY_COLOR = "#FFFFFF";
var ACCENT_COLORS = ["#53FF01", "#FF0002", "#FCFF00"];
var TRANSITION_WORDS = new Set([
"the", "a", "an", "and", "or", "but", "is", "are", "was", "were",
"to", "of", "in", "on", "at", "for", "with", "by", "from", "as",
"it", "its", "this", "that", "these", "those", "be", "been",
"have", "has", "had", "do", "does", "did", "will", "would", "could",
"should", "may", "might", "must", "can", "if", "then", "so", "just",
"not", "no", "yes", "up", "out", "about", "lets", "you", "write", "render"
]);
var KEYWORDS = new Set(["hyperframes", "html", "professional", "code", "cinema"]);
var TRANSCRIPT = [
{"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}
];
gsap.registerPlugin && gsap.registerPlugin();
function cleanText(text) {
return String(text || "").trim();
}
function normalizeWords(rawWords) {
return rawWords.map(function (item) {
return {
text: cleanText(item.word || item.text),
start: Math.max(0, Number(item.start) || 0),
end: Math.min(DURATION, Math.max(Number(item.start) || 0, Number(item.end) || 0)),
};
}).filter(function (word) { return word.text.length > 0; });
}
var measureCanvas = document.createElement("canvas");
var measureContext = measureCanvas.getContext("2d");
function seededRandom(seed) {
var x = Math.sin(seed * 9999) * 10000;
return x - Math.floor(x);
}
function isKeyword(text) {
return KEYWORDS.has(text.toLowerCase().replace(/[^a-z]/g, ""));
}
function assignWordColors(words, groupIndex) {
return words.map(function (word) {
if (isKeyword(word.text)) {
var colorIndex = Math.floor(seededRandom(groupIndex * 23 + 7) * ACCENT_COLORS.length);
return ACCENT_COLORS[colorIndex];
}
var clean = word.text.toLowerCase().replace(/[^a-z']/g, "");
if (TRANSITION_WORDS.has(clean)) return PRIMARY_COLOR;
var rand = seededRandom(groupIndex * 7 + words.indexOf(word));
if (rand > 0.6) {
var ci = Math.floor(rand * ACCENT_COLORS.length) % ACCENT_COLORS.length;
return ACCENT_COLORS[ci];
}
return PRIMARY_COLOR;
});
}
function measureWord(word, color, fontSize) {
var size = color === PRIMARY_COLOR ? fontSize : fontSize * 1.2;
measureContext.font = "800 " + size + "px Montserrat";
return measureContext.measureText(word.text.toUpperCase()).width * FONT_WIDTH_SAFETY;
}
function measureLineWidth(words, colors, fontSize) {
return words.reduce(function (total, word, index) {
return total + measureWord(word, colors[index] || PRIMARY_COLOR, fontSize) + (index ? WORD_SPACING : 0);
}, 0);
}
function splitLinesForGroup(words, colors, fontSize) {
var maxLineWidth = SAFE_ZONE_WIDTH;
if (words.some(function (word, i) { return measureWord(word, colors[i] || PRIMARY_COLOR, fontSize) > maxLineWidth; })) return null;
if (measureLineWidth(words, colors, fontSize) <= maxLineWidth) return [{ words: words, colors: colors }];
var best = null;
var bestDiff = Infinity;
for (var split = 1; split < words.length; split++) {
var fw = words.slice(0, split), sw = words.slice(split);
var fc = colors.slice(0, split), sc = colors.slice(split);
var fW = measureLineWidth(fw, fc, fontSize);
var sW = measureLineWidth(sw, sc, fontSize);
if (fW <= maxLineWidth && sW <= maxLineWidth) {
var diff = Math.abs(fW - sW);
if (diff < bestDiff) { best = [{ words: fw, colors: fc }, { words: sw, colors: sc }]; bestDiff = diff; }
}
}
return best;
}
function makeGroups(words) {
var groups = [];
var current = [];
words.forEach(function (word, index) {
var nextWord = words[index + 1];
var testGroup = current.concat([word]);
var exceedsWords = testGroup.length > MAX_WORDS_PER_GROUP;
var colors = assignWordColors(testGroup, groups.length);
var exceedsFit = !splitLinesForGroup(testGroup, colors, BASE_FONT_SIZE);
if (exceedsWords || exceedsFit) {
if (current.length) groups.push(makeGroup(current, groups.length));
current = [word];
return;
}
current.push(word);
var punctuation = /[,.:!?]$/.test(word.text);
var pause = nextWord ? nextWord.start - word.end : 0;
if (!nextWord || punctuation || pause >= PAUSE_THRESHOLD || current.length >= MAX_WORDS_PER_GROUP) {
groups.push(makeGroup(current, groups.length));
current = [];
}
});
if (current.length) groups.push(makeGroup(current, groups.length));
return groups;
}
function makeGroup(words, groupIndex) {
var colors = assignWordColors(words, groupIndex);
var fontSize = BASE_FONT_SIZE;
while (fontSize > MIN_FONT_SIZE && !splitLinesForGroup(words, colors, fontSize)) { fontSize -= 2; }
return {
words: words.slice(), colors: colors, fontSize: fontSize,
lines: splitLinesForGroup(words, colors, fontSize) || [{ words: words, colors: colors }],
start: words[0].start, end: words[words.length - 1].end,
};
}
function shadowForColor(color) {
var hex = color.replace("#", "");
var r = parseInt(hex.slice(0, 2), 16);
var g = parseInt(hex.slice(2, 4), 16);
var b = parseInt(hex.slice(4, 6), 16);
var glow = "0 0 4px rgba(" + r + "," + g + "," + b + ",1), 0 0 10px rgba(" + r + "," + g + "," + b + ",0.9), 0 0 20px rgba(" + r + "," + g + "," + b + ",0.7), 0 0 40px rgba(" + r + "," + g + "," + b + ",0.4)";
return "0 8px 16px rgba(0,0,0,0.8), 0 16px 40px rgba(0,0,0,0.6), " + glow;
}
function buildCaptions(groups) {
var stage = document.getElementById("caption-stage");
groups.forEach(function (group, groupIndex) {
var groupEl = document.createElement("div");
var copyEl = document.createElement("div");
groupEl.id = "caption-group-" + groupIndex;
groupEl.className = "caption-group";
copyEl.className = "caption-copy";
group.lines.slice(0, 2).forEach(function (line) {
var lineEl = document.createElement("div");
lineEl.className = "caption-line";
line.words.forEach(function (word, wordIndex) {
var color = line.colors[wordIndex] || PRIMARY_COLOR;
var wordEl = document.createElement("span");
wordEl.className = color === PRIMARY_COLOR ? "caption-word" : "caption-word accent";
wordEl.textContent = word.text.toUpperCase();
wordEl.style.color = color;
wordEl.style.fontSize = color === PRIMARY_COLOR ? group.fontSize + "px" : group.fontSize * 1.2 + "px";
wordEl.style.textShadow = shadowForColor(color);
lineEl.appendChild(wordEl);
});
copyEl.appendChild(lineEl);
});
groupEl.appendChild(copyEl);
stage.appendChild(groupEl);
});
}
var WORDS = normalizeWords(TRANSCRIPT);
var GROUPS = makeGroups(WORDS);
buildCaptions(GROUPS);
window.__timelines = window.__timelines || {};
var tl = gsap.timeline({ paused: true });
var visibleStarts = GROUPS.map(function (group, groupIndex) {
var prev = GROUPS[groupIndex - 1];
var availableGap = prev ? Math.max(0, group.start - prev.end) : ENTRY_DURATION;
var entryLead = Math.min(ENTRY_DURATION, availableGap);
return Math.max(0, group.start - entryLead);
});
var allGroupEls = GROUPS.map(function (_, i) { return document.getElementById("caption-group-" + i); });
GROUPS.forEach(function (group, groupIndex) {
var groupEl = allGroupEls[groupIndex];
var visibleStart = visibleStarts[groupIndex];
var visibleEnd = groupIndex < GROUPS.length - 1 ? visibleStarts[groupIndex + 1] : DURATION;
var direction = groupIndex % 2 === 0 ? -1 : 1;
var D = WIGGLE_DISTANCE * direction;
var wiggleCycles = Math.max(1, Math.ceil((visibleEnd - visibleStart) / WIGGLE_CYCLE));
allGroupEls.forEach(function (otherEl, otherIndex) {
if (otherIndex !== groupIndex) tl.set(otherEl, { opacity: 0 }, visibleStart);
});
tl.set(groupEl, { opacity: 0, scale: 0.65, x: 0, y: 0 }, visibleStart);
tl.to(groupEl, { opacity: 1, scale: 1, duration: ENTRY_DURATION, ease: "power3.out", force3D: true }, visibleStart);
tl.to(groupEl, { x: D, y: D, duration: WIGGLE_CYCLE / 2, ease: "sine.inOut", yoyo: true, repeat: Math.max(0, wiggleCycles * 2 - 1), force3D: true }, visibleStart);
tl.set(groupEl, { opacity: 0 }, visibleEnd);
});
window.__timelines["neon-accent"] = tl;
</script>
</body>
</html>
+384
View File
@@ -0,0 +1,384 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Neon Accent</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=Montserrat:wght@800&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: #000000;
font-family: "Montserrat", Arial, sans-serif;
}
#neon-accent {
position: relative;
width: 1920px;
height: 1080px;
overflow: hidden;
background: #000000;
}
#avatar-video {
position: absolute;
inset: 0;
z-index: 1;
width: 100%;
height: 100%;
object-fit: cover;
}
.caption-layer {
position: absolute;
inset: 0;
z-index: 20;
pointer-events: none;
transform: translateZ(0);
}
.safe-zone {
position: absolute;
bottom: 100px;
left: 50%;
transform: translateX(-50%);
width: 1400px;
height: 260px;
display: flex;
align-items: center;
justify-content: center;
}
.caption-group {
position: absolute;
left: 0;
top: 0;
width: 1400px;
height: 260px;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transform-origin: 50% 50%;
will-change: transform;
backface-visibility: hidden;
}
.caption-copy {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
max-width: 1400px;
text-align: center;
}
.caption-line {
display: flex;
align-items: center;
justify-content: center;
gap: 16px;
width: max-content;
max-width: 1400px;
line-height: 1.1;
white-space: nowrap;
}
.caption-word {
display: inline-block;
color: #ffffff;
font-family: "Montserrat", Arial, sans-serif;
font-weight: 800;
font-size: 88px;
line-height: 1.1;
letter-spacing: 0;
text-transform: uppercase;
}
.caption-word.accent {
font-size: 106px;
}
</style>
</head>
<body>
<div
id="neon-accent"
data-composition-id="caption-neon-accent"
data-start="0"
data-duration="8"
data-fps="30"
data-width="1920"
data-height="1080"
>
<video
id="avatar-video"
muted
playsinline
data-start="0"
data-duration="8"
data-track-index="0"
data-has-audio="false"
></video>
<div class="caption-layer" aria-hidden="true">
<div id="caption-stage" class="safe-zone"></div>
</div>
</div>
<script>
var DURATION = 8;
var SAFE_ZONE_WIDTH = 1400;
var BASE_FONT_SIZE = 88;
var ACCENT_FONT_SIZE = 106;
var MIN_FONT_SIZE = 62;
var MAX_WORDS_PER_GROUP = 4;
var WORD_SPACING = 16;
var PAUSE_THRESHOLD = 0.1;
var ENTRY_DURATION = 7 / 30;
var WIGGLE_DISTANCE = 14;
var WIGGLE_CYCLE = 2 / 1.3;
var FONT_WIDTH_SAFETY = 1.14;
var PRIMARY_COLOR = "#FFFFFF";
var ACCENT_COLORS = ["#53FF01", "#FF0002", "#FCFF00"];
var TRANSITION_WORDS = new Set([
"the", "a", "an", "and", "or", "but", "is", "are", "was", "were",
"to", "of", "in", "on", "at", "for", "with", "by", "from", "as",
"it", "its", "this", "that", "these", "those", "be", "been",
"have", "has", "had", "do", "does", "did", "will", "would", "could",
"should", "may", "might", "must", "can", "if", "then", "so", "just",
"not", "no", "yes", "up", "out", "about", "lets", "you", "write", "render"
]);
var KEYWORDS = new Set(["hyperframes", "html", "professional", "code", "cinema"]);
var TRANSCRIPT = [
{"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}
];
gsap.registerPlugin && gsap.registerPlugin();
function cleanText(text) {
return String(text || "").trim();
}
function normalizeWords(rawWords) {
return rawWords.map(function (item) {
return {
text: cleanText(item.word || item.text),
start: Math.max(0, Number(item.start) || 0),
end: Math.min(DURATION, Math.max(Number(item.start) || 0, Number(item.end) || 0)),
};
}).filter(function (word) { return word.text.length > 0; });
}
var measureCanvas = document.createElement("canvas");
var measureContext = measureCanvas.getContext("2d");
function seededRandom(seed) {
var x = Math.sin(seed * 9999) * 10000;
return x - Math.floor(x);
}
function isKeyword(text) {
return KEYWORDS.has(text.toLowerCase().replace(/[^a-z]/g, ""));
}
function assignWordColors(words, groupIndex) {
return words.map(function (word) {
if (isKeyword(word.text)) {
var colorIndex = Math.floor(seededRandom(groupIndex * 23 + 7) * ACCENT_COLORS.length);
return ACCENT_COLORS[colorIndex];
}
var clean = word.text.toLowerCase().replace(/[^a-z']/g, "");
if (TRANSITION_WORDS.has(clean)) return PRIMARY_COLOR;
var rand = seededRandom(groupIndex * 7 + words.indexOf(word));
if (rand > 0.6) {
var ci = Math.floor(rand * ACCENT_COLORS.length) % ACCENT_COLORS.length;
return ACCENT_COLORS[ci];
}
return PRIMARY_COLOR;
});
}
function measureWord(word, color, fontSize) {
var size = color === PRIMARY_COLOR ? fontSize : fontSize * 1.2;
measureContext.font = "800 " + size + "px Montserrat";
return measureContext.measureText(word.text.toUpperCase()).width * FONT_WIDTH_SAFETY;
}
function measureLineWidth(words, colors, fontSize) {
return words.reduce(function (total, word, index) {
return total + measureWord(word, colors[index] || PRIMARY_COLOR, fontSize) + (index ? WORD_SPACING : 0);
}, 0);
}
function splitLinesForGroup(words, colors, fontSize) {
var maxLineWidth = SAFE_ZONE_WIDTH;
if (words.some(function (word, i) { return measureWord(word, colors[i] || PRIMARY_COLOR, fontSize) > maxLineWidth; })) return null;
if (measureLineWidth(words, colors, fontSize) <= maxLineWidth) return [{ words: words, colors: colors }];
var best = null;
var bestDiff = Infinity;
for (var split = 1; split < words.length; split++) {
var fw = words.slice(0, split), sw = words.slice(split);
var fc = colors.slice(0, split), sc = colors.slice(split);
var fW = measureLineWidth(fw, fc, fontSize);
var sW = measureLineWidth(sw, sc, fontSize);
if (fW <= maxLineWidth && sW <= maxLineWidth) {
var diff = Math.abs(fW - sW);
if (diff < bestDiff) { best = [{ words: fw, colors: fc }, { words: sw, colors: sc }]; bestDiff = diff; }
}
}
return best;
}
function makeGroups(words) {
var groups = [];
var current = [];
words.forEach(function (word, index) {
var nextWord = words[index + 1];
var testGroup = current.concat([word]);
var exceedsWords = testGroup.length > MAX_WORDS_PER_GROUP;
var colors = assignWordColors(testGroup, groups.length);
var exceedsFit = !splitLinesForGroup(testGroup, colors, BASE_FONT_SIZE);
if (exceedsWords || exceedsFit) {
if (current.length) groups.push(makeGroup(current, groups.length));
current = [word];
return;
}
current.push(word);
var punctuation = /[,.:!?]$/.test(word.text);
var pause = nextWord ? nextWord.start - word.end : 0;
if (!nextWord || punctuation || pause >= PAUSE_THRESHOLD || current.length >= MAX_WORDS_PER_GROUP) {
groups.push(makeGroup(current, groups.length));
current = [];
}
});
if (current.length) groups.push(makeGroup(current, groups.length));
return groups;
}
function makeGroup(words, groupIndex) {
var colors = assignWordColors(words, groupIndex);
var fontSize = BASE_FONT_SIZE;
while (fontSize > MIN_FONT_SIZE && !splitLinesForGroup(words, colors, fontSize)) { fontSize -= 2; }
return {
words: words.slice(), colors: colors, fontSize: fontSize,
lines: splitLinesForGroup(words, colors, fontSize) || [{ words: words, colors: colors }],
start: words[0].start, end: words[words.length - 1].end,
};
}
function shadowForColor(color) {
var hex = color.replace("#", "");
var r = parseInt(hex.slice(0, 2), 16);
var g = parseInt(hex.slice(2, 4), 16);
var b = parseInt(hex.slice(4, 6), 16);
var glow = "0 0 4px rgba(" + r + "," + g + "," + b + ",1), 0 0 10px rgba(" + r + "," + g + "," + b + ",0.9), 0 0 20px rgba(" + r + "," + g + "," + b + ",0.7), 0 0 40px rgba(" + r + "," + g + "," + b + ",0.4)";
return "0 8px 16px rgba(0,0,0,0.8), 0 16px 40px rgba(0,0,0,0.6), " + glow;
}
function buildCaptions(groups) {
var stage = document.getElementById("caption-stage");
groups.forEach(function (group, groupIndex) {
var groupEl = document.createElement("div");
var copyEl = document.createElement("div");
groupEl.id = "caption-group-" + groupIndex;
groupEl.className = "caption-group";
copyEl.className = "caption-copy";
group.lines.slice(0, 2).forEach(function (line) {
var lineEl = document.createElement("div");
lineEl.className = "caption-line";
line.words.forEach(function (word, wordIndex) {
var color = line.colors[wordIndex] || PRIMARY_COLOR;
var wordEl = document.createElement("span");
wordEl.className = color === PRIMARY_COLOR ? "caption-word" : "caption-word accent";
wordEl.textContent = word.text.toUpperCase();
wordEl.style.color = color;
wordEl.style.fontSize = color === PRIMARY_COLOR ? group.fontSize + "px" : group.fontSize * 1.2 + "px";
wordEl.style.textShadow = shadowForColor(color);
lineEl.appendChild(wordEl);
});
copyEl.appendChild(lineEl);
});
groupEl.appendChild(copyEl);
stage.appendChild(groupEl);
});
}
var WORDS = normalizeWords(TRANSCRIPT);
var GROUPS = makeGroups(WORDS);
buildCaptions(GROUPS);
window.__timelines = window.__timelines || {};
var tl = gsap.timeline({ paused: true });
var visibleStarts = GROUPS.map(function (group, groupIndex) {
var prev = GROUPS[groupIndex - 1];
var availableGap = prev ? Math.max(0, group.start - prev.end) : ENTRY_DURATION;
var entryLead = Math.min(ENTRY_DURATION, availableGap);
return Math.max(0, group.start - entryLead);
});
var allGroupEls = GROUPS.map(function (_, i) { return document.getElementById("caption-group-" + i); });
GROUPS.forEach(function (group, groupIndex) {
var groupEl = allGroupEls[groupIndex];
var visibleStart = visibleStarts[groupIndex];
var visibleEnd = groupIndex < GROUPS.length - 1 ? visibleStarts[groupIndex + 1] : DURATION;
var direction = groupIndex % 2 === 0 ? -1 : 1;
var D = WIGGLE_DISTANCE * direction;
var wiggleCycles = Math.max(1, Math.ceil((visibleEnd - visibleStart) / WIGGLE_CYCLE));
allGroupEls.forEach(function (otherEl, otherIndex) {
if (otherIndex !== groupIndex) tl.set(otherEl, { opacity: 0 }, visibleStart);
});
tl.set(groupEl, { opacity: 0, scale: 0.65, x: 0, y: 0 }, visibleStart);
tl.to(groupEl, { opacity: 1, scale: 1, duration: ENTRY_DURATION, ease: "power3.out", force3D: true }, visibleStart);
tl.to(groupEl, { x: D, y: D, duration: WIGGLE_CYCLE / 2, ease: "sine.inOut", yoyo: true, repeat: Math.max(0, wiggleCycles * 2 - 1), force3D: true }, visibleStart);
tl.set(groupEl, { opacity: 0 }, visibleEnd);
});
window.__timelines["neon-accent"] = tl;
</script>
</body>
</html>
@@ -0,0 +1,15 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "caption-neon-accent",
"type": "hyperframes:component",
"title": "Neon Accent",
"description": "Multi-color neon glow accents with wiggle drift animation",
"tags": ["captions", "caption-style", "neon", "glow", "accent"],
"files": [
{
"path": "caption-neon-accent.html",
"target": "compositions/components/caption-neon-accent.html",
"type": "hyperframes:snippet"
}
]
}
@@ -0,0 +1,161 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Neon Glow</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: #000;
}
#neon-glow {
position: relative; width: 1920px; height: 1080px;
overflow: hidden; background: #000;
}
#ne-video {
position: absolute; inset: 0; z-index: 0;
width: 100%; height: 100%; object-fit: cover;
}
.ne-overlay {
position: absolute; inset: 0;
background: rgba(0,0,0,0.75); z-index: 1; pointer-events: none;
}
#ne-container {
position: absolute; inset: 0; z-index: 10; pointer-events: none;
}
.ne-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 80px;
opacity: 0; visibility: hidden;
}
.ne-word {
font-family: 'Outfit', sans-serif;
font-weight: 900; font-size: 96px;
text-transform: uppercase;
color: rgba(0,255,240,0.14);
display: inline-block; letter-spacing: 0.04em; line-height: 1;
}
</style>
</head>
<body>
<div
id="neon-glow"
data-composition-id="caption-neon-glow"
data-start="0"
data-duration="8"
data-fps="30"
data-width="1920"
data-height="1080"
>
<video id="ne-video" muted playsinline
data-start="0" data-duration="8" data-track-index="0" data-has-audio="false"></video>
<div class="ne-overlay"></div>
<div id="ne-container"></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
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}
];
var GROUPS = [
{wordStart:0, wordEnd:3, start:0.0, end:1.05},
{wordStart:4, wordEnd:7, start:1.1, end:2.05},
{wordStart:8, wordEnd:8, start:2.1, end:2.6},
{wordStart:9, wordEnd:12, start:2.65, end:3.5},
{wordStart:13, wordEnd:15, start:3.55, end:4.4},
{wordStart:16, wordEnd:16, start:3.95, end:4.75},
{wordStart:17, wordEnd:19, start:4.9, end:5.4},
{wordStart:20, wordEnd:22, start:5.65, end:6.2},
{wordStart:23, wordEnd:24, start:6.35, end:6.75},
{wordStart:25, wordEnd:27, start:6.8, end:7.8},
];
// Keywords get pink glow; others get cyan glow
var KEYWORDS = new Set([8, 12, 15, 24, 27]); // HyperFrames, HTML, professional, code, cinema.
var container = document.getElementById('ne-container');
var tl = gsap.timeline({ paused: true });
GROUPS.forEach(function(g, gi) {
var groupWords = WORDS.slice(g.wordStart, g.wordEnd + 1);
var grp = document.createElement('div');
grp.className = 'ne-group';
grp.id = 'ne-grp-' + gi;
groupWords.forEach(function(w, i) {
var wi = g.wordStart + i;
var span = document.createElement('span');
span.className = 'ne-word';
span.id = 'ne-w-' + wi;
span.textContent = w.text;
span.style.fontSize = '96px';
grp.appendChild(span);
});
container.appendChild(grp);
tl.set(grp, { visibility: 'visible', opacity: 0 }, g.start);
tl.fromTo(grp, { opacity: 0 }, { opacity: 1, duration: 0.05, ease: 'steps(1)' }, g.start);
tl.fromTo(grp, { x: -8 }, { x: 0, duration: 0.14, ease: 'power3.out' }, g.start + 0.05);
groupWords.forEach(function(w, i) {
var wi = g.wordStart + i;
var el = document.getElementById('ne-w-' + wi);
var isKW = KEYWORDS.has(wi);
var activeColor = isKW ? '#FF0099' : '#00FFF0';
tl.to(el, {
color: activeColor,
textShadow: '0 0 10px ' + activeColor + ', 0 0 35px ' + activeColor + ', 0 0 90px ' + activeColor,
duration: 0.06
}, w.start);
tl.to(el, { color: 'rgba(0,255,240,0.14)', textShadow: 'none', duration: 0.06 }, w.end);
});
tl.to(grp, { opacity: 0, x: 6, duration: 0.1, ease: 'steps(1)' }, g.end - 0.1);
tl.set(grp, { opacity: 0, visibility: 'hidden' }, g.end);
});
window.__timelines['neon-glow'] = tl;
})();
</script>
</body>
</html>
+161
View File
@@ -0,0 +1,161 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Neon Glow</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: #000;
}
#neon-glow {
position: relative; width: 1920px; height: 1080px;
overflow: hidden; background: #000;
}
#ne-video {
position: absolute; inset: 0; z-index: 0;
width: 100%; height: 100%; object-fit: cover;
}
.ne-overlay {
position: absolute; inset: 0;
background: rgba(0,0,0,0.75); z-index: 1; pointer-events: none;
}
#ne-container {
position: absolute; inset: 0; z-index: 10; pointer-events: none;
}
.ne-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 80px;
opacity: 0; visibility: hidden;
}
.ne-word {
font-family: 'Outfit', sans-serif;
font-weight: 900; font-size: 96px;
text-transform: uppercase;
color: rgba(0,255,240,0.14);
display: inline-block; letter-spacing: 0.04em; line-height: 1;
}
</style>
</head>
<body>
<div
id="neon-glow"
data-composition-id="caption-neon-glow"
data-start="0"
data-duration="8"
data-fps="30"
data-width="1920"
data-height="1080"
>
<video id="ne-video" muted playsinline
data-start="0" data-duration="8" data-track-index="0" data-has-audio="false"></video>
<div class="ne-overlay"></div>
<div id="ne-container"></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
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}
];
var GROUPS = [
{wordStart:0, wordEnd:3, start:0.0, end:1.05},
{wordStart:4, wordEnd:7, start:1.1, end:2.05},
{wordStart:8, wordEnd:8, start:2.1, end:2.6},
{wordStart:9, wordEnd:12, start:2.65, end:3.5},
{wordStart:13, wordEnd:15, start:3.55, end:4.4},
{wordStart:16, wordEnd:16, start:3.95, end:4.75},
{wordStart:17, wordEnd:19, start:4.9, end:5.4},
{wordStart:20, wordEnd:22, start:5.65, end:6.2},
{wordStart:23, wordEnd:24, start:6.35, end:6.75},
{wordStart:25, wordEnd:27, start:6.8, end:7.8},
];
// Keywords get pink glow; others get cyan glow
var KEYWORDS = new Set([8, 12, 15, 24, 27]); // HyperFrames, HTML, professional, code, cinema.
var container = document.getElementById('ne-container');
var tl = gsap.timeline({ paused: true });
GROUPS.forEach(function(g, gi) {
var groupWords = WORDS.slice(g.wordStart, g.wordEnd + 1);
var grp = document.createElement('div');
grp.className = 'ne-group';
grp.id = 'ne-grp-' + gi;
groupWords.forEach(function(w, i) {
var wi = g.wordStart + i;
var span = document.createElement('span');
span.className = 'ne-word';
span.id = 'ne-w-' + wi;
span.textContent = w.text;
span.style.fontSize = '96px';
grp.appendChild(span);
});
container.appendChild(grp);
tl.set(grp, { visibility: 'visible', opacity: 0 }, g.start);
tl.fromTo(grp, { opacity: 0 }, { opacity: 1, duration: 0.05, ease: 'steps(1)' }, g.start);
tl.fromTo(grp, { x: -8 }, { x: 0, duration: 0.14, ease: 'power3.out' }, g.start + 0.05);
groupWords.forEach(function(w, i) {
var wi = g.wordStart + i;
var el = document.getElementById('ne-w-' + wi);
var isKW = KEYWORDS.has(wi);
var activeColor = isKW ? '#FF0099' : '#00FFF0';
tl.to(el, {
color: activeColor,
textShadow: '0 0 10px ' + activeColor + ', 0 0 35px ' + activeColor + ', 0 0 90px ' + activeColor,
duration: 0.06
}, w.start);
tl.to(el, { color: 'rgba(0,255,240,0.14)', textShadow: 'none', duration: 0.06 }, w.end);
});
tl.to(grp, { opacity: 0, x: 6, duration: 0.1, ease: 'steps(1)' }, g.end - 0.1);
tl.set(grp, { opacity: 0, visibility: 'hidden' }, g.end);
});
window.__timelines['neon-glow'] = tl;
})();
</script>
</body>
</html>
@@ -0,0 +1,15 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "caption-neon-glow",
"type": "hyperframes:component",
"title": "Neon Glow",
"description": "Cyan and magenta neon glow with keyword accent colors",
"tags": ["captions", "caption-style", "neon", "glow", "gaming"],
"files": [
{
"path": "caption-neon-glow.html",
"target": "compositions/components/caption-neon-glow.html",
"type": "hyperframes:snippet"
}
]
}
@@ -0,0 +1,329 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Parallax Layers</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=Instrument+Serif:ital@0;1&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: #000;
font-family: "Instrument Serif", serif;
}
#parallax-layers {
position: relative;
width: 1920px;
height: 1080px;
overflow: hidden;
background: #000;
}
#bg-video {
position: absolute;
inset: 0;
z-index: 1;
width: 100%;
height: 100%;
object-fit: cover;
}
/* Behind captions: big red 3D text */
.behind-caption-layer {
position: absolute;
inset: 0;
z-index: 10;
pointer-events: none;
transform: translateZ(0);
}
/* Front captions */
.front-caption-layer {
position: absolute;
inset: 0;
z-index: 30;
pointer-events: none;
transform: translateZ(0);
}
.behind-safe-zone {
position: absolute;
top: 240px;
left: 0;
width: 1920px;
height: 500px;
}
.front-safe-zone {
position: absolute;
top: 700px;
left: 0;
width: 1920px;
height: 300px;
}
.caption-block {
position: absolute;
left: 50%;
top: 0;
transform: translateX(-50%);
opacity: 0;
transform-origin: 50% 50%;
will-change: transform, opacity;
backface-visibility: hidden;
}
.caption-line {
display: flex;
align-items: baseline;
justify-content: center;
gap: 14px;
line-height: 1.1;
white-space: nowrap;
}
.caption-line--behind {
transform: scaleY(2.8);
transform-origin: 50% 100%;
}
.word {
display: inline-block;
will-change: transform, opacity;
backface-visibility: hidden;
}
.word--behind {
font-family: "Instrument Serif", serif;
font-size: 220px;
font-weight: 400;
font-style: normal;
line-height: 0.9;
color: #E50914;
-webkit-text-stroke: 5px #E50914;
text-shadow: 2px 4px 4px #A30610;
}
.word--front-normal {
font-family: "Instrument Serif", serif;
font-size: 130px;
font-weight: 400;
font-style: normal;
color: #EEEEEE;
text-shadow: 0 2px 12px rgba(0, 0, 0, 0.6), 0 4px 24px rgba(0, 0, 0, 0.35);
}
.word--front-italic {
font-family: "Instrument Serif", serif;
font-size: 130px;
font-weight: 400;
font-style: italic;
color: #EEEEEE;
text-shadow: 0 2px 12px rgba(0, 0, 0, 0.6), 0 4px 24px rgba(0, 0, 0, 0.35);
}
</style>
</head>
<body>
<div
id="parallax-layers"
data-composition-id="caption-parallax-layers"
data-start="0"
data-duration="8"
data-fps="30"
data-width="1920"
data-height="1080"
>
<video
id="bg-video"
muted
playsinline
preload="auto"
data-start="0"
data-duration="8"
data-track-index="0"
data-has-audio="false"
></video>
<div class="behind-caption-layer" aria-hidden="true">
<div id="behind-stage" class="behind-safe-zone"></div>
</div>
<div class="front-caption-layer" aria-hidden="true">
<div id="front-stage" class="front-safe-zone"></div>
</div>
</div>
<script>
var DURATION = 8;
var BEHIND_WIDTH = 1800;
var FRONT_WIDTH = 1750;
var ENTRY_DUR = 0.1;
var SLIDE_DUR = 0.2;
var W = [
{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}
];
// behind=red 3D emphasis words, front=normal white words
var BLOCKS = [
{ behind: null, front: [[0],[1],[2]] },
{ behind: [[3]], front: [[4],[5]] },
{ behind: [[6]], front: [[7]] },
{ behind: [[8]], front: [[9],[10],[11]] },
{ behind: [[12]], front: [[13],[14]] },
{ behind: [[15]], front: [[16]] },
{ behind: null, front: [[17],[18]] },
{ behind: null, front: [[19],[20],[21],[22]] },
{ behind: [[24]], front: [[23],[25],[26]] },
{ behind: [[27]], front: null },
];
var behindStage = document.getElementById("behind-stage");
var frontStage = document.getElementById("front-stage");
BLOCKS.forEach(function (block, bi) {
var bEl = document.createElement("div");
bEl.className = "caption-block";
bEl.id = "bb" + bi;
if (block.behind) {
var bLine = document.createElement("div");
bLine.className = "caption-line caption-line--behind";
block.behind.forEach(function (wi_arr, wi) {
var span = document.createElement("span");
span.className = "word word--behind";
span.id = "bb" + bi + "w" + wi;
span.textContent = W[wi_arr[0]].text;
bLine.appendChild(span);
});
bEl.appendChild(bLine);
}
behindStage.appendChild(bEl);
var fEl = document.createElement("div");
fEl.className = "caption-block";
fEl.id = "fb" + bi;
if (block.front) {
var fLine = document.createElement("div");
fLine.className = "caption-line";
block.front.forEach(function (wi_arr, wi) {
var span = document.createElement("span");
var isItalic = wi_arr[0] === 7 || wi_arr[0] === 18; // frame., timeline.
span.className = "word " + (isItalic ? "word--front-italic" : "word--front-normal");
span.id = "fb" + bi + "w" + wi;
span.textContent = W[wi_arr[0]].text;
fLine.appendChild(span);
});
fEl.appendChild(fLine);
}
frontStage.appendChild(fEl);
});
var allBehind = BLOCKS.map(function (_, i) { return document.getElementById("bb" + i); });
var allFront = BLOCKS.map(function (_, i) { return document.getElementById("fb" + i); });
function applyBehindScales() {
BLOCKS.forEach(function (block, bi) {
if (block.behind) {
var line = allBehind[bi].querySelector(".caption-line");
if (line) {
var natW = line.scrollWidth;
if (natW > 0) {
var s = BEHIND_WIDTH / natW;
line.style.transform = "scaleX(" + s + ") scaleY(2.8)";
}
}
}
});
}
applyBehindScales();
window.__timelines = window.__timelines || {};
var tl = gsap.timeline({ paused: true });
function blockStart(bi) {
var b = BLOCKS[bi];
var firstIdx = b.behind ? b.behind[0][0] : b.front[0][0];
return W[firstIdx].start;
}
BLOCKS.forEach(function (block, bi) {
var start = blockStart(bi);
var nextStart = bi < BLOCKS.length - 1 ? blockStart(bi + 1) : DURATION;
var bEl = allBehind[bi];
var fEl = allFront[bi];
allBehind.forEach(function (other, oi) { if (oi !== bi) tl.set(other, { opacity: 0 }, start); });
allFront.forEach(function (other, oi) { if (oi !== bi) tl.set(other, { opacity: 0 }, start); });
if (block.behind) {
tl.set(bEl, { opacity: 1 }, start);
block.behind.forEach(function (wi_arr, wi) {
var wordEl = document.getElementById("bb" + bi + "w" + wi);
var wordStart = W[wi_arr[0]].start;
tl.set(wordEl, { opacity: 0 }, start);
tl.to(wordEl, { opacity: 1, duration: ENTRY_DUR, ease: "power2.out" }, wordStart);
});
}
if (block.front) {
tl.set(fEl, { opacity: 1 }, start);
block.front.forEach(function (wi_arr, wi) {
var wordEl = document.getElementById("fb" + bi + "w" + wi);
var wordStart = W[wi_arr[0]].start;
tl.set(wordEl, { opacity: 0 }, start);
tl.set(wordEl, { opacity: 1 }, wordStart);
});
}
tl.set(bEl, { opacity: 0 }, nextStart);
tl.set(fEl, { opacity: 0 }, nextStart);
});
tl.to({}, { duration: DURATION }, 0);
window.__timelines["parallax-layers"] = tl;
document.fonts.ready.then(function () { applyBehindScales(); });
</script>
</body>
</html>
+329
View File
@@ -0,0 +1,329 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Parallax Layers</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=Instrument+Serif:ital@0;1&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: #000;
font-family: "Instrument Serif", serif;
}
#parallax-layers {
position: relative;
width: 1920px;
height: 1080px;
overflow: hidden;
background: #000;
}
#bg-video {
position: absolute;
inset: 0;
z-index: 1;
width: 100%;
height: 100%;
object-fit: cover;
}
/* Behind captions: big red 3D text */
.behind-caption-layer {
position: absolute;
inset: 0;
z-index: 10;
pointer-events: none;
transform: translateZ(0);
}
/* Front captions */
.front-caption-layer {
position: absolute;
inset: 0;
z-index: 30;
pointer-events: none;
transform: translateZ(0);
}
.behind-safe-zone {
position: absolute;
top: 240px;
left: 0;
width: 1920px;
height: 500px;
}
.front-safe-zone {
position: absolute;
top: 700px;
left: 0;
width: 1920px;
height: 300px;
}
.caption-block {
position: absolute;
left: 50%;
top: 0;
transform: translateX(-50%);
opacity: 0;
transform-origin: 50% 50%;
will-change: transform, opacity;
backface-visibility: hidden;
}
.caption-line {
display: flex;
align-items: baseline;
justify-content: center;
gap: 14px;
line-height: 1.1;
white-space: nowrap;
}
.caption-line--behind {
transform: scaleY(2.8);
transform-origin: 50% 100%;
}
.word {
display: inline-block;
will-change: transform, opacity;
backface-visibility: hidden;
}
.word--behind {
font-family: "Instrument Serif", serif;
font-size: 220px;
font-weight: 400;
font-style: normal;
line-height: 0.9;
color: #E50914;
-webkit-text-stroke: 5px #E50914;
text-shadow: 2px 4px 4px #A30610;
}
.word--front-normal {
font-family: "Instrument Serif", serif;
font-size: 130px;
font-weight: 400;
font-style: normal;
color: #EEEEEE;
text-shadow: 0 2px 12px rgba(0, 0, 0, 0.6), 0 4px 24px rgba(0, 0, 0, 0.35);
}
.word--front-italic {
font-family: "Instrument Serif", serif;
font-size: 130px;
font-weight: 400;
font-style: italic;
color: #EEEEEE;
text-shadow: 0 2px 12px rgba(0, 0, 0, 0.6), 0 4px 24px rgba(0, 0, 0, 0.35);
}
</style>
</head>
<body>
<div
id="parallax-layers"
data-composition-id="caption-parallax-layers"
data-start="0"
data-duration="8"
data-fps="30"
data-width="1920"
data-height="1080"
>
<video
id="bg-video"
muted
playsinline
preload="auto"
data-start="0"
data-duration="8"
data-track-index="0"
data-has-audio="false"
></video>
<div class="behind-caption-layer" aria-hidden="true">
<div id="behind-stage" class="behind-safe-zone"></div>
</div>
<div class="front-caption-layer" aria-hidden="true">
<div id="front-stage" class="front-safe-zone"></div>
</div>
</div>
<script>
var DURATION = 8;
var BEHIND_WIDTH = 1800;
var FRONT_WIDTH = 1750;
var ENTRY_DUR = 0.1;
var SLIDE_DUR = 0.2;
var W = [
{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}
];
// behind=red 3D emphasis words, front=normal white words
var BLOCKS = [
{ behind: null, front: [[0],[1],[2]] },
{ behind: [[3]], front: [[4],[5]] },
{ behind: [[6]], front: [[7]] },
{ behind: [[8]], front: [[9],[10],[11]] },
{ behind: [[12]], front: [[13],[14]] },
{ behind: [[15]], front: [[16]] },
{ behind: null, front: [[17],[18]] },
{ behind: null, front: [[19],[20],[21],[22]] },
{ behind: [[24]], front: [[23],[25],[26]] },
{ behind: [[27]], front: null },
];
var behindStage = document.getElementById("behind-stage");
var frontStage = document.getElementById("front-stage");
BLOCKS.forEach(function (block, bi) {
var bEl = document.createElement("div");
bEl.className = "caption-block";
bEl.id = "bb" + bi;
if (block.behind) {
var bLine = document.createElement("div");
bLine.className = "caption-line caption-line--behind";
block.behind.forEach(function (wi_arr, wi) {
var span = document.createElement("span");
span.className = "word word--behind";
span.id = "bb" + bi + "w" + wi;
span.textContent = W[wi_arr[0]].text;
bLine.appendChild(span);
});
bEl.appendChild(bLine);
}
behindStage.appendChild(bEl);
var fEl = document.createElement("div");
fEl.className = "caption-block";
fEl.id = "fb" + bi;
if (block.front) {
var fLine = document.createElement("div");
fLine.className = "caption-line";
block.front.forEach(function (wi_arr, wi) {
var span = document.createElement("span");
var isItalic = wi_arr[0] === 7 || wi_arr[0] === 18; // frame., timeline.
span.className = "word " + (isItalic ? "word--front-italic" : "word--front-normal");
span.id = "fb" + bi + "w" + wi;
span.textContent = W[wi_arr[0]].text;
fLine.appendChild(span);
});
fEl.appendChild(fLine);
}
frontStage.appendChild(fEl);
});
var allBehind = BLOCKS.map(function (_, i) { return document.getElementById("bb" + i); });
var allFront = BLOCKS.map(function (_, i) { return document.getElementById("fb" + i); });
function applyBehindScales() {
BLOCKS.forEach(function (block, bi) {
if (block.behind) {
var line = allBehind[bi].querySelector(".caption-line");
if (line) {
var natW = line.scrollWidth;
if (natW > 0) {
var s = BEHIND_WIDTH / natW;
line.style.transform = "scaleX(" + s + ") scaleY(2.8)";
}
}
}
});
}
applyBehindScales();
window.__timelines = window.__timelines || {};
var tl = gsap.timeline({ paused: true });
function blockStart(bi) {
var b = BLOCKS[bi];
var firstIdx = b.behind ? b.behind[0][0] : b.front[0][0];
return W[firstIdx].start;
}
BLOCKS.forEach(function (block, bi) {
var start = blockStart(bi);
var nextStart = bi < BLOCKS.length - 1 ? blockStart(bi + 1) : DURATION;
var bEl = allBehind[bi];
var fEl = allFront[bi];
allBehind.forEach(function (other, oi) { if (oi !== bi) tl.set(other, { opacity: 0 }, start); });
allFront.forEach(function (other, oi) { if (oi !== bi) tl.set(other, { opacity: 0 }, start); });
if (block.behind) {
tl.set(bEl, { opacity: 1 }, start);
block.behind.forEach(function (wi_arr, wi) {
var wordEl = document.getElementById("bb" + bi + "w" + wi);
var wordStart = W[wi_arr[0]].start;
tl.set(wordEl, { opacity: 0 }, start);
tl.to(wordEl, { opacity: 1, duration: ENTRY_DUR, ease: "power2.out" }, wordStart);
});
}
if (block.front) {
tl.set(fEl, { opacity: 1 }, start);
block.front.forEach(function (wi_arr, wi) {
var wordEl = document.getElementById("fb" + bi + "w" + wi);
var wordStart = W[wi_arr[0]].start;
tl.set(wordEl, { opacity: 0 }, start);
tl.set(wordEl, { opacity: 1 }, wordStart);
});
}
tl.set(bEl, { opacity: 0 }, nextStart);
tl.set(fEl, { opacity: 0 }, nextStart);
});
tl.to({}, { duration: DURATION }, 0);
window.__timelines["parallax-layers"] = tl;
document.fonts.ready.then(function () { applyBehindScales(); });
</script>
</body>
</html>
@@ -0,0 +1,15 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "caption-parallax-layers",
"type": "hyperframes:component",
"title": "Parallax Layers",
"description": "Behind-subject 3D text layering with vertical stretch effect",
"tags": ["captions", "caption-style", "parallax", "3d", "depth"],
"files": [
{
"path": "caption-parallax-layers.html",
"target": "compositions/components/caption-parallax-layers.html",
"type": "hyperframes:snippet"
}
]
}
@@ -0,0 +1,212 @@
<!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: #0a0a0a;
}
#particle-burst {
position: relative; width: 1920px; height: 1080px;
overflow: hidden; background: #0a0a0a;
}
#bs-video {
position: absolute; inset: 0; z-index: 0;
width: 100%; height: 100%; object-fit: cover;
}
.bs-overlay {
position: absolute; inset: 0;
background: rgba(0,0,0,0.45); 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-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 || {};
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 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 = '88px';
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['particle-burst'] = tl;
})();
</script>
</body>
</html>
+212
View File
@@ -0,0 +1,212 @@
<!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: #0a0a0a;
}
#particle-burst {
position: relative; width: 1920px; height: 1080px;
overflow: hidden; background: #0a0a0a;
}
#bs-video {
position: absolute; inset: 0; z-index: 0;
width: 100%; height: 100%; object-fit: cover;
}
.bs-overlay {
position: absolute; inset: 0;
background: rgba(0,0,0,0.45); 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-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 || {};
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 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 = '88px';
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['particle-burst'] = tl;
})();
</script>
</body>
</html>
@@ -0,0 +1,15 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "caption-particle-burst",
"type": "hyperframes:component",
"title": "Particle Burst",
"description": "Keyword words trigger colored particle explosions",
"tags": ["captions", "caption-style", "particles", "burst", "effects"],
"files": [
{
"path": "caption-particle-burst.html",
"target": "compositions/components/caption-particle-burst.html",
"type": "hyperframes:snippet"
}
]
}
@@ -0,0 +1,365 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Pill Karaoke</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=Poppins:wght@700&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: #000000;
font-family: "Poppins", Arial, sans-serif;
}
#pill-karaoke {
position: relative;
width: 1920px;
height: 1080px;
overflow: hidden;
background: #000000;
}
#avatar-video {
position: absolute;
inset: 0;
z-index: 1;
width: 100%;
height: 100%;
object-fit: cover;
}
.caption-layer {
position: absolute;
inset: 0;
z-index: 20;
pointer-events: none;
transform: translateZ(0);
}
.safe-zone {
position: absolute;
bottom: 100px;
left: 50%;
transform: translateX(-50%);
width: 1600px;
height: 220px;
display: flex;
align-items: center;
justify-content: center;
}
.caption-group {
position: absolute;
left: 0;
top: 0;
width: 1600px;
height: 220px;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
}
.caption-pill {
width: auto;
max-width: 1600px;
padding: 18px 60px 20px;
border-radius: 22px;
background: #e7e5e7;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
text-align: center;
overflow: visible;
}
.caption-copy {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
color: #a6a6a6;
font-family: "Poppins", Arial, sans-serif;
font-size: 72px;
font-weight: 700;
line-height: 1.16;
letter-spacing: 0;
white-space: nowrap;
word-break: normal;
overflow: visible;
}
.caption-line {
display: flex;
justify-content: center;
gap: 16px;
width: 100%;
max-width: none;
line-height: 1.16;
white-space: nowrap;
overflow: visible;
}
.caption-word {
display: inline-block;
color: #a6a6a6;
}
</style>
</head>
<body>
<div
id="pill-karaoke"
data-composition-id="caption-pill-karaoke"
data-start="0"
data-duration="8"
data-fps="30"
data-width="1920"
data-height="1080"
>
<video
id="avatar-video"
muted
playsinline
data-start="0"
data-duration="8"
data-track-index="0"
data-has-audio="false"
></video>
<div class="caption-layer" aria-hidden="true">
<div id="caption-stage" class="safe-zone"></div>
</div>
</div>
<script>
var DURATION = 8;
var SAFE_ZONE_WIDTH = 1400;
var PILL_MAX_WIDTH = 1400;
var PILL_MIN_PADDING_X = 30;
var PILL_MAX_PADDING_X = 60;
var FONT_WIDTH_SAFETY = 1.18;
var FONT_FAMILY = "Poppins";
var FONT_WEIGHT = "700";
var BASE_FONT_SIZE = 72;
var MIN_FONT_SIZE = 52;
var MAX_WORDS_PER_GROUP = 4;
var WORD_SPACING = 16;
var PAUSE_THRESHOLD = 0.1;
var GROUP_END_BUFFER = 0.3;
var COLOR_INACTIVE = "#A6A6A6";
var COLOR_ACTIVE = "#1C1E1D";
var COLOR_FADE_DURATION = 0.1;
var WORD_FADE_LEAD = 0.05;
var TRANSCRIPT = [
{"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}
];
function normalizeWords(rawWords) {
return rawWords.map(function (item) {
var text = String(item.word || item.text || "").trim();
return {
text: text,
start: Math.max(0, Number(item.start) || 0),
end: Math.min(DURATION, Math.max(Number(item.start) || 0, Number(item.end) || 0)),
};
}).filter(function (w) { return w.text.length > 0; });
}
var measureCanvas = document.createElement("canvas");
var measureContext = measureCanvas.getContext("2d");
function measureWordWidth(text, fontSize) {
measureContext.font = FONT_WEIGHT + " " + fontSize + "px " + FONT_FAMILY;
return measureContext.measureText(text).width * FONT_WIDTH_SAFETY;
}
function measureLineWidth(words, fontSize) {
return words.reduce(function (total, word, index) {
return total + measureWordWidth(word.text.toLowerCase(), fontSize) + (index ? WORD_SPACING : 0);
}, 0);
}
function splitLinesForGroup(words, fontSize) {
var maxLineWidth = PILL_MAX_WIDTH - PILL_MIN_PADDING_X * 2;
if (words.some(function (w) { return measureWordWidth(w.text.toLowerCase(), fontSize) > maxLineWidth; })) {
return null;
}
if (measureLineWidth(words, fontSize) <= maxLineWidth) return [words];
var bestSplit = null;
var bestImbalance = Infinity;
for (var split = 1; split < words.length; split++) {
var firstLine = words.slice(0, split);
var secondLine = words.slice(split);
var firstWidth = measureLineWidth(firstLine, fontSize);
var secondWidth = measureLineWidth(secondLine, fontSize);
if (firstWidth <= maxLineWidth && secondWidth <= maxLineWidth) {
var imbalance = Math.abs(firstWidth - secondWidth);
if (imbalance < bestImbalance) {
bestSplit = [firstLine, secondLine];
bestImbalance = imbalance;
}
}
}
return bestSplit;
}
function fitsInTwoLinesAtSize(words, fontSize) {
return splitLinesForGroup(words, fontSize) !== null;
}
function planPillLayout(words, fontSize) {
var lines = splitLinesForGroup(words, fontSize) || [words];
var widestLine = lines.reduce(function (max, lineWords) {
return Math.max(max, measureLineWidth(lineWords, fontSize));
}, 0);
var availablePadding = Math.floor((PILL_MAX_WIDTH - widestLine) / 2);
var padX = Math.max(PILL_MIN_PADDING_X, Math.min(PILL_MAX_PADDING_X, availablePadding));
var pillWidth = Math.min(PILL_MAX_WIDTH, Math.ceil(widestLine + padX * 2));
return { lines: lines, padX: padX, pillWidth: pillWidth };
}
function makeGroups(words) {
var groups = [];
var current = [];
words.forEach(function (word, index) {
var testGroup = current.concat([word]);
var nextWord = words[index + 1];
var wouldExceedMaxWords = testGroup.length > MAX_WORDS_PER_GROUP;
var wouldExceedTwoLines = !fitsInTwoLinesAtSize(testGroup, BASE_FONT_SIZE);
if (wouldExceedMaxWords || wouldExceedTwoLines) {
if (current.length) {
groups.push({ words: current.slice(), start: current[0].start, end: current[current.length - 1].end });
}
current = [word];
return;
}
current.push(word);
var hasPunctuation = /[,.:!?]$/.test(word.text);
var pauseDuration = nextWord ? nextWord.start - word.end : 0;
var hasNaturalPause = pauseDuration >= PAUSE_THRESHOLD;
var maxWordsReached = current.length >= MAX_WORDS_PER_GROUP;
if (maxWordsReached || !nextWord || hasPunctuation || hasNaturalPause) {
groups.push({ words: current.slice(), start: current[0].start, end: current[current.length - 1].end });
current = [];
}
});
if (current.length) groups.push({ words: current.slice(), start: current[0].start, end: current[current.length - 1].end });
return groups;
}
function fontSizeForGroup(words) {
var size = BASE_FONT_SIZE;
while (size > MIN_FONT_SIZE && !fitsInTwoLinesAtSize(words, size)) { size -= 2; }
return size;
}
function buildCaptions(groups) {
var stage = document.getElementById("caption-stage");
groups.forEach(function (group, groupIndex) {
var groupEl = document.createElement("div");
var pillEl = document.createElement("div");
var copyEl = document.createElement("div");
var fontSize = fontSizeForGroup(group.words);
groupEl.id = "caption-group-" + groupIndex;
groupEl.className = "caption-group";
pillEl.className = "caption-pill";
copyEl.className = "caption-copy";
copyEl.style.fontSize = fontSize + "px";
var indexedWords = group.words.map(function (word, wordIndex) {
return { text: word.text, start: word.start, end: word.end, wordIndex: wordIndex };
});
var layout = planPillLayout(indexedWords, fontSize);
pillEl.style.width = layout.pillWidth + "px";
pillEl.style.paddingLeft = layout.padX + "px";
pillEl.style.paddingRight = layout.padX + "px";
layout.lines.slice(0, 2).forEach(function (lineWords) {
var lineEl = document.createElement("div");
lineEl.className = "caption-line";
lineWords.forEach(function (word) {
var wordEl = document.createElement("span");
wordEl.id = "caption-word-" + groupIndex + "-" + word.wordIndex;
wordEl.className = "caption-word";
wordEl.textContent = word.text.toLowerCase();
lineEl.appendChild(wordEl);
});
copyEl.appendChild(lineEl);
});
pillEl.appendChild(copyEl);
groupEl.appendChild(pillEl);
stage.appendChild(groupEl);
});
}
var WORDS = normalizeWords(TRANSCRIPT);
var GROUPS = makeGroups(WORDS);
buildCaptions(GROUPS);
window.__timelines = window.__timelines || {};
var tl = gsap.timeline({ paused: true });
GROUPS.forEach(function (group, groupIndex) {
var groupEl = document.getElementById("caption-group-" + groupIndex);
var nextGroup = GROUPS[groupIndex + 1];
var isLastGroup = groupIndex === GROUPS.length - 1;
var effectiveEnd = isLastGroup ? DURATION : Math.min(nextGroup.start, group.end + GROUP_END_BUFFER);
var visibleStart = Math.max(0, group.start);
var visibleEnd = Math.max(visibleStart + 0.01, effectiveEnd);
tl.set(groupEl, { opacity: 1 }, visibleStart);
tl.set(groupEl, { opacity: 0 }, visibleEnd);
group.words.forEach(function (word, wordIndex) {
var wordEl = document.getElementById("caption-word-" + groupIndex + "-" + wordIndex);
var isFirstWord = wordIndex === 0;
var wordStart = Math.max(visibleStart, word.start - WORD_FADE_LEAD);
tl.set(wordEl, { color: isFirstWord ? COLOR_ACTIVE : COLOR_INACTIVE }, visibleStart);
if (isFirstWord) return;
tl.to(wordEl, { color: COLOR_ACTIVE, duration: COLOR_FADE_DURATION, ease: "none" }, wordStart);
});
});
window.__timelines["pill-karaoke"] = tl;
</script>
</body>
</html>
+365
View File
@@ -0,0 +1,365 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Pill Karaoke</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=Poppins:wght@700&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: #000000;
font-family: "Poppins", Arial, sans-serif;
}
#pill-karaoke {
position: relative;
width: 1920px;
height: 1080px;
overflow: hidden;
background: #000000;
}
#avatar-video {
position: absolute;
inset: 0;
z-index: 1;
width: 100%;
height: 100%;
object-fit: cover;
}
.caption-layer {
position: absolute;
inset: 0;
z-index: 20;
pointer-events: none;
transform: translateZ(0);
}
.safe-zone {
position: absolute;
bottom: 100px;
left: 50%;
transform: translateX(-50%);
width: 1600px;
height: 220px;
display: flex;
align-items: center;
justify-content: center;
}
.caption-group {
position: absolute;
left: 0;
top: 0;
width: 1600px;
height: 220px;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
}
.caption-pill {
width: auto;
max-width: 1600px;
padding: 18px 60px 20px;
border-radius: 22px;
background: #e7e5e7;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.12);
text-align: center;
overflow: visible;
}
.caption-copy {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
color: #a6a6a6;
font-family: "Poppins", Arial, sans-serif;
font-size: 72px;
font-weight: 700;
line-height: 1.16;
letter-spacing: 0;
white-space: nowrap;
word-break: normal;
overflow: visible;
}
.caption-line {
display: flex;
justify-content: center;
gap: 16px;
width: 100%;
max-width: none;
line-height: 1.16;
white-space: nowrap;
overflow: visible;
}
.caption-word {
display: inline-block;
color: #a6a6a6;
}
</style>
</head>
<body>
<div
id="pill-karaoke"
data-composition-id="caption-pill-karaoke"
data-start="0"
data-duration="8"
data-fps="30"
data-width="1920"
data-height="1080"
>
<video
id="avatar-video"
muted
playsinline
data-start="0"
data-duration="8"
data-track-index="0"
data-has-audio="false"
></video>
<div class="caption-layer" aria-hidden="true">
<div id="caption-stage" class="safe-zone"></div>
</div>
</div>
<script>
var DURATION = 8;
var SAFE_ZONE_WIDTH = 1400;
var PILL_MAX_WIDTH = 1400;
var PILL_MIN_PADDING_X = 30;
var PILL_MAX_PADDING_X = 60;
var FONT_WIDTH_SAFETY = 1.18;
var FONT_FAMILY = "Poppins";
var FONT_WEIGHT = "700";
var BASE_FONT_SIZE = 72;
var MIN_FONT_SIZE = 52;
var MAX_WORDS_PER_GROUP = 4;
var WORD_SPACING = 16;
var PAUSE_THRESHOLD = 0.1;
var GROUP_END_BUFFER = 0.3;
var COLOR_INACTIVE = "#A6A6A6";
var COLOR_ACTIVE = "#1C1E1D";
var COLOR_FADE_DURATION = 0.1;
var WORD_FADE_LEAD = 0.05;
var TRANSCRIPT = [
{"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}
];
function normalizeWords(rawWords) {
return rawWords.map(function (item) {
var text = String(item.word || item.text || "").trim();
return {
text: text,
start: Math.max(0, Number(item.start) || 0),
end: Math.min(DURATION, Math.max(Number(item.start) || 0, Number(item.end) || 0)),
};
}).filter(function (w) { return w.text.length > 0; });
}
var measureCanvas = document.createElement("canvas");
var measureContext = measureCanvas.getContext("2d");
function measureWordWidth(text, fontSize) {
measureContext.font = FONT_WEIGHT + " " + fontSize + "px " + FONT_FAMILY;
return measureContext.measureText(text).width * FONT_WIDTH_SAFETY;
}
function measureLineWidth(words, fontSize) {
return words.reduce(function (total, word, index) {
return total + measureWordWidth(word.text.toLowerCase(), fontSize) + (index ? WORD_SPACING : 0);
}, 0);
}
function splitLinesForGroup(words, fontSize) {
var maxLineWidth = PILL_MAX_WIDTH - PILL_MIN_PADDING_X * 2;
if (words.some(function (w) { return measureWordWidth(w.text.toLowerCase(), fontSize) > maxLineWidth; })) {
return null;
}
if (measureLineWidth(words, fontSize) <= maxLineWidth) return [words];
var bestSplit = null;
var bestImbalance = Infinity;
for (var split = 1; split < words.length; split++) {
var firstLine = words.slice(0, split);
var secondLine = words.slice(split);
var firstWidth = measureLineWidth(firstLine, fontSize);
var secondWidth = measureLineWidth(secondLine, fontSize);
if (firstWidth <= maxLineWidth && secondWidth <= maxLineWidth) {
var imbalance = Math.abs(firstWidth - secondWidth);
if (imbalance < bestImbalance) {
bestSplit = [firstLine, secondLine];
bestImbalance = imbalance;
}
}
}
return bestSplit;
}
function fitsInTwoLinesAtSize(words, fontSize) {
return splitLinesForGroup(words, fontSize) !== null;
}
function planPillLayout(words, fontSize) {
var lines = splitLinesForGroup(words, fontSize) || [words];
var widestLine = lines.reduce(function (max, lineWords) {
return Math.max(max, measureLineWidth(lineWords, fontSize));
}, 0);
var availablePadding = Math.floor((PILL_MAX_WIDTH - widestLine) / 2);
var padX = Math.max(PILL_MIN_PADDING_X, Math.min(PILL_MAX_PADDING_X, availablePadding));
var pillWidth = Math.min(PILL_MAX_WIDTH, Math.ceil(widestLine + padX * 2));
return { lines: lines, padX: padX, pillWidth: pillWidth };
}
function makeGroups(words) {
var groups = [];
var current = [];
words.forEach(function (word, index) {
var testGroup = current.concat([word]);
var nextWord = words[index + 1];
var wouldExceedMaxWords = testGroup.length > MAX_WORDS_PER_GROUP;
var wouldExceedTwoLines = !fitsInTwoLinesAtSize(testGroup, BASE_FONT_SIZE);
if (wouldExceedMaxWords || wouldExceedTwoLines) {
if (current.length) {
groups.push({ words: current.slice(), start: current[0].start, end: current[current.length - 1].end });
}
current = [word];
return;
}
current.push(word);
var hasPunctuation = /[,.:!?]$/.test(word.text);
var pauseDuration = nextWord ? nextWord.start - word.end : 0;
var hasNaturalPause = pauseDuration >= PAUSE_THRESHOLD;
var maxWordsReached = current.length >= MAX_WORDS_PER_GROUP;
if (maxWordsReached || !nextWord || hasPunctuation || hasNaturalPause) {
groups.push({ words: current.slice(), start: current[0].start, end: current[current.length - 1].end });
current = [];
}
});
if (current.length) groups.push({ words: current.slice(), start: current[0].start, end: current[current.length - 1].end });
return groups;
}
function fontSizeForGroup(words) {
var size = BASE_FONT_SIZE;
while (size > MIN_FONT_SIZE && !fitsInTwoLinesAtSize(words, size)) { size -= 2; }
return size;
}
function buildCaptions(groups) {
var stage = document.getElementById("caption-stage");
groups.forEach(function (group, groupIndex) {
var groupEl = document.createElement("div");
var pillEl = document.createElement("div");
var copyEl = document.createElement("div");
var fontSize = fontSizeForGroup(group.words);
groupEl.id = "caption-group-" + groupIndex;
groupEl.className = "caption-group";
pillEl.className = "caption-pill";
copyEl.className = "caption-copy";
copyEl.style.fontSize = fontSize + "px";
var indexedWords = group.words.map(function (word, wordIndex) {
return { text: word.text, start: word.start, end: word.end, wordIndex: wordIndex };
});
var layout = planPillLayout(indexedWords, fontSize);
pillEl.style.width = layout.pillWidth + "px";
pillEl.style.paddingLeft = layout.padX + "px";
pillEl.style.paddingRight = layout.padX + "px";
layout.lines.slice(0, 2).forEach(function (lineWords) {
var lineEl = document.createElement("div");
lineEl.className = "caption-line";
lineWords.forEach(function (word) {
var wordEl = document.createElement("span");
wordEl.id = "caption-word-" + groupIndex + "-" + word.wordIndex;
wordEl.className = "caption-word";
wordEl.textContent = word.text.toLowerCase();
lineEl.appendChild(wordEl);
});
copyEl.appendChild(lineEl);
});
pillEl.appendChild(copyEl);
groupEl.appendChild(pillEl);
stage.appendChild(groupEl);
});
}
var WORDS = normalizeWords(TRANSCRIPT);
var GROUPS = makeGroups(WORDS);
buildCaptions(GROUPS);
window.__timelines = window.__timelines || {};
var tl = gsap.timeline({ paused: true });
GROUPS.forEach(function (group, groupIndex) {
var groupEl = document.getElementById("caption-group-" + groupIndex);
var nextGroup = GROUPS[groupIndex + 1];
var isLastGroup = groupIndex === GROUPS.length - 1;
var effectiveEnd = isLastGroup ? DURATION : Math.min(nextGroup.start, group.end + GROUP_END_BUFFER);
var visibleStart = Math.max(0, group.start);
var visibleEnd = Math.max(visibleStart + 0.01, effectiveEnd);
tl.set(groupEl, { opacity: 1 }, visibleStart);
tl.set(groupEl, { opacity: 0 }, visibleEnd);
group.words.forEach(function (word, wordIndex) {
var wordEl = document.getElementById("caption-word-" + groupIndex + "-" + wordIndex);
var isFirstWord = wordIndex === 0;
var wordStart = Math.max(visibleStart, word.start - WORD_FADE_LEAD);
tl.set(wordEl, { color: isFirstWord ? COLOR_ACTIVE : COLOR_INACTIVE }, visibleStart);
if (isFirstWord) return;
tl.to(wordEl, { color: COLOR_ACTIVE, duration: COLOR_FADE_DURATION, ease: "none" }, wordStart);
});
});
window.__timelines["pill-karaoke"] = tl;
</script>
</body>
</html>
@@ -0,0 +1,15 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "caption-pill-karaoke",
"type": "hyperframes:component",
"title": "Pill Karaoke",
"description": "Pill-shaped container with per-word karaoke color highlight",
"tags": ["captions", "caption-style", "karaoke", "pill"],
"files": [
{
"path": "caption-pill-karaoke.html",
"target": "compositions/components/caption-pill-karaoke.html",
"type": "hyperframes:snippet"
}
]
}
@@ -0,0 +1,172 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Texture Lava</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=Anton&display=swap" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
/* Inlined texture-mask-text component styles */
.hf-texture-text {
color: #fff;
-webkit-mask-size: var(--mask-size, cover);
mask-size: var(--mask-size, cover);
-webkit-mask-position: var(--mask-position, center);
mask-position: var(--mask-position, center);
-webkit-mask-mode: luminance;
mask-mode: luminance;
filter: contrast(var(--mask-contrast, 1));
}
.hf-texture-lava {
-webkit-mask-image: url("/assets/texture-mask-text/masks/lava.png");
mask-image: url("/assets/texture-mask-text/masks/lava.png");
}
*, *::before, *::after { box-sizing: border-box; }
html, body {
width: 1920px; height: 1080px; margin: 0;
overflow: hidden; background: #0a0805;
}
#texture-lava {
position: relative; width: 1920px; height: 1080px;
overflow: hidden; background: #0a0805;
}
#lv-video {
position: absolute; inset: 0; z-index: 0;
width: 100%; height: 100%; object-fit: cover;
}
.lv-overlay {
position: absolute; inset: 0;
background: rgba(0,0,0,0.55); z-index: 1; pointer-events: none;
}
#lv-container {
position: absolute; inset: 0; z-index: 10; pointer-events: none;
}
/* Lava-specific overrides for texture mask */
.lv-texture-override {
-webkit-mask-size: 200% 200%;
mask-size: 200% 200%;
-webkit-mask-position: 0% 50%;
mask-position: 0% 50%;
-webkit-mask-mode: luminance;
mask-mode: luminance;
filter: contrast(1.2);
}
.lv-shadow {
position: absolute;
left: 0; width: 100%; text-align: center;
top: 50%;
filter: drop-shadow(0 4px 24px rgba(255,100,20,0.55));
opacity: 0; visibility: hidden;
}
.lv-word {
font-family: 'Anton', sans-serif;
font-weight: 400;
text-transform: uppercase;
color: #FFD0A0;
display: inline-block;
letter-spacing: 0.04em; line-height: 1;
}
</style>
</head>
<body>
<div
id="texture-lava"
data-composition-id="caption-texture-lava"
data-start="0"
data-duration="8"
data-fps="30"
data-width="1920"
data-height="1080"
>
<video id="lv-video" muted playsinline
data-start="0" data-duration="8" data-track-index="0" data-has-audio="false"></video>
<div class="lv-overlay"></div>
<div id="lv-container"></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
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}
];
var KEYWORDS = new Set([8, 12, 15, 24, 27]); // HyperFrames, HTML, professional, code, cinema.
var container = document.getElementById('lv-container');
var tl = gsap.timeline({ paused: true });
WORDS.forEach(function(w, wi) {
var txt = w.text.toUpperCase();
var fontSize = 200;
var shadow = document.createElement('div');
shadow.className = 'lv-shadow';
shadow.id = 'lv-shadow-' + wi;
gsap.set(shadow, { yPercent: -50 });
var wordEl = document.createElement('div');
wordEl.className = 'hf-texture-text hf-texture-lava lv-texture-override lv-word';
wordEl.id = 'lv-word-' + wi;
wordEl.textContent = txt;
wordEl.style.fontSize = fontSize + 'px';
if (KEYWORDS.has(wi)) wordEl.style.color = '#FFAA44';
shadow.appendChild(wordEl);
container.appendChild(shadow);
var dur = Math.max(w.end - w.start + 0.3, 0.4);
tl.set(shadow, { visibility: 'visible', opacity: 0 }, w.start);
tl.fromTo(shadow,
{ opacity: 0, scale: 0.88 },
{ opacity: 1, scale: 1, duration: 0.18, ease: 'power3.out' },
w.start
);
tl.set(wordEl, { '--mask-position': '0% 50%' }, w.start);
tl.to(wordEl, { '--mask-position': '100% 50%', duration: dur, ease: 'sine.inOut' }, w.start);
tl.to(shadow, { opacity: 0, scale: 1.08, duration: 0.1, ease: 'power2.in' }, w.end);
tl.set(shadow, { opacity: 0, visibility: 'hidden' }, w.end + 0.1);
});
tl.seek(0);
window.__timelines['texture-lava'] = tl;
})();
</script>
</body>
</html>
+172
View File
@@ -0,0 +1,172 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Texture Lava</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=Anton&display=swap" rel="stylesheet" />
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
<style>
/* Inlined texture-mask-text component styles */
.hf-texture-text {
color: #fff;
-webkit-mask-size: var(--mask-size, cover);
mask-size: var(--mask-size, cover);
-webkit-mask-position: var(--mask-position, center);
mask-position: var(--mask-position, center);
-webkit-mask-mode: luminance;
mask-mode: luminance;
filter: contrast(var(--mask-contrast, 1));
}
.hf-texture-lava {
-webkit-mask-image: url("/assets/texture-mask-text/masks/lava.png");
mask-image: url("/assets/texture-mask-text/masks/lava.png");
}
*, *::before, *::after { box-sizing: border-box; }
html, body {
width: 1920px; height: 1080px; margin: 0;
overflow: hidden; background: #0a0805;
}
#texture-lava {
position: relative; width: 1920px; height: 1080px;
overflow: hidden; background: #0a0805;
}
#lv-video {
position: absolute; inset: 0; z-index: 0;
width: 100%; height: 100%; object-fit: cover;
}
.lv-overlay {
position: absolute; inset: 0;
background: rgba(0,0,0,0.55); z-index: 1; pointer-events: none;
}
#lv-container {
position: absolute; inset: 0; z-index: 10; pointer-events: none;
}
/* Lava-specific overrides for texture mask */
.lv-texture-override {
-webkit-mask-size: 200% 200%;
mask-size: 200% 200%;
-webkit-mask-position: 0% 50%;
mask-position: 0% 50%;
-webkit-mask-mode: luminance;
mask-mode: luminance;
filter: contrast(1.2);
}
.lv-shadow {
position: absolute;
left: 0; width: 100%; text-align: center;
top: 50%;
filter: drop-shadow(0 4px 24px rgba(255,100,20,0.55));
opacity: 0; visibility: hidden;
}
.lv-word {
font-family: 'Anton', sans-serif;
font-weight: 400;
text-transform: uppercase;
color: #FFD0A0;
display: inline-block;
letter-spacing: 0.04em; line-height: 1;
}
</style>
</head>
<body>
<div
id="texture-lava"
data-composition-id="caption-texture-lava"
data-start="0"
data-duration="8"
data-fps="30"
data-width="1920"
data-height="1080"
>
<video id="lv-video" muted playsinline
data-start="0" data-duration="8" data-track-index="0" data-has-audio="false"></video>
<div class="lv-overlay"></div>
<div id="lv-container"></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
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}
];
var KEYWORDS = new Set([8, 12, 15, 24, 27]); // HyperFrames, HTML, professional, code, cinema.
var container = document.getElementById('lv-container');
var tl = gsap.timeline({ paused: true });
WORDS.forEach(function(w, wi) {
var txt = w.text.toUpperCase();
var fontSize = 200;
var shadow = document.createElement('div');
shadow.className = 'lv-shadow';
shadow.id = 'lv-shadow-' + wi;
gsap.set(shadow, { yPercent: -50 });
var wordEl = document.createElement('div');
wordEl.className = 'hf-texture-text hf-texture-lava lv-texture-override lv-word';
wordEl.id = 'lv-word-' + wi;
wordEl.textContent = txt;
wordEl.style.fontSize = fontSize + 'px';
if (KEYWORDS.has(wi)) wordEl.style.color = '#FFAA44';
shadow.appendChild(wordEl);
container.appendChild(shadow);
var dur = Math.max(w.end - w.start + 0.3, 0.4);
tl.set(shadow, { visibility: 'visible', opacity: 0 }, w.start);
tl.fromTo(shadow,
{ opacity: 0, scale: 0.88 },
{ opacity: 1, scale: 1, duration: 0.18, ease: 'power3.out' },
w.start
);
tl.set(wordEl, { '--mask-position': '0% 50%' }, w.start);
tl.to(wordEl, { '--mask-position': '100% 50%', duration: dur, ease: 'sine.inOut' }, w.start);
tl.to(shadow, { opacity: 0, scale: 1.08, duration: 0.1, ease: 'power2.in' }, w.end);
tl.set(shadow, { opacity: 0, visibility: 'hidden' }, w.end + 0.1);
});
tl.seek(0);
window.__timelines['texture-lava'] = tl;
})();
</script>
</body>
</html>
Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

@@ -0,0 +1,15 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "caption-texture-lava",
"type": "hyperframes:component",
"title": "Texture Lava",
"description": "Flowing lava texture mask over large uppercase text",
"tags": ["captions", "caption-style", "texture", "lava", "mask"],
"files": [
{
"path": "caption-texture-lava.html",
"target": "compositions/components/caption-texture-lava.html",
"type": "hyperframes:snippet"
}
]
}
@@ -0,0 +1,181 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Typewriter</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=JetBrains+Mono:wght@700&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: #000;
}
#typewriter {
position: relative; width: 1920px; height: 1080px;
overflow: hidden; background: #000;
}
#tw-video {
position: absolute; inset: 0; z-index: 0;
width: 100%; height: 100%; object-fit: cover;
}
.tw-overlay {
position: absolute; inset: 0;
background: rgba(0,0,0,0.85); z-index: 1; pointer-events: none;
}
#tw-container {
position: absolute; inset: 0; z-index: 10; pointer-events: none;
}
.tw-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 80px;
opacity: 0; visibility: hidden;
}
.tw-word {
font-family: 'JetBrains Mono', monospace;
font-weight: 700; font-size: 76px;
color: #00FF41;
text-shadow: 0 0 12px rgba(0,255,65,0.5);
display: inline-block; letter-spacing: 0.04em; line-height: 1; white-space: nowrap;
}
.tw-char { display: none; }
.tw-cursor {
font-family: 'JetBrains Mono', monospace;
font-weight: 700; font-size: 76px;
color: #00FF41;
text-shadow: 0 0 12px rgba(0,255,65,0.5);
display: none;
}
</style>
</head>
<body>
<div
id="typewriter"
data-composition-id="caption-typewriter"
data-start="0"
data-duration="8"
data-fps="30"
data-width="1920"
data-height="1080"
>
<video id="tw-video" muted playsinline
data-start="0" data-duration="8" data-track-index="0" data-has-audio="false"></video>
<div class="tw-overlay"></div>
<div id="tw-container"></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
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}
];
var GROUPS = [
{wordStart:0, wordEnd:3, start:0.0, end:1.1},
{wordStart:4, wordEnd:7, start:1.1, end:2.05},
{wordStart:8, wordEnd:8, start:2.1, end:2.6},
{wordStart:9, wordEnd:12, start:2.65, end:3.5},
{wordStart:13, wordEnd:15, start:3.55, end:4.4},
{wordStart:16, wordEnd:16, start:3.95, end:4.75},
{wordStart:17, wordEnd:19, start:4.9, end:5.4},
{wordStart:20, wordEnd:22, start:5.65, end:6.2},
{wordStart:23, wordEnd:24, start:6.35, end:6.75},
{wordStart:25, wordEnd:27, start:6.8, end:7.8},
];
var container = document.getElementById('tw-container');
var tl = gsap.timeline({ paused: true });
GROUPS.forEach(function(g, gi) {
var groupWords = WORDS.slice(g.wordStart, g.wordEnd + 1);
var fontSize = 76;
var grp = document.createElement('div');
grp.className = 'tw-group';
grp.id = 'tw-grp-' + gi;
groupWords.forEach(function(w, i) {
var wi = g.wordStart + i;
var wordWrap = document.createElement('span');
wordWrap.className = 'tw-word';
wordWrap.id = 'tw-w-' + wi;
wordWrap.style.fontSize = fontSize + 'px';
w.text.split('').forEach(function(c, ci) {
var charEl = document.createElement('span');
charEl.className = 'tw-char';
charEl.id = 'tw-c-' + wi + '-' + ci;
charEl.textContent = c;
wordWrap.appendChild(charEl);
});
var cursor = document.createElement('span');
cursor.className = 'tw-cursor';
cursor.id = 'tw-cur-' + wi;
cursor.textContent = '_';
cursor.style.fontSize = fontSize + 'px';
wordWrap.appendChild(cursor);
grp.appendChild(wordWrap);
});
container.appendChild(grp);
tl.set(grp, { visibility: 'visible', opacity: 1 }, g.start);
groupWords.forEach(function(w, i) {
var wi = g.wordStart + i;
var chars = w.text.split('');
var wordDuration = Math.max(w.end - w.start, 0.1);
var charCount = chars.length;
var cursorEl = document.getElementById('tw-cur-' + wi);
tl.set(cursorEl, { display: 'inline' }, w.start);
chars.forEach(function(c, ci) {
var charEl = document.getElementById('tw-c-' + wi + '-' + ci);
tl.set(charEl, { display: 'inline' }, w.start + ci * (wordDuration / charCount));
});
tl.set(cursorEl, { display: 'none' }, w.end);
});
tl.to(grp, { opacity: 0, duration: 0.2 }, g.end - 0.2);
tl.set(grp, { opacity: 0, visibility: 'hidden' }, g.end);
});
window.__timelines['typewriter'] = tl;
})();
</script>
</body>
</html>
+181
View File
@@ -0,0 +1,181 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Typewriter</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=JetBrains+Mono:wght@700&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: #000;
}
#typewriter {
position: relative; width: 1920px; height: 1080px;
overflow: hidden; background: #000;
}
#tw-video {
position: absolute; inset: 0; z-index: 0;
width: 100%; height: 100%; object-fit: cover;
}
.tw-overlay {
position: absolute; inset: 0;
background: rgba(0,0,0,0.85); z-index: 1; pointer-events: none;
}
#tw-container {
position: absolute; inset: 0; z-index: 10; pointer-events: none;
}
.tw-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 80px;
opacity: 0; visibility: hidden;
}
.tw-word {
font-family: 'JetBrains Mono', monospace;
font-weight: 700; font-size: 76px;
color: #00FF41;
text-shadow: 0 0 12px rgba(0,255,65,0.5);
display: inline-block; letter-spacing: 0.04em; line-height: 1; white-space: nowrap;
}
.tw-char { display: none; }
.tw-cursor {
font-family: 'JetBrains Mono', monospace;
font-weight: 700; font-size: 76px;
color: #00FF41;
text-shadow: 0 0 12px rgba(0,255,65,0.5);
display: none;
}
</style>
</head>
<body>
<div
id="typewriter"
data-composition-id="caption-typewriter"
data-start="0"
data-duration="8"
data-fps="30"
data-width="1920"
data-height="1080"
>
<video id="tw-video" muted playsinline
data-start="0" data-duration="8" data-track-index="0" data-has-audio="false"></video>
<div class="tw-overlay"></div>
<div id="tw-container"></div>
</div>
<script>
(function () {
window.__timelines = window.__timelines || {};
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}
];
var GROUPS = [
{wordStart:0, wordEnd:3, start:0.0, end:1.1},
{wordStart:4, wordEnd:7, start:1.1, end:2.05},
{wordStart:8, wordEnd:8, start:2.1, end:2.6},
{wordStart:9, wordEnd:12, start:2.65, end:3.5},
{wordStart:13, wordEnd:15, start:3.55, end:4.4},
{wordStart:16, wordEnd:16, start:3.95, end:4.75},
{wordStart:17, wordEnd:19, start:4.9, end:5.4},
{wordStart:20, wordEnd:22, start:5.65, end:6.2},
{wordStart:23, wordEnd:24, start:6.35, end:6.75},
{wordStart:25, wordEnd:27, start:6.8, end:7.8},
];
var container = document.getElementById('tw-container');
var tl = gsap.timeline({ paused: true });
GROUPS.forEach(function(g, gi) {
var groupWords = WORDS.slice(g.wordStart, g.wordEnd + 1);
var fontSize = 76;
var grp = document.createElement('div');
grp.className = 'tw-group';
grp.id = 'tw-grp-' + gi;
groupWords.forEach(function(w, i) {
var wi = g.wordStart + i;
var wordWrap = document.createElement('span');
wordWrap.className = 'tw-word';
wordWrap.id = 'tw-w-' + wi;
wordWrap.style.fontSize = fontSize + 'px';
w.text.split('').forEach(function(c, ci) {
var charEl = document.createElement('span');
charEl.className = 'tw-char';
charEl.id = 'tw-c-' + wi + '-' + ci;
charEl.textContent = c;
wordWrap.appendChild(charEl);
});
var cursor = document.createElement('span');
cursor.className = 'tw-cursor';
cursor.id = 'tw-cur-' + wi;
cursor.textContent = '_';
cursor.style.fontSize = fontSize + 'px';
wordWrap.appendChild(cursor);
grp.appendChild(wordWrap);
});
container.appendChild(grp);
tl.set(grp, { visibility: 'visible', opacity: 1 }, g.start);
groupWords.forEach(function(w, i) {
var wi = g.wordStart + i;
var chars = w.text.split('');
var wordDuration = Math.max(w.end - w.start, 0.1);
var charCount = chars.length;
var cursorEl = document.getElementById('tw-cur-' + wi);
tl.set(cursorEl, { display: 'inline' }, w.start);
chars.forEach(function(c, ci) {
var charEl = document.getElementById('tw-c-' + wi + '-' + ci);
tl.set(charEl, { display: 'inline' }, w.start + ci * (wordDuration / charCount));
});
tl.set(cursorEl, { display: 'none' }, w.end);
});
tl.to(grp, { opacity: 0, duration: 0.2 }, g.end - 0.2);
tl.set(grp, { opacity: 0, visibility: 'hidden' }, g.end);
});
window.__timelines['typewriter'] = tl;
})();
</script>
</body>
</html>
@@ -0,0 +1,15 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "caption-typewriter",
"type": "hyperframes:component",
"title": "Typewriter",
"description": "Green terminal character-by-character reveal with blinking cursor",
"tags": ["captions", "caption-style", "terminal", "typewriter", "hacker"],
"files": [
{
"path": "caption-typewriter.html",
"target": "compositions/components/caption-typewriter.html",
"type": "hyperframes:snippet"
}
]
}
@@ -0,0 +1,323 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Weight Shift</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=Montserrat:wght@300;700&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: #000000;
font-family: "Montserrat", Arial, sans-serif;
}
#weight-shift {
position: relative;
width: 1920px;
height: 1080px;
overflow: hidden;
background: #000000;
}
#avatar-video {
position: absolute;
inset: 0;
z-index: 1;
width: 100%;
height: 100%;
object-fit: cover;
}
.caption-layer {
position: absolute;
inset: 0;
z-index: 20;
pointer-events: none;
transform: translateZ(0);
}
.safe-zone {
position: absolute;
bottom: 100px;
left: 50%;
transform: translateX(-50%);
width: 1400px;
height: 260px;
display: flex;
align-items: center;
justify-content: center;
}
.caption-group {
position: absolute;
left: 0;
top: 0;
width: 1400px;
height: 260px;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transform-origin: 50% 50%;
will-change: transform;
backface-visibility: hidden;
}
.caption-copy {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
max-width: 1400px;
text-align: center;
}
.caption-line {
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
width: max-content;
max-width: 1400px;
line-height: 1.1;
white-space: nowrap;
color: #ffffff;
font-family: "Montserrat", Arial, sans-serif;
font-weight: 300;
font-size: 72px;
letter-spacing: -0.03em;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
text-transform: lowercase;
}
</style>
</head>
<body>
<div
id="weight-shift"
data-composition-id="caption-weight-shift"
data-start="0"
data-duration="8"
data-fps="30"
data-width="1920"
data-height="1080"
>
<video
id="avatar-video"
muted
playsinline
data-start="0"
data-duration="8"
data-track-index="0"
data-has-audio="false"
></video>
<div class="caption-layer" aria-hidden="true">
<div id="caption-stage" class="safe-zone"></div>
</div>
</div>
<script>
var DURATION = 8;
var SAFE_ZONE_WIDTH = 1400;
var MAX_LINE_WIDTH = SAFE_ZONE_WIDTH - 40;
var CAPTION_FONT_SIZE = 72;
var MAX_WORDS_PER_GROUP = 4;
var WORD_SPACING = 12;
var PAUSE_THRESHOLD = 0.1;
var ENTRY_DURATION = 100 / 1000;
var WEIGHT_TRANSITION = 100 / 1000;
var FONT_WIDTH_SAFETY = 1.14;
var FONT_WEIGHT_LIGHT = 300;
var FONT_WEIGHT_BOLD = 700;
var TRANSCRIPT = [
{"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}
];
function normalizeWords(rawWords) {
return rawWords.map(function (item) {
return {
text: String(item.word || item.text || "").trim(),
start: Math.max(0, Number(item.start) || 0),
end: Math.min(DURATION, Math.max(Number(item.start) || 0, Number(item.end) || 0)),
};
}).filter(function (word) { return word.text.length > 0; });
}
var measureCanvas = document.createElement("canvas");
var measureContext = measureCanvas.getContext("2d");
measureContext.font = "700 " + CAPTION_FONT_SIZE + "px Montserrat";
function measureWord(word) {
return measureContext.measureText(word.text.toLowerCase()).width * FONT_WIDTH_SAFETY;
}
function measureLineWidth(words) {
return words.reduce(function (total, word, index) {
return total + measureWord(word) + (index ? WORD_SPACING : 0);
}, 0);
}
function fitsInTwoLines(words) {
return measureLineWidth(words) <= MAX_LINE_WIDTH * 1.8;
}
function splitLines(words) {
if (measureLineWidth(words) <= MAX_LINE_WIDTH) {
return [{ words: words, startIndex: 0 }];
}
var bestSplit = Math.ceil(words.length / 2);
var bestDiff = Infinity;
for (var s = 1; s < words.length; s++) {
var w1 = measureLineWidth(words.slice(0, s));
var w2 = measureLineWidth(words.slice(s));
if (w1 <= MAX_LINE_WIDTH && w2 <= MAX_LINE_WIDTH) {
var diff = Math.abs(w1 - w2);
if (diff < bestDiff) { bestDiff = diff; bestSplit = s; }
}
}
var result = [];
var line1 = words.slice(0, bestSplit);
var line2 = words.slice(bestSplit);
if (line1.length) result.push({ words: line1, startIndex: 0 });
if (line2.length) result.push({ words: line2, startIndex: line1.length });
return result;
}
function makeGroups(words) {
var groups = [];
var current = [];
words.forEach(function (word, index) {
var nextWord = words[index + 1];
var testGroup = current.concat([word]);
if (testGroup.length > MAX_WORDS_PER_GROUP || !fitsInTwoLines(testGroup)) {
if (current.length) groups.push(makeGroup(current));
current = [word];
return;
}
current.push(word);
var punctuation = /[,.:!?]$/.test(word.text);
var pause = nextWord ? nextWord.start - word.end : 0;
if (!nextWord || punctuation || pause >= PAUSE_THRESHOLD || current.length >= MAX_WORDS_PER_GROUP) {
groups.push(makeGroup(current));
current = [];
}
});
if (current.length) groups.push(makeGroup(current));
return groups;
}
function makeGroup(words) {
return { words: words.slice(), lines: splitLines(words), start: words[0].start, end: words[words.length - 1].end };
}
function buildCaptions(groups) {
var stage = document.getElementById("caption-stage");
groups.forEach(function (group, groupIndex) {
var groupEl = document.createElement("div");
var copyEl = document.createElement("div");
groupEl.id = "caption-group-" + groupIndex;
groupEl.className = "caption-group";
copyEl.className = "caption-copy";
group.lines.forEach(function (line, lineIndex) {
var lineEl = document.createElement("div");
lineEl.className = "caption-line";
lineEl.id = "caption-group-" + groupIndex + "-line-" + lineIndex;
lineEl.style.fontWeight = lineIndex === 0 ? FONT_WEIGHT_BOLD : FONT_WEIGHT_LIGHT;
line.words.forEach(function (word) {
var wordEl = document.createElement("span");
wordEl.textContent = word.text.toLowerCase();
lineEl.appendChild(wordEl);
});
copyEl.appendChild(lineEl);
});
groupEl.appendChild(copyEl);
stage.appendChild(groupEl);
});
}
var WORDS = normalizeWords(TRANSCRIPT);
var GROUPS = makeGroups(WORDS);
buildCaptions(GROUPS);
window.__timelines = window.__timelines || {};
var tl = gsap.timeline({ paused: true });
var visibleStarts = GROUPS.map(function (group, groupIndex) {
var prev = GROUPS[groupIndex - 1];
var availableGap = prev ? Math.max(0, group.start - prev.end) : ENTRY_DURATION;
var entryLead = Math.min(ENTRY_DURATION, availableGap);
return Math.max(0, group.start - entryLead);
});
var allGroupEls = GROUPS.map(function (_, i) { return document.getElementById("caption-group-" + i); });
GROUPS.forEach(function (group, groupIndex) {
var groupEl = allGroupEls[groupIndex];
var visibleStart = visibleStarts[groupIndex];
var visibleEnd = groupIndex < GROUPS.length - 1 ? visibleStarts[groupIndex + 1] : DURATION;
allGroupEls.forEach(function (otherEl, otherIndex) {
if (otherIndex !== groupIndex) tl.set(otherEl, { opacity: 0 }, visibleStart);
});
tl.set(groupEl, { opacity: 0, scale: 0.85 }, visibleStart);
tl.to(groupEl, { opacity: 1, scale: 1, duration: ENTRY_DURATION, ease: "power3.out", force3D: true }, visibleStart);
if (group.lines.length === 2) {
var line1El = document.getElementById("caption-group-" + groupIndex + "-line-0");
var line2El = document.getElementById("caption-group-" + groupIndex + "-line-1");
var line2FirstWord = group.lines[1].words[0];
var switchTime = line2FirstWord.start;
tl.to(line1El, { fontWeight: FONT_WEIGHT_LIGHT, duration: WEIGHT_TRANSITION, ease: "power2.out" }, switchTime);
tl.to(line2El, { fontWeight: FONT_WEIGHT_BOLD, duration: WEIGHT_TRANSITION, ease: "power2.out" }, switchTime);
}
tl.set(groupEl, { opacity: 0 }, visibleEnd);
});
window.__timelines["weight-shift"] = tl;
</script>
</body>
</html>
+323
View File
@@ -0,0 +1,323 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Weight Shift</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=Montserrat:wght@300;700&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: #000000;
font-family: "Montserrat", Arial, sans-serif;
}
#weight-shift {
position: relative;
width: 1920px;
height: 1080px;
overflow: hidden;
background: #000000;
}
#avatar-video {
position: absolute;
inset: 0;
z-index: 1;
width: 100%;
height: 100%;
object-fit: cover;
}
.caption-layer {
position: absolute;
inset: 0;
z-index: 20;
pointer-events: none;
transform: translateZ(0);
}
.safe-zone {
position: absolute;
bottom: 100px;
left: 50%;
transform: translateX(-50%);
width: 1400px;
height: 260px;
display: flex;
align-items: center;
justify-content: center;
}
.caption-group {
position: absolute;
left: 0;
top: 0;
width: 1400px;
height: 260px;
display: flex;
align-items: center;
justify-content: center;
opacity: 0;
transform-origin: 50% 50%;
will-change: transform;
backface-visibility: hidden;
}
.caption-copy {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
max-width: 1400px;
text-align: center;
}
.caption-line {
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
width: max-content;
max-width: 1400px;
line-height: 1.1;
white-space: nowrap;
color: #ffffff;
font-family: "Montserrat", Arial, sans-serif;
font-weight: 300;
font-size: 72px;
letter-spacing: -0.03em;
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
text-transform: lowercase;
}
</style>
</head>
<body>
<div
id="weight-shift"
data-composition-id="caption-weight-shift"
data-start="0"
data-duration="8"
data-fps="30"
data-width="1920"
data-height="1080"
>
<video
id="avatar-video"
muted
playsinline
data-start="0"
data-duration="8"
data-track-index="0"
data-has-audio="false"
></video>
<div class="caption-layer" aria-hidden="true">
<div id="caption-stage" class="safe-zone"></div>
</div>
</div>
<script>
var DURATION = 8;
var SAFE_ZONE_WIDTH = 1400;
var MAX_LINE_WIDTH = SAFE_ZONE_WIDTH - 40;
var CAPTION_FONT_SIZE = 72;
var MAX_WORDS_PER_GROUP = 4;
var WORD_SPACING = 12;
var PAUSE_THRESHOLD = 0.1;
var ENTRY_DURATION = 100 / 1000;
var WEIGHT_TRANSITION = 100 / 1000;
var FONT_WIDTH_SAFETY = 1.14;
var FONT_WEIGHT_LIGHT = 300;
var FONT_WEIGHT_BOLD = 700;
var TRANSCRIPT = [
{"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}
];
function normalizeWords(rawWords) {
return rawWords.map(function (item) {
return {
text: String(item.word || item.text || "").trim(),
start: Math.max(0, Number(item.start) || 0),
end: Math.min(DURATION, Math.max(Number(item.start) || 0, Number(item.end) || 0)),
};
}).filter(function (word) { return word.text.length > 0; });
}
var measureCanvas = document.createElement("canvas");
var measureContext = measureCanvas.getContext("2d");
measureContext.font = "700 " + CAPTION_FONT_SIZE + "px Montserrat";
function measureWord(word) {
return measureContext.measureText(word.text.toLowerCase()).width * FONT_WIDTH_SAFETY;
}
function measureLineWidth(words) {
return words.reduce(function (total, word, index) {
return total + measureWord(word) + (index ? WORD_SPACING : 0);
}, 0);
}
function fitsInTwoLines(words) {
return measureLineWidth(words) <= MAX_LINE_WIDTH * 1.8;
}
function splitLines(words) {
if (measureLineWidth(words) <= MAX_LINE_WIDTH) {
return [{ words: words, startIndex: 0 }];
}
var bestSplit = Math.ceil(words.length / 2);
var bestDiff = Infinity;
for (var s = 1; s < words.length; s++) {
var w1 = measureLineWidth(words.slice(0, s));
var w2 = measureLineWidth(words.slice(s));
if (w1 <= MAX_LINE_WIDTH && w2 <= MAX_LINE_WIDTH) {
var diff = Math.abs(w1 - w2);
if (diff < bestDiff) { bestDiff = diff; bestSplit = s; }
}
}
var result = [];
var line1 = words.slice(0, bestSplit);
var line2 = words.slice(bestSplit);
if (line1.length) result.push({ words: line1, startIndex: 0 });
if (line2.length) result.push({ words: line2, startIndex: line1.length });
return result;
}
function makeGroups(words) {
var groups = [];
var current = [];
words.forEach(function (word, index) {
var nextWord = words[index + 1];
var testGroup = current.concat([word]);
if (testGroup.length > MAX_WORDS_PER_GROUP || !fitsInTwoLines(testGroup)) {
if (current.length) groups.push(makeGroup(current));
current = [word];
return;
}
current.push(word);
var punctuation = /[,.:!?]$/.test(word.text);
var pause = nextWord ? nextWord.start - word.end : 0;
if (!nextWord || punctuation || pause >= PAUSE_THRESHOLD || current.length >= MAX_WORDS_PER_GROUP) {
groups.push(makeGroup(current));
current = [];
}
});
if (current.length) groups.push(makeGroup(current));
return groups;
}
function makeGroup(words) {
return { words: words.slice(), lines: splitLines(words), start: words[0].start, end: words[words.length - 1].end };
}
function buildCaptions(groups) {
var stage = document.getElementById("caption-stage");
groups.forEach(function (group, groupIndex) {
var groupEl = document.createElement("div");
var copyEl = document.createElement("div");
groupEl.id = "caption-group-" + groupIndex;
groupEl.className = "caption-group";
copyEl.className = "caption-copy";
group.lines.forEach(function (line, lineIndex) {
var lineEl = document.createElement("div");
lineEl.className = "caption-line";
lineEl.id = "caption-group-" + groupIndex + "-line-" + lineIndex;
lineEl.style.fontWeight = lineIndex === 0 ? FONT_WEIGHT_BOLD : FONT_WEIGHT_LIGHT;
line.words.forEach(function (word) {
var wordEl = document.createElement("span");
wordEl.textContent = word.text.toLowerCase();
lineEl.appendChild(wordEl);
});
copyEl.appendChild(lineEl);
});
groupEl.appendChild(copyEl);
stage.appendChild(groupEl);
});
}
var WORDS = normalizeWords(TRANSCRIPT);
var GROUPS = makeGroups(WORDS);
buildCaptions(GROUPS);
window.__timelines = window.__timelines || {};
var tl = gsap.timeline({ paused: true });
var visibleStarts = GROUPS.map(function (group, groupIndex) {
var prev = GROUPS[groupIndex - 1];
var availableGap = prev ? Math.max(0, group.start - prev.end) : ENTRY_DURATION;
var entryLead = Math.min(ENTRY_DURATION, availableGap);
return Math.max(0, group.start - entryLead);
});
var allGroupEls = GROUPS.map(function (_, i) { return document.getElementById("caption-group-" + i); });
GROUPS.forEach(function (group, groupIndex) {
var groupEl = allGroupEls[groupIndex];
var visibleStart = visibleStarts[groupIndex];
var visibleEnd = groupIndex < GROUPS.length - 1 ? visibleStarts[groupIndex + 1] : DURATION;
allGroupEls.forEach(function (otherEl, otherIndex) {
if (otherIndex !== groupIndex) tl.set(otherEl, { opacity: 0 }, visibleStart);
});
tl.set(groupEl, { opacity: 0, scale: 0.85 }, visibleStart);
tl.to(groupEl, { opacity: 1, scale: 1, duration: ENTRY_DURATION, ease: "power3.out", force3D: true }, visibleStart);
if (group.lines.length === 2) {
var line1El = document.getElementById("caption-group-" + groupIndex + "-line-0");
var line2El = document.getElementById("caption-group-" + groupIndex + "-line-1");
var line2FirstWord = group.lines[1].words[0];
var switchTime = line2FirstWord.start;
tl.to(line1El, { fontWeight: FONT_WEIGHT_LIGHT, duration: WEIGHT_TRANSITION, ease: "power2.out" }, switchTime);
tl.to(line2El, { fontWeight: FONT_WEIGHT_BOLD, duration: WEIGHT_TRANSITION, ease: "power2.out" }, switchTime);
}
tl.set(groupEl, { opacity: 0 }, visibleEnd);
});
window.__timelines["weight-shift"] = tl;
</script>
</body>
</html>
@@ -0,0 +1,15 @@
{
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
"name": "caption-weight-shift",
"type": "hyperframes:component",
"title": "Weight Shift",
"description": "Elegant font-weight transition between caption lines",
"tags": ["captions", "caption-style", "minimal", "typography"],
"files": [
{
"path": "caption-weight-shift.html",
"target": "compositions/components/caption-weight-shift.html",
"type": "hyperframes:snippet"
}
]
}
+60
View File
@@ -67,6 +67,66 @@
"name": "vignette",
"type": "hyperframes:component"
},
{
"name": "caption-pill-karaoke",
"type": "hyperframes:component"
},
{
"name": "caption-neon-accent",
"type": "hyperframes:component"
},
{
"name": "caption-weight-shift",
"type": "hyperframes:component"
},
{
"name": "caption-emoji-pop",
"type": "hyperframes:component"
},
{
"name": "caption-editorial-emphasis",
"type": "hyperframes:component"
},
{
"name": "caption-parallax-layers",
"type": "hyperframes:component"
},
{
"name": "caption-glitch-rgb",
"type": "hyperframes:component"
},
{
"name": "caption-typewriter",
"type": "hyperframes:component"
},
{
"name": "caption-matrix-decode",
"type": "hyperframes:component"
},
{
"name": "caption-particle-burst",
"type": "hyperframes:component"
},
{
"name": "caption-texture-lava",
"type": "hyperframes:component"
},
{
"name": "caption-clip-wipe",
"type": "hyperframes:component"
},
{
"name": "caption-kinetic-slam",
"type": "hyperframes:component"
},
{
"name": "caption-gradient-fill",
"type": "hyperframes:component"
},
{
"name": "caption-neon-glow",
"type": "hyperframes:component"
},
{
"name": "instagram-follow",
"type": "hyperframes:block"