mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-02 12:08:50 +00:00
feat(registry): bring back the video-primitive moves (#3169)
Restores the 208 catalog items reverted after their previews 404'd in production, this time on the payload mechanism rather than the .html files that caused the outage. The generator no longer writes a preview document to docs/public. That writer, and the machinery under it, existed only to produce files the docs host discards, so it is gone rather than bypassed. Items now embed the composition itself via a payload, which is what the previous change already does for the items that were already in the catalog. The variables explorer is parked, not restored: it drove its preview through the same unpublished .html path, so it would have shown an empty frame. Items that declare variables get the live player plus the static variables table, and reconnecting the explorer to payloads is a follow-up.
This commit is contained in:
@@ -3,6 +3,8 @@ title: "Beat Freeze Cut"
|
||||
description: "A beat-driven speed ramp, freeze-frame hit, and hard-cut sequence for music-led promos and montages."
|
||||
---
|
||||
|
||||
import { InstallCommand } from "/snippets/install-command.jsx";
|
||||
|
||||
<iframe
|
||||
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
||||
title="beat-freeze-cut preview"
|
||||
@@ -12,9 +14,7 @@ description: "A beat-driven speed ramp, freeze-frame hit, and hard-cut sequence
|
||||
|
||||
## Install
|
||||
|
||||
```bash Terminal
|
||||
npx hyperframes add beat-freeze-cut
|
||||
```
|
||||
<InstallCommand command="npx hyperframes add beat-freeze-cut" />
|
||||
|
||||
That writes one file: `compositions/beat-freeze-cut.html`.
|
||||
|
||||
@@ -44,6 +44,894 @@ Set these CSS variables on the block:
|
||||
- `--bg-color` — Background. Defaults to `#0B0D10`.
|
||||
- `--accent-color` — Accent. Defaults to `#00E5C7`.
|
||||
|
||||
## Source
|
||||
|
||||
<Accordion title={`beat-freeze-cut.html`}>
|
||||
|
||||
```html
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=1920, height=1080" />
|
||||
<title>Beat Freeze Cut</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@500;600;700;800;900&display=block"
|
||||
rel="stylesheet"
|
||||
/>
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<style>
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
html,
|
||||
body {
|
||||
width: 1920px;
|
||||
height: 1080px;
|
||||
margin: 0;
|
||||
overflow: hidden;
|
||||
background: #0b0d10;
|
||||
}
|
||||
|
||||
/*
|
||||
EDIT / STUDIO PARAMS
|
||||
────────────────────
|
||||
Studio color params (CSS custom properties on the root):
|
||||
--bg-color default #0B0D10
|
||||
--accent-color default #00E5C7
|
||||
|
||||
Numeric / string params (beat times, labels, intensity) are NOT in the
|
||||
formal registry params schema today (only color keys are used by
|
||||
existing blocks). Edit the CONFIG object in the script below instead.
|
||||
*/
|
||||
|
||||
#bfc-root {
|
||||
position: relative;
|
||||
width: 1920px;
|
||||
height: 1080px;
|
||||
overflow: hidden;
|
||||
--bg-color: #0b0d10;
|
||||
--accent-color: #00e5c7;
|
||||
--bfc-ink: #f4f6f8;
|
||||
--bfc-muted: #a0a8b0;
|
||||
--bfc-panel: #14181f;
|
||||
--bfc-panel-2: #1a2029;
|
||||
--bfc-line: rgba(255, 255, 255, 0.08);
|
||||
font-family:
|
||||
"Inter",
|
||||
system-ui,
|
||||
-apple-system,
|
||||
sans-serif;
|
||||
color: var(--bfc-ink);
|
||||
}
|
||||
|
||||
#bfc-bg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background:
|
||||
radial-gradient(ellipse 80% 60% at 50% 40%, rgba(0, 229, 199, 0.07) 0%, transparent 55%),
|
||||
radial-gradient(
|
||||
ellipse 50% 40% at 80% 80%,
|
||||
rgba(255, 255, 255, 0.03) 0%,
|
||||
transparent 50%
|
||||
),
|
||||
var(--bg-color);
|
||||
}
|
||||
|
||||
#bfc-grid {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
opacity: 0.22;
|
||||
background-image:
|
||||
linear-gradient(rgba(255, 255, 255, 0.04) 1px, transparent 1px),
|
||||
linear-gradient(90deg, rgba(255, 255, 255, 0.04) 1px, transparent 1px);
|
||||
background-size: 80px 80px;
|
||||
mask-image: radial-gradient(ellipse 70% 60% at 50% 45%, #000 20%, transparent 75%);
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#bfc-stage {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
transform-origin: 50% 48%;
|
||||
will-change: transform, filter;
|
||||
}
|
||||
|
||||
#bfc-shot-a,
|
||||
#bfc-shot-b {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
#bfc-shot-b {
|
||||
visibility: hidden;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* ── Shot A: single product card hero ── */
|
||||
#bfc-a-crop {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
transform-origin: 50% 50%;
|
||||
}
|
||||
|
||||
#bfc-a-card {
|
||||
position: relative;
|
||||
width: 680px;
|
||||
height: 720px;
|
||||
border-radius: 36px;
|
||||
background:
|
||||
linear-gradient(160deg, rgba(255, 255, 255, 0.06) 0%, transparent 40%),
|
||||
linear-gradient(180deg, var(--bfc-panel-2) 0%, var(--bfc-panel) 100%);
|
||||
border: 1px solid var(--bfc-line);
|
||||
box-shadow:
|
||||
0 40px 120px rgba(0, 0, 0, 0.55),
|
||||
0 0 0 1px rgba(0, 229, 199, 0.08),
|
||||
inset 0 1px 0 rgba(255, 255, 255, 0.06);
|
||||
overflow: hidden;
|
||||
transform-origin: 50% 50%;
|
||||
}
|
||||
|
||||
#bfc-a-card-glow {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 28%;
|
||||
width: 420px;
|
||||
height: 420px;
|
||||
margin-left: -210px;
|
||||
margin-top: -210px;
|
||||
border-radius: 50%;
|
||||
background: radial-gradient(circle, rgba(0, 229, 199, 0.28) 0%, transparent 68%);
|
||||
opacity: 0.85;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
#bfc-a-wave {
|
||||
position: absolute;
|
||||
left: 64px;
|
||||
right: 64px;
|
||||
top: 120px;
|
||||
height: 220px;
|
||||
}
|
||||
|
||||
#bfc-a-wave svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: block;
|
||||
}
|
||||
|
||||
#bfc-a-wave-path {
|
||||
fill: none;
|
||||
stroke: var(--accent-color);
|
||||
stroke-width: 4;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
opacity: 0.95;
|
||||
}
|
||||
|
||||
#bfc-a-wave-fill {
|
||||
fill: var(--accent-color);
|
||||
opacity: 0.12;
|
||||
}
|
||||
|
||||
#bfc-a-bars {
|
||||
position: absolute;
|
||||
left: 80px;
|
||||
right: 80px;
|
||||
bottom: 160px;
|
||||
height: 96px;
|
||||
display: flex;
|
||||
align-items: flex-end;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.bfc-bar {
|
||||
flex: 1;
|
||||
border-radius: 6px 6px 2px 2px;
|
||||
background: linear-gradient(180deg, var(--accent-color) 0%, rgba(0, 229, 199, 0.25) 100%);
|
||||
transform-origin: 50% 100%;
|
||||
height: 40%;
|
||||
}
|
||||
|
||||
#bfc-a-meta {
|
||||
position: absolute;
|
||||
left: 64px;
|
||||
right: 64px;
|
||||
bottom: 56px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
#bfc-a-kicker {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.18em;
|
||||
text-transform: uppercase;
|
||||
color: var(--bfc-muted);
|
||||
}
|
||||
|
||||
#bfc-a-pill {
|
||||
font-size: 15px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.08em;
|
||||
text-transform: uppercase;
|
||||
color: #04120f;
|
||||
background: var(--accent-color);
|
||||
padding: 8px 16px;
|
||||
border-radius: 999px;
|
||||
}
|
||||
|
||||
/* ── Shot B: split layout, higher info density ── */
|
||||
#bfc-b-layout {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: grid;
|
||||
grid-template-columns: 1.05fr 0.95fr;
|
||||
gap: 48px;
|
||||
padding: 100px 110px;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#bfc-b-left {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 28px;
|
||||
transform-origin: 0% 50%;
|
||||
}
|
||||
|
||||
#bfc-b-eyebrow {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.22em;
|
||||
text-transform: uppercase;
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
#bfc-b-title {
|
||||
font-size: 128px;
|
||||
font-weight: 900;
|
||||
line-height: 0.92;
|
||||
letter-spacing: -0.04em;
|
||||
text-transform: uppercase;
|
||||
color: var(--bfc-ink);
|
||||
}
|
||||
|
||||
#bfc-b-sub {
|
||||
font-size: 28px;
|
||||
font-weight: 500;
|
||||
color: var(--bfc-muted);
|
||||
max-width: 520px;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
#bfc-b-right {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.bfc-b-card {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 24px;
|
||||
padding: 28px 32px;
|
||||
border-radius: 20px;
|
||||
background: linear-gradient(135deg, var(--bfc-panel-2), var(--bfc-panel));
|
||||
border: 1px solid var(--bfc-line);
|
||||
box-shadow: 0 18px 48px rgba(0, 0, 0, 0.35);
|
||||
}
|
||||
|
||||
.bfc-b-card-label {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.12em;
|
||||
text-transform: uppercase;
|
||||
color: var(--bfc-muted);
|
||||
}
|
||||
|
||||
.bfc-b-card-value {
|
||||
font-size: 42px;
|
||||
font-weight: 800;
|
||||
letter-spacing: -0.03em;
|
||||
color: var(--bfc-ink);
|
||||
}
|
||||
|
||||
.bfc-b-card-value.accent {
|
||||
color: var(--accent-color);
|
||||
}
|
||||
|
||||
#bfc-b-accent-bar {
|
||||
width: 120px;
|
||||
height: 6px;
|
||||
border-radius: 999px;
|
||||
background: var(--accent-color);
|
||||
opacity: 0.9;
|
||||
}
|
||||
|
||||
/* ── Intro / transient typography ── */
|
||||
#bfc-intro {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 72px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
pointer-events: none;
|
||||
z-index: 20;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#bfc-intro-label {
|
||||
font-size: 18px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.28em;
|
||||
text-transform: uppercase;
|
||||
color: var(--bfc-muted);
|
||||
padding: 10px 18px;
|
||||
border: 1px solid var(--bfc-line);
|
||||
border-radius: 999px;
|
||||
background: rgba(11, 13, 16, 0.55);
|
||||
backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
#bfc-hit-type {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
pointer-events: none;
|
||||
z-index: 30;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#bfc-hit-type-text {
|
||||
font-size: 160px;
|
||||
font-weight: 900;
|
||||
letter-spacing: -0.04em;
|
||||
text-transform: uppercase;
|
||||
color: var(--bfc-ink);
|
||||
text-shadow:
|
||||
0 0 40px rgba(0, 229, 199, 0.35),
|
||||
0 8px 40px rgba(0, 0, 0, 0.55);
|
||||
-webkit-text-stroke: 2px rgba(0, 0, 0, 0.35);
|
||||
paint-order: stroke fill;
|
||||
}
|
||||
|
||||
/* ── Freeze frame treatment ── */
|
||||
#bfc-freeze {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
z-index: 25;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
#bfc-freeze-outline {
|
||||
position: absolute;
|
||||
inset: 48px;
|
||||
border: 3px solid var(--accent-color);
|
||||
border-radius: 8px;
|
||||
box-shadow:
|
||||
0 0 0 1px rgba(0, 229, 199, 0.25),
|
||||
inset 0 0 0 1px rgba(0, 229, 199, 0.15),
|
||||
0 0 48px rgba(0, 229, 199, 0.2);
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
#bfc-freeze-flash {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: #ffffff;
|
||||
opacity: 0;
|
||||
mix-blend-mode: screen;
|
||||
}
|
||||
|
||||
#bfc-freeze-contour {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background:
|
||||
linear-gradient(90deg, transparent 0%, rgba(0, 229, 199, 0.08) 48%, transparent 52%),
|
||||
radial-gradient(ellipse 40% 55% at 50% 48%, transparent 40%, rgba(0, 229, 199, 0.18) 100%);
|
||||
opacity: 0;
|
||||
mix-blend-mode: screen;
|
||||
}
|
||||
|
||||
#bfc-freeze-badge {
|
||||
position: absolute;
|
||||
top: 80px;
|
||||
right: 96px;
|
||||
font-size: 16px;
|
||||
font-weight: 800;
|
||||
letter-spacing: 0.2em;
|
||||
text-transform: uppercase;
|
||||
color: #04120f;
|
||||
background: var(--accent-color);
|
||||
padding: 10px 18px;
|
||||
border-radius: 6px;
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
/* ── Hard-cut connector (directional stretch / blur) ── */
|
||||
#bfc-cut {
|
||||
position: absolute;
|
||||
inset: -8% -20%;
|
||||
pointer-events: none;
|
||||
z-index: 40;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
background: linear-gradient(
|
||||
90deg,
|
||||
transparent 0%,
|
||||
rgba(0, 229, 199, 0.12) 35%,
|
||||
rgba(255, 255, 255, 0.55) 50%,
|
||||
rgba(0, 229, 199, 0.12) 65%,
|
||||
transparent 100%
|
||||
);
|
||||
filter: blur(18px);
|
||||
transform-origin: 50% 50%;
|
||||
}
|
||||
|
||||
#bfc-vignette {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
z-index: 15;
|
||||
background: radial-gradient(
|
||||
ellipse 75% 70% at 50% 50%,
|
||||
transparent 45%,
|
||||
rgba(0, 0, 0, 0.55) 100%
|
||||
);
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
#bfc-driver {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
height: 1px;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div
|
||||
id="bfc-root"
|
||||
data-composition-id="beat-freeze-cut"
|
||||
data-start="0"
|
||||
data-duration="6"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
>
|
||||
<div id="bfc-bg" aria-hidden="true"></div>
|
||||
<div id="bfc-grid" aria-hidden="true"></div>
|
||||
|
||||
<div id="bfc-stage">
|
||||
<!-- Shot A -->
|
||||
<div id="bfc-shot-a">
|
||||
<div id="bfc-a-crop" data-layout-allow-overflow>
|
||||
<div id="bfc-a-card" data-layout-allow-overflow>
|
||||
<div id="bfc-a-card-glow" aria-hidden="true"></div>
|
||||
<div id="bfc-a-wave" aria-hidden="true">
|
||||
<svg viewBox="0 0 592 220" preserveAspectRatio="none">
|
||||
<path
|
||||
id="bfc-a-wave-fill"
|
||||
d="M0,160 C40,150 60,90 100,100 C140,110 160,40 200,55 C240,70 260,150 300,140 C340,130 360,30 400,45 C440,60 460,130 500,120 C540,110 560,70 592,80 L592,220 L0,220 Z"
|
||||
/>
|
||||
<path
|
||||
id="bfc-a-wave-path"
|
||||
d="M0,160 C40,150 60,90 100,100 C140,110 160,40 200,55 C240,70 260,150 300,140 C340,130 360,30 400,45 C440,60 460,130 500,120 C540,110 560,70 592,80"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
<div id="bfc-a-bars" aria-hidden="true">
|
||||
<div class="bfc-bar" id="bfc-bar-0" style="height: 34%"></div>
|
||||
<div class="bfc-bar" id="bfc-bar-1" style="height: 58%"></div>
|
||||
<div class="bfc-bar" id="bfc-bar-2" style="height: 42%"></div>
|
||||
<div class="bfc-bar" id="bfc-bar-3" style="height: 78%"></div>
|
||||
<div class="bfc-bar" id="bfc-bar-4" style="height: 52%"></div>
|
||||
<div class="bfc-bar" id="bfc-bar-5" style="height: 88%"></div>
|
||||
<div class="bfc-bar" id="bfc-bar-6" style="height: 46%"></div>
|
||||
<div class="bfc-bar" id="bfc-bar-7" style="height: 64%"></div>
|
||||
<div class="bfc-bar" id="bfc-bar-8" style="height: 36%"></div>
|
||||
<div class="bfc-bar" id="bfc-bar-9" style="height: 72%"></div>
|
||||
<div class="bfc-bar" id="bfc-bar-10" style="height: 48%"></div>
|
||||
<div class="bfc-bar" id="bfc-bar-11" style="height: 92%"></div>
|
||||
</div>
|
||||
<div id="bfc-a-meta">
|
||||
<span id="bfc-a-kicker">MUSIC PROMO</span>
|
||||
<span id="bfc-a-pill">LIVE</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Shot B -->
|
||||
<div id="bfc-shot-b">
|
||||
<div id="bfc-b-layout">
|
||||
<div id="bfc-b-left">
|
||||
<div id="bfc-b-eyebrow">Next shot</div>
|
||||
<div id="bfc-b-title">HARD<br />CUT</div>
|
||||
<div id="bfc-b-accent-bar" aria-hidden="true"></div>
|
||||
<div id="bfc-b-sub">
|
||||
Beat-locked freeze, then cut. Built for music-led promos and montages.
|
||||
</div>
|
||||
</div>
|
||||
<div id="bfc-b-right">
|
||||
<div class="bfc-b-card" id="bfc-b-card-0">
|
||||
<span class="bfc-b-card-label">Ramp</span>
|
||||
<span class="bfc-b-card-value accent">1.35→2.2</span>
|
||||
</div>
|
||||
<div class="bfc-b-card" id="bfc-b-card-1">
|
||||
<span class="bfc-b-card-label">Freeze</span>
|
||||
<span class="bfc-b-card-value">0.8s</span>
|
||||
</div>
|
||||
<div class="bfc-b-card" id="bfc-b-card-2">
|
||||
<span class="bfc-b-card-label">Cut</span>
|
||||
<span class="bfc-b-card-value accent">3.00</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="bfc-vignette" aria-hidden="true"></div>
|
||||
|
||||
<div id="bfc-intro" aria-hidden="true">
|
||||
<span id="bfc-intro-label">ON THE BEAT</span>
|
||||
</div>
|
||||
|
||||
<div id="bfc-hit-type" aria-hidden="true">
|
||||
<span id="bfc-hit-type-text">DROP</span>
|
||||
</div>
|
||||
|
||||
<div id="bfc-freeze" aria-hidden="true">
|
||||
<div id="bfc-freeze-flash"></div>
|
||||
<div id="bfc-freeze-contour"></div>
|
||||
<div id="bfc-freeze-outline"></div>
|
||||
<div id="bfc-freeze-badge">FREEZE</div>
|
||||
</div>
|
||||
|
||||
<div id="bfc-cut" aria-hidden="true"></div>
|
||||
|
||||
<div id="bfc-driver" class="clip" data-start="0" data-duration="6" data-track-index="0"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
window.__timelines = window.__timelines || {};
|
||||
|
||||
// ── EDIT ME (not Studio params — see HTML comment) ──────────────
|
||||
var CONFIG = {
|
||||
primaryLabel: "DROP",
|
||||
secondaryLabel: "ON THE BEAT",
|
||||
beat1: 0.7,
|
||||
beat2: 2.2,
|
||||
freezeTime: 2.2,
|
||||
intensity: 1.0,
|
||||
};
|
||||
// ────────────────────────────────────────────────────────────────
|
||||
|
||||
var DURATION = 6;
|
||||
var intensity = Math.max(0.6, Math.min(1.4, Number(CONFIG.intensity) || 1));
|
||||
var beat1 = Number(CONFIG.beat1) || 0.7;
|
||||
var beat2 = Number(CONFIG.beat2) || 2.2;
|
||||
var freezeAt = Number(CONFIG.freezeTime) || beat2;
|
||||
var cutAt = 3.0;
|
||||
var beat3 = 4.7;
|
||||
var holdAt = 5.2;
|
||||
|
||||
var root = document.getElementById("bfc-root");
|
||||
var stage = document.getElementById("bfc-stage");
|
||||
var shotA = document.getElementById("bfc-shot-a");
|
||||
var shotB = document.getElementById("bfc-shot-b");
|
||||
var aCrop = document.getElementById("bfc-a-crop");
|
||||
var aCard = document.getElementById("bfc-a-card");
|
||||
var intro = document.getElementById("bfc-intro");
|
||||
var introLabel = document.getElementById("bfc-intro-label");
|
||||
var hitType = document.getElementById("bfc-hit-type");
|
||||
var hitText = document.getElementById("bfc-hit-type-text");
|
||||
var freeze = document.getElementById("bfc-freeze");
|
||||
var freezeOutline = document.getElementById("bfc-freeze-outline");
|
||||
var freezeFlash = document.getElementById("bfc-freeze-flash");
|
||||
var freezeContour = document.getElementById("bfc-freeze-contour");
|
||||
var freezeBadge = document.getElementById("bfc-freeze-badge");
|
||||
var cutFx = document.getElementById("bfc-cut");
|
||||
var bLeft = document.getElementById("bfc-b-left");
|
||||
var bCards = [
|
||||
document.getElementById("bfc-b-card-0"),
|
||||
document.getElementById("bfc-b-card-1"),
|
||||
document.getElementById("bfc-b-card-2"),
|
||||
];
|
||||
var bars = [];
|
||||
for (var bi = 0; bi < 12; bi++) {
|
||||
bars.push(document.getElementById("bfc-bar-" + bi));
|
||||
}
|
||||
|
||||
// Apply editable labels
|
||||
if (hitText) hitText.textContent = CONFIG.primaryLabel || "DROP";
|
||||
if (introLabel) introLabel.textContent = CONFIG.secondaryLabel || "ON THE BEAT";
|
||||
|
||||
// Accent color from CSS var → freeze/glow that use hardcoded rgba fallbacks
|
||||
// stay coherent because stroke/fill already use var(--accent-color).
|
||||
|
||||
var tl = gsap.timeline({ paused: true });
|
||||
|
||||
// Initial states (no CSS transform conflicts on animated props)
|
||||
gsap.set(stage, { scale: 1, x: 0, y: 0, filter: "blur(0px) brightness(1)" });
|
||||
gsap.set(aCrop, { scale: 1, x: 0, y: 0 });
|
||||
// Card visible at t=0 so catalog snapshots are never a black frame
|
||||
gsap.set(aCard, { scale: 0.9, y: 20, opacity: 1 });
|
||||
gsap.set(shotA, { opacity: 1, visibility: "visible" });
|
||||
gsap.set(shotB, { opacity: 0, visibility: "hidden" });
|
||||
gsap.set(intro, { opacity: 0, visibility: "hidden", y: -12 });
|
||||
gsap.set(hitType, { opacity: 0, visibility: "hidden", scale: 1.15 });
|
||||
gsap.set(freeze, { opacity: 0, visibility: "hidden" });
|
||||
gsap.set([freezeOutline, freezeFlash, freezeContour, freezeBadge], { opacity: 0 });
|
||||
gsap.set(cutFx, { opacity: 0, visibility: "hidden", scaleX: 0.4, x: -200 });
|
||||
gsap.set(bLeft, { opacity: 0, x: -48 });
|
||||
gsap.set(bCards, { opacity: 0, x: 40 });
|
||||
gsap.set(bars, { scaleY: 0.55 });
|
||||
|
||||
// Full duration anchor (seek-safe length)
|
||||
tl.to({}, { duration: DURATION, ease: "none" }, 0);
|
||||
|
||||
// ── 0.00–0.70: intro push-in + secondary type ──
|
||||
tl.to(aCard, { scale: 1, y: 0, duration: 0.55, ease: "power3.out" }, 0);
|
||||
tl.to(stage, { scale: 1 + 0.04 * intensity, duration: 0.7, ease: "power1.out" }, 0);
|
||||
tl.to(bars, { scaleY: 1, duration: 0.5, ease: "power2.out", stagger: 0.02 }, 0.12);
|
||||
tl.set(intro, { visibility: "visible" }, 0.12);
|
||||
tl.to(intro, { opacity: 1, y: 0, duration: 0.28, ease: "power2.out" }, 0.12);
|
||||
tl.to(intro, { opacity: 0, y: -8, duration: 0.18, ease: "power2.in" }, 0.52);
|
||||
tl.set(intro, { visibility: "hidden", opacity: 0 }, 0.7);
|
||||
|
||||
// ── 0.70–1.35: beat 1 — hard reframe ──
|
||||
tl.to(
|
||||
aCrop,
|
||||
{
|
||||
scale: 1 + 0.18 * intensity,
|
||||
x: -80 * intensity,
|
||||
y: 24 * intensity,
|
||||
duration: 0.22,
|
||||
ease: "power4.out",
|
||||
},
|
||||
beat1,
|
||||
);
|
||||
tl.to(
|
||||
stage,
|
||||
{
|
||||
scale: 1.08 * intensity > 1.2 ? 1.2 : 1 + 0.08 * intensity,
|
||||
duration: 0.28,
|
||||
ease: "expo.out",
|
||||
},
|
||||
beat1,
|
||||
);
|
||||
tl.to(
|
||||
bars,
|
||||
{
|
||||
scaleY: function (i) {
|
||||
// Deterministic bar pattern (no Math.random)
|
||||
var pattern = [0.55, 1.15, 0.7, 1.35, 0.9, 1.4, 0.65, 1.1, 0.5, 1.25, 0.8, 1.45];
|
||||
return pattern[i % pattern.length];
|
||||
},
|
||||
duration: 0.18,
|
||||
ease: "power3.out",
|
||||
stagger: 0.01,
|
||||
},
|
||||
beat1,
|
||||
);
|
||||
|
||||
// ── 1.35–2.20: speed ramp into freeze ──
|
||||
var rampStart = 1.35;
|
||||
var rampDur = Math.max(0.2, freezeAt - rampStart);
|
||||
tl.to(
|
||||
aCrop,
|
||||
{
|
||||
scale: 1.28 * (0.9 + 0.1 * intensity),
|
||||
x: 28 * intensity,
|
||||
y: -12 * intensity,
|
||||
duration: rampDur,
|
||||
ease: "power3.in",
|
||||
},
|
||||
rampStart,
|
||||
);
|
||||
tl.to(
|
||||
stage,
|
||||
{
|
||||
scale: 1.12,
|
||||
filter: "blur(0px) brightness(1.08)",
|
||||
duration: rampDur,
|
||||
ease: "power2.in",
|
||||
},
|
||||
rampStart,
|
||||
);
|
||||
tl.to(
|
||||
bars,
|
||||
{
|
||||
scaleY: 1.55,
|
||||
duration: rampDur * 0.85,
|
||||
ease: "power2.in",
|
||||
stagger: { each: 0.012, from: "center" },
|
||||
},
|
||||
rampStart,
|
||||
);
|
||||
|
||||
// ── 2.20–3.00: freeze hit (snap-on for seek/snapshot stability) ──
|
||||
tl.set(freeze, { visibility: "visible", opacity: 1 }, freezeAt);
|
||||
tl.set(freezeOutline, { opacity: 1 }, freezeAt);
|
||||
tl.set(freezeContour, { opacity: 0.85 }, freezeAt);
|
||||
tl.set(freezeBadge, { opacity: 1 }, freezeAt);
|
||||
tl.set(hitType, { visibility: "visible", opacity: 1, scale: 1 }, freezeAt);
|
||||
// Hold crop/stage where ramp ended
|
||||
tl.to(aCrop, { duration: 0.01, ease: "none" }, freezeAt);
|
||||
tl.to(
|
||||
stage,
|
||||
{
|
||||
scale: 1.12,
|
||||
filter: "blur(0px) brightness(1.15)",
|
||||
duration: 0.05,
|
||||
ease: "power4.out",
|
||||
},
|
||||
freezeAt,
|
||||
);
|
||||
// Single exposure flash (not a strobe sequence)
|
||||
tl.fromTo(
|
||||
freezeFlash,
|
||||
{ opacity: 0.55 * intensity },
|
||||
{ opacity: 0, duration: 0.28, ease: "power2.out" },
|
||||
freezeAt,
|
||||
);
|
||||
tl.to(
|
||||
hitType,
|
||||
{ opacity: 0, scale: 0.96, duration: 0.2, ease: "power2.in" },
|
||||
freezeAt + 0.55,
|
||||
);
|
||||
tl.set(hitType, { visibility: "hidden", opacity: 0 }, freezeAt + 0.78);
|
||||
// Soft settle of freeze dressing before cut
|
||||
tl.to(
|
||||
[freezeOutline, freezeContour, freezeBadge],
|
||||
{ opacity: 0.75, duration: 0.35, ease: "sine.out" },
|
||||
freezeAt + 0.35,
|
||||
);
|
||||
|
||||
// ── 3.00–3.35: hard cut connector ──
|
||||
tl.set(cutFx, { visibility: "visible" }, cutAt);
|
||||
tl.fromTo(
|
||||
cutFx,
|
||||
{ opacity: 0, scaleX: 0.35, x: -280 },
|
||||
{
|
||||
opacity: 0.95,
|
||||
scaleX: 1.6,
|
||||
x: 120,
|
||||
duration: 0.12,
|
||||
ease: "power4.in",
|
||||
},
|
||||
cutAt,
|
||||
);
|
||||
tl.to(
|
||||
stage,
|
||||
{
|
||||
filter: "blur(14px) brightness(1.35)",
|
||||
scaleX: 1.35,
|
||||
scaleY: 0.92,
|
||||
duration: 0.12,
|
||||
ease: "power3.in",
|
||||
},
|
||||
cutAt,
|
||||
);
|
||||
// Switch shots mid-cut — shot B content lands immediately (no black gap)
|
||||
var switchAt = cutAt + 0.14;
|
||||
tl.set(shotA, { opacity: 0, visibility: "hidden" }, switchAt);
|
||||
tl.set(shotB, { opacity: 1, visibility: "visible" }, switchAt);
|
||||
tl.set(hitType, { opacity: 0, visibility: "hidden" }, switchAt);
|
||||
tl.set(
|
||||
[freeze, freezeOutline, freezeFlash, freezeContour, freezeBadge],
|
||||
{ opacity: 0, visibility: "hidden" },
|
||||
switchAt,
|
||||
);
|
||||
tl.set(freeze, { visibility: "hidden" }, switchAt);
|
||||
// Shot B content visible on switch (offset settles after)
|
||||
tl.set(bLeft, { opacity: 1, x: -24 }, switchAt);
|
||||
tl.set(bCards, { opacity: 1, x: 24 }, switchAt);
|
||||
tl.to(
|
||||
stage,
|
||||
{
|
||||
filter: "blur(0px) brightness(1)",
|
||||
scale: 1,
|
||||
scaleX: 1,
|
||||
scaleY: 1,
|
||||
x: 0,
|
||||
y: 0,
|
||||
duration: 0.18,
|
||||
ease: "power3.out",
|
||||
},
|
||||
switchAt,
|
||||
);
|
||||
tl.to(cutFx, { opacity: 0, x: 360, duration: 0.16, ease: "power2.out" }, cutAt + 0.16);
|
||||
tl.set(cutFx, { visibility: "hidden", opacity: 0 }, cutAt + 0.34);
|
||||
|
||||
// ── after switch: shot B settle (no scale overlap with recover) ──
|
||||
tl.to(bLeft, { x: 0, duration: 0.4, ease: "power3.out" }, switchAt);
|
||||
tl.to(
|
||||
bCards,
|
||||
{
|
||||
x: 0,
|
||||
duration: 0.38,
|
||||
ease: "power3.out",
|
||||
stagger: 0.06,
|
||||
},
|
||||
switchAt + 0.04,
|
||||
);
|
||||
tl.to(stage, { scale: 1.02, duration: 1.17, ease: "sine.out" }, switchAt + 0.18);
|
||||
|
||||
// ── 4.70–5.20: final beat punch-in ──
|
||||
tl.to(
|
||||
stage,
|
||||
{
|
||||
scale: 1.08 * (0.9 + 0.1 * intensity),
|
||||
duration: 0.14,
|
||||
ease: "power4.out",
|
||||
},
|
||||
beat3,
|
||||
);
|
||||
tl.to(
|
||||
bCards[1],
|
||||
{
|
||||
scale: 1.04,
|
||||
duration: 0.12,
|
||||
ease: "power3.out",
|
||||
},
|
||||
beat3,
|
||||
);
|
||||
tl.to(
|
||||
bCards[1],
|
||||
{
|
||||
scale: 1,
|
||||
duration: 0.22,
|
||||
ease: "power2.out",
|
||||
},
|
||||
beat3 + 0.14,
|
||||
);
|
||||
// Micro freeze feel: brief hold of scale
|
||||
tl.to(
|
||||
stage,
|
||||
{ scale: 1.08 * (0.9 + 0.1 * intensity), duration: 0.2, ease: "none" },
|
||||
beat3 + 0.14,
|
||||
);
|
||||
|
||||
// ── 5.20–6.00: hold readable end card (≥0.8s) ──
|
||||
tl.to(stage, { scale: 1.03, duration: 0.35, ease: "power2.out" }, holdAt);
|
||||
// Ensure transient layers stay dead through end
|
||||
tl.set(intro, { opacity: 0, visibility: "hidden" }, holdAt);
|
||||
tl.set(hitType, { opacity: 0, visibility: "hidden" }, holdAt);
|
||||
tl.set(freeze, { opacity: 0, visibility: "hidden" }, holdAt);
|
||||
tl.set(cutFx, { opacity: 0, visibility: "hidden" }, holdAt);
|
||||
|
||||
window.__timelines["beat-freeze-cut"] = tl;
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
</Accordion>
|
||||
|
||||
{/* hf:generated-footer */}
|
||||
|
||||
Tagged `transition` `music` `beat-driven` `freeze-frame`.
|
||||
|
||||
Reference in New Issue
Block a user