mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-02 12:08:50 +00:00
feat(registry): mk / yt / hw theme families — 29 items
Adds three themed registry families contributed by @jbernard077: - mk-* minimal presentation (7 blocks, 3 components) - yt-* retro-broadcast creator (5 blocks, 4 components) - hw-* hand-drawn scribble (6 blocks, 5 components) Consolidates PRs #1933, #1992, #1993 and #1994, cuts 5 of the original 34 items, and fixes the defects that CI never got to report. Cut (5), each covered by something we already ship or trivial to inline: - yt-doc-lower-third — would be the 12th name/role lower third next to the 10 lt-* variants and lower-third-bild - mk-logo-sting — logo-outro exists, and yt-logo-intro is the richer version - mk-cta-button, mk-pill-callout, yt-avatar-pip — a styled button, a styled chip and a masked circle Fixes: - hw-path-text rendered as a solid black blob. #hw-pt-path is the textPath carrier and had no rule, so it inherited SVG's default black fill while only #hw-pt-guide set fill:none. - mk-line-graph value labels collided with their own dots and each other. Series after the first now label downward, and the offset moved off a CSS transform because the entrance tween animates y and GSAP rewrote it. - Added demo.html for the 11 components and the 3 transparent-root blocks. Without it generate-catalog-previews.ts skips components entirely, so the catalog-previews job failed for every component in the original PRs. - Tagged hw-title, hw-pipeline and hw-path-text as overlays. Also registers all catalog pages in docs.json, which the original PRs left orphaned, and picks up 4 pre-existing blocks that had no catalog page (camcorder-hud, editorial-flash-overlay, freeze-frame-dressing, organic-light-leak-overlay) because the regenerated nav now links them. Every item was verified by rendering it and looking at the frames. Co-authored-by: jbernard077 <jbernard077@users.noreply.github.com>
This commit is contained in:
co-authored by
jbernard077
parent
58ef6aca67
commit
f252ec6d99
@@ -0,0 +1,44 @@
|
||||
---
|
||||
title: "Camcorder HUD"
|
||||
description: "Responsive REC, battery, editable date placeholder, and timeline-driven counter overlay for creator-camera and camcorder treatments"
|
||||
---
|
||||
|
||||
# Camcorder HUD
|
||||
|
||||
Responsive REC, battery, editable date placeholder, and timeline-driven counter overlay for creator-camera and camcorder treatments
|
||||
|
||||
`camcorder` `camera` `recording` `hud` `overlay` `creator` `media-treatment-overlay`
|
||||
|
||||
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/camcorder-hud.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/camcorder-hud.png" autoPlay muted loop playsInline />
|
||||
|
||||
## Install
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```bash Terminal
|
||||
npx hyperframes add camcorder-hud
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## Details
|
||||
|
||||
| Property | Value |
|
||||
| --- | --- |
|
||||
| Type | Block |
|
||||
| Dimensions | 1920×1080 |
|
||||
| Duration | 4s |
|
||||
|
||||
## Files
|
||||
|
||||
| File | Target | Type |
|
||||
| --- | --- | --- |
|
||||
| `camcorder-hud.html` | `compositions/camcorder-hud.html` | hyperframes:composition |
|
||||
|
||||
## Usage
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
|
||||
```html
|
||||
<div data-composition-id="camcorder-hud" data-composition-src="compositions/camcorder-hud.html" data-start="0" data-duration="4" data-track-index="1" data-width="1920" data-height="1080"></div>
|
||||
```
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
title: "Editorial Flash Overlay"
|
||||
description: "Finite neutral-warm light layers for a seek-safe camera-flash cut or social reveal"
|
||||
---
|
||||
|
||||
# Editorial Flash Overlay
|
||||
|
||||
Finite neutral-warm light layers for a seek-safe camera-flash cut or social reveal
|
||||
|
||||
`flash` `editorial` `social` `light` `transition` `overlay` `media-treatment-overlay`
|
||||
|
||||
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/editorial-flash-overlay.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/editorial-flash-overlay.png" autoPlay muted loop playsInline />
|
||||
|
||||
## Install
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```bash Terminal
|
||||
npx hyperframes add editorial-flash-overlay
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## Details
|
||||
|
||||
| Property | Value |
|
||||
| --- | --- |
|
||||
| Type | Block |
|
||||
| Dimensions | 1920×1080 |
|
||||
| Duration | 4s |
|
||||
|
||||
## Files
|
||||
|
||||
| File | Target | Type |
|
||||
| --- | --- | --- |
|
||||
| `editorial-flash-overlay.html` | `compositions/editorial-flash-overlay.html` | hyperframes:composition |
|
||||
|
||||
## Usage
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
|
||||
```html
|
||||
<div data-composition-id="editorial-flash-overlay" data-composition-src="compositions/editorial-flash-overlay.html" data-start="0" data-duration="4" data-track-index="1" data-width="1920" data-height="1080"></div>
|
||||
```
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
title: "Freeze-Frame Dressing"
|
||||
description: "Timeline-driven paper, tape, and flash dressing for a freeze-frame or background-removed subject"
|
||||
---
|
||||
|
||||
# Freeze-Frame Dressing
|
||||
|
||||
Timeline-driven paper, tape, and flash dressing for a freeze-frame or background-removed subject
|
||||
|
||||
`freeze-frame` `cutout` `scrapbook` `paper` `social` `overlay` `media-treatment-overlay`
|
||||
|
||||
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/freeze-frame-dressing.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/freeze-frame-dressing.png" autoPlay muted loop playsInline />
|
||||
|
||||
## Install
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```bash Terminal
|
||||
npx hyperframes add freeze-frame-dressing
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## Details
|
||||
|
||||
| Property | Value |
|
||||
| --- | --- |
|
||||
| Type | Block |
|
||||
| Dimensions | 1920×1080 |
|
||||
| Duration | 4s |
|
||||
|
||||
## Files
|
||||
|
||||
| File | Target | Type |
|
||||
| --- | --- | --- |
|
||||
| `freeze-frame-dressing.html` | `compositions/freeze-frame-dressing.html` | hyperframes:composition |
|
||||
|
||||
## Usage
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
|
||||
```html
|
||||
<div data-composition-id="freeze-frame-dressing" data-composition-src="compositions/freeze-frame-dressing.html" data-start="0" data-duration="4" data-track-index="1" data-width="1920" data-height="1080"></div>
|
||||
```
|
||||
@@ -34,6 +34,7 @@ npx hyperframes add hw-frame
|
||||
| File | Target | Type |
|
||||
| --- | --- | --- |
|
||||
| `hw-frame.html` | `compositions/hw-frame.html` | hyperframes:composition |
|
||||
| `assets/fonts/Caveat-700-latin.woff2` | `assets/fonts/Caveat-700-latin.woff2` | hyperframes:asset |
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ description: "Handwriting flowing along any SVG path with a per-character reveal
|
||||
|
||||
Handwriting flowing along any SVG path with a per-character reveal; the path is the only geometry input
|
||||
|
||||
`handwritten` `typography` `path`
|
||||
`handwritten` `typography` `path` `overlay`
|
||||
|
||||
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/hw-path-text.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/hw-path-text.png" autoPlay muted loop playsInline />
|
||||
|
||||
@@ -34,6 +34,7 @@ npx hyperframes add hw-path-text
|
||||
| File | Target | Type |
|
||||
| --- | --- | --- |
|
||||
| `hw-path-text.html` | `compositions/hw-path-text.html` | hyperframes:composition |
|
||||
| `assets/fonts/Caveat-700-latin.woff2` | `assets/fonts/Caveat-700-latin.woff2` | hyperframes:asset |
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ description: "Wobbled boxes with handwritten labels joined by curved connectors,
|
||||
|
||||
Wobbled boxes with handwritten labels joined by curved connectors, drawing on in sequence from a node list
|
||||
|
||||
`handwritten` `diagram` `data-viz`
|
||||
`handwritten` `diagram` `data-viz` `overlay`
|
||||
|
||||
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/hw-pipeline.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/hw-pipeline.png" autoPlay muted loop playsInline />
|
||||
|
||||
@@ -34,6 +34,7 @@ npx hyperframes add hw-pipeline
|
||||
| File | Target | Type |
|
||||
| --- | --- | --- |
|
||||
| `hw-pipeline.html` | `compositions/hw-pipeline.html` | hyperframes:composition |
|
||||
| `assets/fonts/Caveat-700-latin.woff2` | `assets/fonts/Caveat-700-latin.woff2` | hyperframes:asset |
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ npx hyperframes add hw-text-cloud
|
||||
| File | Target | Type |
|
||||
| --- | --- | --- |
|
||||
| `hw-text-cloud.html` | `compositions/hw-text-cloud.html` | hyperframes:composition |
|
||||
| `assets/fonts/Caveat-700-latin.woff2` | `assets/fonts/Caveat-700-latin.woff2` | hyperframes:asset |
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ description: "Handwritten display text with a scalar-driven word-by-word highlig
|
||||
|
||||
Handwritten display text with a scalar-driven word-by-word highlight sweep and a seeded squiggle underline sized to the text; boils
|
||||
|
||||
`handwritten` `typography` `title-card`
|
||||
`handwritten` `typography` `title-card` `overlay`
|
||||
|
||||
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/hw-title.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/hw-title.png" autoPlay muted loop playsInline />
|
||||
|
||||
@@ -34,6 +34,7 @@ npx hyperframes add hw-title
|
||||
| File | Target | Type |
|
||||
| --- | --- | --- |
|
||||
| `hw-title.html` | `compositions/hw-title.html` | hyperframes:composition |
|
||||
| `assets/fonts/Caveat-700-latin.woff2` | `assets/fonts/Caveat-700-latin.woff2` | hyperframes:asset |
|
||||
|
||||
## Usage
|
||||
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
---
|
||||
title: "Outline Logo Sting"
|
||||
description: "A hairline rounded-square outline draws on and the logo lands inside — image or text wordmark in the same shell"
|
||||
---
|
||||
|
||||
# Outline Logo Sting
|
||||
|
||||
A hairline rounded-square outline draws on and the logo lands inside — image or text wordmark in the same shell
|
||||
|
||||
`logo` `sting` `minimal`
|
||||
|
||||
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/mk-logo-sting.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/mk-logo-sting.png" autoPlay muted loop playsInline />
|
||||
|
||||
## Install
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```bash Terminal
|
||||
npx hyperframes add mk-logo-sting
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## Details
|
||||
|
||||
| Property | Value |
|
||||
| --- | --- |
|
||||
| Type | Block |
|
||||
| Dimensions | 1920×1080 |
|
||||
| Duration | 3.5s |
|
||||
|
||||
## Files
|
||||
|
||||
| File | Target | Type |
|
||||
| --- | --- | --- |
|
||||
| `mk-logo-sting.html` | `compositions/mk-logo-sting.html` | hyperframes:composition |
|
||||
|
||||
## Usage
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
|
||||
```html
|
||||
<div data-composition-id="mk-logo-sting" data-composition-src="compositions/mk-logo-sting.html" data-start="0" data-duration="3.5" data-track-index="1" data-width="1920" data-height="1080"></div>
|
||||
```
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
title: "Organic Light Leak Overlay"
|
||||
description: "Finite CSS light-leak overlay for memory beats and motivated transitions"
|
||||
---
|
||||
|
||||
# Organic Light Leak Overlay
|
||||
|
||||
Finite CSS light-leak overlay for memory beats and motivated transitions
|
||||
|
||||
`light-leak` `film` `memory` `transition` `overlay` `media-treatment-overlay`
|
||||
|
||||
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/organic-light-leak-overlay.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/organic-light-leak-overlay.png" autoPlay muted loop playsInline />
|
||||
|
||||
## Install
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```bash Terminal
|
||||
npx hyperframes add organic-light-leak-overlay
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## Details
|
||||
|
||||
| Property | Value |
|
||||
| --- | --- |
|
||||
| Type | Block |
|
||||
| Dimensions | 1920×1080 |
|
||||
| Duration | 4s |
|
||||
|
||||
## Files
|
||||
|
||||
| File | Target | Type |
|
||||
| --- | --- | --- |
|
||||
| `organic-light-leak-overlay.html` | `compositions/organic-light-leak-overlay.html` | hyperframes:composition |
|
||||
|
||||
## Usage
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
|
||||
```html
|
||||
<div data-composition-id="organic-light-leak-overlay" data-composition-src="compositions/organic-light-leak-overlay.html" data-start="0" data-duration="4" data-track-index="1" data-width="1920" data-height="1080"></div>
|
||||
```
|
||||
@@ -1,44 +0,0 @@
|
||||
---
|
||||
title: "Documentary Lower Third"
|
||||
description: "Name and role lower third: accent bar draws down, text slides in, hairline rule grows; overlay and light schemes"
|
||||
---
|
||||
|
||||
# Documentary Lower Third
|
||||
|
||||
Name and role lower third: accent bar draws down, text slides in, hairline rule grows; overlay and light schemes
|
||||
|
||||
`lower-third` `title` `creator`
|
||||
|
||||
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/yt-doc-lower-third.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/yt-doc-lower-third.png" autoPlay muted loop playsInline />
|
||||
|
||||
## Install
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```bash Terminal
|
||||
npx hyperframes add yt-doc-lower-third
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## Details
|
||||
|
||||
| Property | Value |
|
||||
| --- | --- |
|
||||
| Type | Block |
|
||||
| Dimensions | 1920×1080 |
|
||||
| Duration | 6s |
|
||||
|
||||
## Files
|
||||
|
||||
| File | Target | Type |
|
||||
| --- | --- | --- |
|
||||
| `yt-doc-lower-third.html` | `compositions/yt-doc-lower-third.html` | hyperframes:composition |
|
||||
|
||||
## Usage
|
||||
|
||||
After installing, add the block to your host composition:
|
||||
|
||||
```html
|
||||
<div data-composition-id="yt-doc-lower-third" data-composition-src="compositions/yt-doc-lower-third.html" data-start="0" data-duration="6" data-track-index="1" data-width="1920" data-height="1080"></div>
|
||||
```
|
||||
@@ -32,7 +32,8 @@ npx hyperframes add hw-arrow
|
||||
| File | Target | Type |
|
||||
| --- | --- | --- |
|
||||
| `hw-arrow.html` | `compositions/components/hw-arrow.html` | hyperframes:snippet |
|
||||
| `assets/fonts/Caveat-700-latin.woff2` | `assets/fonts/Caveat-700-latin.woff2` | hyperframes:asset |
|
||||
|
||||
## Usage
|
||||
|
||||
Read the installed file and copy its CSS, markup pattern, and helper functions into your host composition — the parent timeline owns all timing. The file's header comment documents the wiring calls.
|
||||
Open `compositions/components/hw-arrow.html` and paste its contents into your composition. See the comment header in the file for detailed instructions.
|
||||
|
||||
@@ -32,7 +32,8 @@ npx hyperframes add hw-boil
|
||||
| File | Target | Type |
|
||||
| --- | --- | --- |
|
||||
| `hw-boil.html` | `compositions/components/hw-boil.html` | hyperframes:snippet |
|
||||
| `assets/fonts/Caveat-700-latin.woff2` | `assets/fonts/Caveat-700-latin.woff2` | hyperframes:asset |
|
||||
|
||||
## Usage
|
||||
|
||||
Read the installed file and copy its CSS, markup pattern, and helper functions into your host composition — the parent timeline owns all timing. The file's header comment documents the wiring calls.
|
||||
Open `compositions/components/hw-boil.html` and paste its contents into your composition. See the comment header in the file for detailed instructions.
|
||||
|
||||
@@ -32,7 +32,8 @@ npx hyperframes add hw-box-label
|
||||
| File | Target | Type |
|
||||
| --- | --- | --- |
|
||||
| `hw-box-label.html` | `compositions/components/hw-box-label.html` | hyperframes:snippet |
|
||||
| `assets/fonts/Caveat-700-latin.woff2` | `assets/fonts/Caveat-700-latin.woff2` | hyperframes:asset |
|
||||
|
||||
## Usage
|
||||
|
||||
Read the installed file and copy its CSS, markup pattern, and helper functions into your host composition — the parent timeline owns all timing. The file's header comment documents the wiring calls.
|
||||
Open `compositions/components/hw-box-label.html` and paste its contents into your composition. See the comment header in the file for detailed instructions.
|
||||
|
||||
@@ -32,7 +32,8 @@ npx hyperframes add hw-callout-circle
|
||||
| File | Target | Type |
|
||||
| --- | --- | --- |
|
||||
| `hw-callout-circle.html` | `compositions/components/hw-callout-circle.html` | hyperframes:snippet |
|
||||
| `assets/fonts/Caveat-700-latin.woff2` | `assets/fonts/Caveat-700-latin.woff2` | hyperframes:asset |
|
||||
|
||||
## Usage
|
||||
|
||||
Read the installed file and copy its CSS, markup pattern, and helper functions into your host composition — the parent timeline owns all timing. The file's header comment documents the wiring calls.
|
||||
Open `compositions/components/hw-callout-circle.html` and paste its contents into your composition. See the comment header in the file for detailed instructions.
|
||||
|
||||
@@ -32,7 +32,8 @@ npx hyperframes add hw-underline
|
||||
| File | Target | Type |
|
||||
| --- | --- | --- |
|
||||
| `hw-underline.html` | `compositions/components/hw-underline.html` | hyperframes:snippet |
|
||||
| `assets/fonts/Caveat-700-latin.woff2` | `assets/fonts/Caveat-700-latin.woff2` | hyperframes:asset |
|
||||
|
||||
## Usage
|
||||
|
||||
Read the installed file and copy its CSS, markup pattern, and helper functions into your host composition — the parent timeline owns all timing. The file's header comment documents the wiring calls.
|
||||
Open `compositions/components/hw-underline.html` and paste its contents into your composition. See the comment header in the file for detailed instructions.
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
---
|
||||
title: "Pill CTA Button"
|
||||
description: "Rounded call-to-action button with arrow, optional outline frame with shadow, frosted variant, and an arrow-nudge helper"
|
||||
---
|
||||
|
||||
# Pill CTA Button
|
||||
|
||||
Rounded call-to-action button with arrow, optional outline frame with shadow, frosted variant, and an arrow-nudge helper
|
||||
|
||||
`cta` `button` `minimal` `overlay`
|
||||
|
||||
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/components/mk-cta-button.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/components/mk-cta-button.png" autoPlay muted loop playsInline />
|
||||
|
||||
## Install
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```bash Terminal
|
||||
npx hyperframes add mk-cta-button
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## Details
|
||||
|
||||
| Property | Value |
|
||||
| --- | --- |
|
||||
| Type | Component |
|
||||
|
||||
## Files
|
||||
|
||||
| File | Target | Type |
|
||||
| --- | --- | --- |
|
||||
| `mk-cta-button.html` | `compositions/components/mk-cta-button.html` | hyperframes:snippet |
|
||||
|
||||
## Usage
|
||||
|
||||
Read the installed file and copy its CSS, markup pattern, and helper functions into your host composition — the parent timeline owns all timing. The file's header comment documents the wiring calls.
|
||||
@@ -35,4 +35,4 @@ npx hyperframes add mk-emphasis-type
|
||||
|
||||
## Usage
|
||||
|
||||
Read the installed file and copy its CSS, markup pattern, and helper functions into your host composition — the parent timeline owns all timing. The file's header comment documents the wiring calls.
|
||||
Open `compositions/components/mk-emphasis-type.html` and paste its contents into your composition. See the comment header in the file for detailed instructions.
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
---
|
||||
title: "Capsule Callout Chip"
|
||||
description: "Rounded capsule chip for handles, tags, and URLs with solid, paper, and frosted-glass variants; slide in/out helpers for the host timeline"
|
||||
---
|
||||
|
||||
# Capsule Callout Chip
|
||||
|
||||
Rounded capsule chip for handles, tags, and URLs with solid, paper, and frosted-glass variants; slide in/out helpers for the host timeline
|
||||
|
||||
`callout` `social` `minimal` `overlay`
|
||||
|
||||
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/components/mk-pill-callout.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/components/mk-pill-callout.png" autoPlay muted loop playsInline />
|
||||
|
||||
## Install
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```bash Terminal
|
||||
npx hyperframes add mk-pill-callout
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## Details
|
||||
|
||||
| Property | Value |
|
||||
| --- | --- |
|
||||
| Type | Component |
|
||||
|
||||
## Files
|
||||
|
||||
| File | Target | Type |
|
||||
| --- | --- | --- |
|
||||
| `mk-pill-callout.html` | `compositions/components/mk-pill-callout.html` | hyperframes:snippet |
|
||||
|
||||
## Usage
|
||||
|
||||
Read the installed file and copy its CSS, markup pattern, and helper functions into your host composition — the parent timeline owns all timing. The file's header comment documents the wiring calls.
|
||||
@@ -35,4 +35,4 @@ npx hyperframes add mk-usage-arc
|
||||
|
||||
## Usage
|
||||
|
||||
Read the installed file and copy its CSS, markup pattern, and helper functions into your host composition — the parent timeline owns all timing. The file's header comment documents the wiring calls.
|
||||
Open `compositions/components/mk-usage-arc.html` and paste its contents into your composition. See the comment header in the file for detailed instructions.
|
||||
|
||||
@@ -1,38 +0,0 @@
|
||||
---
|
||||
title: "Circle PiP Window"
|
||||
description: "Circular or rounded-rect masked picture-in-picture window with ring and shadow for a second video or image; pop in/out helpers"
|
||||
---
|
||||
|
||||
# Circle PiP Window
|
||||
|
||||
Circular or rounded-rect masked picture-in-picture window with ring and shadow for a second video or image; pop in/out helpers
|
||||
|
||||
`pip` `overlay` `creator`
|
||||
|
||||
<video className="w-full aspect-video rounded-xl object-cover bg-zinc-100 dark:bg-zinc-800" src="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/components/yt-avatar-pip.mp4" poster="https://static.heygen.ai/hyperframes-oss/docs/images/catalog/components/yt-avatar-pip.png" autoPlay muted loop playsInline />
|
||||
|
||||
## Install
|
||||
|
||||
<CodeGroup>
|
||||
|
||||
```bash Terminal
|
||||
npx hyperframes add yt-avatar-pip
|
||||
```
|
||||
|
||||
</CodeGroup>
|
||||
|
||||
## Details
|
||||
|
||||
| Property | Value |
|
||||
| --- | --- |
|
||||
| Type | Component |
|
||||
|
||||
## Files
|
||||
|
||||
| File | Target | Type |
|
||||
| --- | --- | --- |
|
||||
| `yt-avatar-pip.html` | `compositions/components/yt-avatar-pip.html` | hyperframes:snippet |
|
||||
|
||||
## Usage
|
||||
|
||||
Read the installed file and copy its CSS, markup pattern, and helper functions into your host composition — the parent timeline owns all timing. The file's header comment documents the wiring calls.
|
||||
@@ -35,4 +35,4 @@ npx hyperframes add yt-camera-move
|
||||
|
||||
## Usage
|
||||
|
||||
Read the installed file and copy its CSS, markup pattern, and helper functions into your host composition — the parent timeline owns all timing. The file's header comment documents the wiring calls.
|
||||
Open `compositions/components/yt-camera-move.html` and paste its contents into your composition. See the comment header in the file for detailed instructions.
|
||||
|
||||
@@ -35,4 +35,4 @@ npx hyperframes add yt-circle-pointer
|
||||
|
||||
## Usage
|
||||
|
||||
Read the installed file and copy its CSS, markup pattern, and helper functions into your host composition — the parent timeline owns all timing. The file's header comment documents the wiring calls.
|
||||
Open `compositions/components/yt-circle-pointer.html` and paste its contents into your composition. See the comment header in the file for detailed instructions.
|
||||
|
||||
@@ -35,4 +35,4 @@ npx hyperframes add yt-feather-highlight
|
||||
|
||||
## Usage
|
||||
|
||||
Read the installed file and copy its CSS, markup pattern, and helper functions into your host composition — the parent timeline owns all timing. The file's header comment documents the wiring calls.
|
||||
Open `compositions/components/yt-feather-highlight.html` and paste its contents into your composition. See the comment header in the file for detailed instructions.
|
||||
|
||||
@@ -35,4 +35,4 @@ npx hyperframes add yt-screen-warp
|
||||
|
||||
## Usage
|
||||
|
||||
Read the installed file and copy its CSS, markup pattern, and helper functions into your host composition — the parent timeline owns all timing. The file's header comment documents the wiring calls.
|
||||
Open `compositions/components/yt-screen-warp.html` and paste its contents into your composition. See the comment header in the file for detailed instructions.
|
||||
|
||||
+80
-56
@@ -200,14 +200,14 @@
|
||||
"group": "Code Animations",
|
||||
"pages": [
|
||||
"catalog/blocks/code-3d-extrude",
|
||||
"catalog/blocks/code-shader-dissolve",
|
||||
"catalog/blocks/code-particle-assemble",
|
||||
"catalog/blocks/code-morph",
|
||||
"catalog/blocks/code-snippet-flight",
|
||||
"catalog/blocks/code-typing",
|
||||
"catalog/blocks/code-diff",
|
||||
"catalog/blocks/code-highlight",
|
||||
"catalog/blocks/code-scroll"
|
||||
"catalog/blocks/code-morph",
|
||||
"catalog/blocks/code-particle-assemble",
|
||||
"catalog/blocks/code-scroll",
|
||||
"catalog/blocks/code-shader-dissolve",
|
||||
"catalog/blocks/code-snippet-flight",
|
||||
"catalog/blocks/code-typing"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -227,7 +227,8 @@
|
||||
"catalog/components/caption-particle-burst",
|
||||
"catalog/components/caption-pill-karaoke",
|
||||
"catalog/components/caption-texture",
|
||||
"catalog/components/caption-weight-shift"
|
||||
"catalog/components/caption-weight-shift",
|
||||
"catalog/blocks/mk-callout-highlight"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -251,12 +252,15 @@
|
||||
{
|
||||
"group": "Social Overlays",
|
||||
"pages": [
|
||||
"catalog/blocks/editorial-flash-overlay",
|
||||
"catalog/blocks/freeze-frame-dressing",
|
||||
"catalog/blocks/instagram-follow",
|
||||
"catalog/blocks/macos-notification",
|
||||
"catalog/blocks/reddit-post",
|
||||
"catalog/blocks/spotify-card",
|
||||
"catalog/blocks/tiktok-follow",
|
||||
"catalog/blocks/x-post",
|
||||
"catalog/blocks/yt-comment-card",
|
||||
"catalog/blocks/yt-lower-third"
|
||||
]
|
||||
},
|
||||
@@ -299,6 +303,9 @@
|
||||
{
|
||||
"group": "CSS Transitions",
|
||||
"pages": [
|
||||
"catalog/blocks/hw-scribble-transition",
|
||||
"catalog/blocks/mk-clone-wall-transition",
|
||||
"catalog/blocks/organic-light-leak-overlay",
|
||||
"catalog/blocks/transitions-3d",
|
||||
"catalog/blocks/transitions-blur",
|
||||
"catalog/blocks/transitions-cover",
|
||||
@@ -320,54 +327,6 @@
|
||||
"catalog/blocks/app-showcase",
|
||||
"catalog/blocks/apple-money-count",
|
||||
"catalog/blocks/blue-sweater-intro-video",
|
||||
"catalog/blocks/north-korea-locked-down",
|
||||
"catalog/blocks/nyc-paris-flight",
|
||||
"catalog/blocks/ui-3d-reveal",
|
||||
"catalog/blocks/vpn-youtube-spot"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Data",
|
||||
"pages": [
|
||||
"catalog/blocks/data-chart",
|
||||
"catalog/blocks/spain-map",
|
||||
"catalog/blocks/us-map",
|
||||
"catalog/blocks/us-map-bubble",
|
||||
"catalog/blocks/us-map-flow",
|
||||
"catalog/blocks/us-map-hex",
|
||||
"catalog/blocks/world-map"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Effects",
|
||||
"pages": [
|
||||
"catalog/components/grain-overlay",
|
||||
"catalog/components/grid-pixelate-wipe",
|
||||
"catalog/components/parallax-unzoom",
|
||||
"catalog/components/parallax-zoom",
|
||||
"catalog/components/shimmer-sweep",
|
||||
"catalog/components/vignette"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Text Effects",
|
||||
"pages": [
|
||||
"catalog/components/caption-blend-difference",
|
||||
"catalog/components/morph-text",
|
||||
"catalog/components/texture-mask-text"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Blocks",
|
||||
"pages": [
|
||||
"catalog/blocks/flowchart",
|
||||
"catalog/blocks/flowchart-vertical",
|
||||
"catalog/blocks/logo-outro"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Code Snippets",
|
||||
"pages": [
|
||||
"catalog/blocks/code-snippet-apple-terminal-basic",
|
||||
"catalog/blocks/code-snippet-apple-terminal-clear-dark",
|
||||
"catalog/blocks/code-snippet-apple-terminal-clear-light",
|
||||
@@ -391,7 +350,72 @@
|
||||
"catalog/blocks/code-snippet-monokai",
|
||||
"catalog/blocks/code-snippet-solarized-light",
|
||||
"catalog/blocks/code-snippet-visual-studio-dark",
|
||||
"catalog/blocks/code-snippet-visual-studio-light"
|
||||
"catalog/blocks/code-snippet-visual-studio-light",
|
||||
"catalog/blocks/north-korea-locked-down",
|
||||
"catalog/blocks/nyc-paris-flight",
|
||||
"catalog/blocks/ui-3d-reveal",
|
||||
"catalog/blocks/vpn-youtube-spot"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Data",
|
||||
"pages": [
|
||||
"catalog/blocks/data-chart",
|
||||
"catalog/blocks/mk-line-graph",
|
||||
"catalog/blocks/spain-map",
|
||||
"catalog/blocks/us-map",
|
||||
"catalog/blocks/us-map-bubble",
|
||||
"catalog/blocks/us-map-flow",
|
||||
"catalog/blocks/us-map-hex",
|
||||
"catalog/blocks/world-map"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Effects",
|
||||
"pages": [
|
||||
"catalog/components/caption-blend-difference",
|
||||
"catalog/components/grain-overlay",
|
||||
"catalog/components/grid-pixelate-wipe",
|
||||
"catalog/components/hw-arrow",
|
||||
"catalog/components/hw-boil",
|
||||
"catalog/components/hw-box-label",
|
||||
"catalog/components/hw-callout-circle",
|
||||
"catalog/components/hw-underline",
|
||||
"catalog/components/mk-emphasis-type",
|
||||
"catalog/components/mk-usage-arc",
|
||||
"catalog/components/morph-text",
|
||||
"catalog/components/motion-blur",
|
||||
"catalog/components/parallax-unzoom",
|
||||
"catalog/components/parallax-zoom",
|
||||
"catalog/components/shimmer-sweep",
|
||||
"catalog/components/texture-mask-text",
|
||||
"catalog/components/vignette",
|
||||
"catalog/components/yt-camera-move",
|
||||
"catalog/components/yt-circle-pointer",
|
||||
"catalog/components/yt-feather-highlight",
|
||||
"catalog/components/yt-screen-warp"
|
||||
]
|
||||
},
|
||||
{
|
||||
"group": "Blocks",
|
||||
"pages": [
|
||||
"catalog/blocks/camcorder-hud",
|
||||
"catalog/blocks/flowchart",
|
||||
"catalog/blocks/flowchart-vertical",
|
||||
"catalog/blocks/hw-frame",
|
||||
"catalog/blocks/hw-path-text",
|
||||
"catalog/blocks/hw-pipeline",
|
||||
"catalog/blocks/hw-text-cloud",
|
||||
"catalog/blocks/hw-title",
|
||||
"catalog/blocks/logo-outro",
|
||||
"catalog/blocks/mk-background",
|
||||
"catalog/blocks/mk-placeholder-grid",
|
||||
"catalog/blocks/mk-progress-stat",
|
||||
"catalog/blocks/mk-specs-list",
|
||||
"catalog/blocks/yt-lcd-background",
|
||||
"catalog/blocks/yt-logo-intro",
|
||||
"catalog/blocks/yt-prism-title",
|
||||
"catalog/blocks/yt-vertical-fill"
|
||||
]
|
||||
}
|
||||
]
|
||||
|
||||
+2337
-1369
File diff suppressed because it is too large
Load Diff
+196
@@ -0,0 +1,196 @@
|
||||
<!doctype html>
|
||||
<!-- Demo plate for the catalog preview. Identical to hw-path-text.html except the
|
||||
root background, which is opaque here so the transparent overlay reads.
|
||||
Not installed by `hyperframes add` - registry-item.json lists hw-path-text.html only. -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: "Caveat";
|
||||
src: url("assets/fonts/Caveat-700-latin.woff2") format("woff2");
|
||||
font-weight: 700;
|
||||
font-display: block;
|
||||
}
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
background: #17161a;
|
||||
overflow: hidden;
|
||||
}
|
||||
#hw-pt-root {
|
||||
--hw-font-print: "Caveat", cursive;
|
||||
--hw-font-script: "Caveat", cursive;
|
||||
--hw-ink: #f4f2ec;
|
||||
|
||||
position: relative;
|
||||
width: 1920px;
|
||||
height: 1080px;
|
||||
overflow: hidden;
|
||||
background: transparent;
|
||||
font-family: var(--hw-font-print, "Caveat", cursive);
|
||||
}
|
||||
#hw-pt-svg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: visible;
|
||||
}
|
||||
/* The textPath carrier is geometry only — without fill:none it inherits
|
||||
SVG's default black fill and renders as a solid blob under the text. */
|
||||
#hw-pt-path {
|
||||
fill: none;
|
||||
}
|
||||
#hw-pt-guide {
|
||||
fill: none;
|
||||
stroke: rgba(244, 242, 236, 0.22);
|
||||
stroke-width: 3;
|
||||
stroke-dasharray: 2 14;
|
||||
stroke-linecap: round;
|
||||
}
|
||||
#hw-pt-svg text {
|
||||
font-family: var(--hw-font-print, "Caveat", cursive);
|
||||
font-weight: 700;
|
||||
fill: var(--hw-ink);
|
||||
}
|
||||
#hw-pt-svg tspan {
|
||||
opacity: 0;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div
|
||||
id="hw-pt-root"
|
||||
data-composition-id="hw-path-text"
|
||||
data-start="0"
|
||||
data-duration="6"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
>
|
||||
<svg id="hw-pt-svg" viewBox="0 0 1920 1080">
|
||||
<path id="hw-pt-path" d=""></path>
|
||||
<path id="hw-pt-guide" d=""></path>
|
||||
<text id="hw-pt-text" font-size="92">
|
||||
<textPath id="hw-pt-tp" href="#hw-pt-path" startOffset="4%"></textPath>
|
||||
</text>
|
||||
</svg>
|
||||
<div
|
||||
id="hw-pt-drv"
|
||||
class="clip"
|
||||
data-start="0"
|
||||
data-duration="6"
|
||||
data-track-index="0"
|
||||
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
|
||||
></div>
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
window.__timelines = window.__timelines || {};
|
||||
|
||||
/* ---- published parameters ----
|
||||
Handwriting along any path — custom path text, natively via
|
||||
SVG textPath (no Fusion overlay). CONFIG.d is the whole design. */
|
||||
var CONFIG = {
|
||||
text: "come on this wild journey with me...",
|
||||
d: "M 220 700 C 520 480, 900 820, 1230 560 S 1700 380, 1780 420",
|
||||
fontSize: 92,
|
||||
showGuide: true, // faint dotted guide under the writing
|
||||
typeCharDelay: 0.05,
|
||||
seed: 12,
|
||||
boil: { enabled: true, amp: 1.4, rot: 0.3, frameDrop: 3 },
|
||||
animationOut: true,
|
||||
};
|
||||
|
||||
var DUR = 6;
|
||||
function clamp(v, lo, hi) {
|
||||
return Math.min(hi, Math.max(lo, v));
|
||||
}
|
||||
var OUT = clamp(DUR * 0.06, 0.25, 0.6);
|
||||
|
||||
/* hw family runtime */
|
||||
window.hwOnUpdate = function (tl, fn) {
|
||||
if (!tl.__hwRenders) {
|
||||
tl.__hwRenders = [];
|
||||
tl.eventCallback("onUpdate", function () {
|
||||
for (var i = 0; i < tl.__hwRenders.length; i++) tl.__hwRenders[i]();
|
||||
});
|
||||
}
|
||||
tl.__hwRenders.push(fn);
|
||||
fn();
|
||||
};
|
||||
window.hwHash = function (n, seed) {
|
||||
var x = Math.sin(n * 127.1 + (seed || 1) * 311.7) * 43758.5453;
|
||||
return (x - Math.floor(x)) * 2 - 1;
|
||||
};
|
||||
window.hwBoil = function (tl, target, opts) {
|
||||
opts = opts || {};
|
||||
var amp = opts.amp !== undefined ? opts.amp : 1.6;
|
||||
var rot = opts.rot !== undefined ? opts.rot : 0.5;
|
||||
var fps = opts.fps || 30;
|
||||
var drop = opts.frameDrop || 3;
|
||||
var seed = opts.seed || 1;
|
||||
var els = gsap.utils.toArray(target);
|
||||
window.hwOnUpdate(tl, function () {
|
||||
var step = Math.floor((tl.time() * fps) / drop);
|
||||
for (var i = 0; i < els.length; i++) {
|
||||
gsap.set(els[i], {
|
||||
x: window.hwHash(step * 3 + i * 97, seed) * amp,
|
||||
y: window.hwHash(step * 3 + 1 + i * 97, seed) * amp,
|
||||
rotation: (opts.baseRot || 0) + window.hwHash(step * 3 + 2 + i * 97, seed) * rot,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
document.getElementById("hw-pt-path").setAttribute("d", CONFIG.d);
|
||||
if (CONFIG.showGuide) document.getElementById("hw-pt-guide").setAttribute("d", CONFIG.d);
|
||||
document.getElementById("hw-pt-text").setAttribute("font-size", CONFIG.fontSize);
|
||||
|
||||
/* per-char tspans along the path */
|
||||
var NS = "http://www.w3.org/2000/svg";
|
||||
var tp = document.getElementById("hw-pt-tp");
|
||||
Array.from(CONFIG.text).forEach(function (ch) {
|
||||
var t = document.createElementNS(NS, "tspan");
|
||||
t.textContent = ch;
|
||||
tp.appendChild(t);
|
||||
});
|
||||
|
||||
var tl = gsap.timeline({ paused: true });
|
||||
|
||||
/* guide draws first, then the handwriting types along it */
|
||||
var guide = document.getElementById("hw-pt-guide");
|
||||
if (CONFIG.showGuide) {
|
||||
var glen = guide.getTotalLength();
|
||||
gsap.set(guide, { strokeDasharray: "2 14", strokeDashoffset: glen });
|
||||
tl.to(guide, { strokeDashoffset: 0, duration: 0.9, ease: "power2.inOut" }, 0.2);
|
||||
}
|
||||
var chars = tp.querySelectorAll("tspan");
|
||||
var delay = Math.min(CONFIG.typeCharDelay, 2.6 / Math.max(1, chars.length));
|
||||
tl.to(chars, { opacity: 1, duration: 0.03, stagger: delay, ease: "none" }, 0.7);
|
||||
|
||||
if (CONFIG.boil.enabled) {
|
||||
window.hwBoil(tl, "#hw-pt-svg", {
|
||||
amp: CONFIG.boil.amp,
|
||||
rot: CONFIG.boil.rot,
|
||||
frameDrop: CONFIG.boil.frameDrop,
|
||||
seed: CONFIG.seed,
|
||||
});
|
||||
}
|
||||
|
||||
if (CONFIG.animationOut) {
|
||||
tl.to("#hw-pt-svg", { opacity: 0, duration: OUT, ease: "power2.in" }, DUR - OUT - 0.05);
|
||||
}
|
||||
tl.set("#hw-pt-root", { visibility: "hidden" }, DUR - 0.02);
|
||||
|
||||
window.__timelines["hw-path-text"] = tl;
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -40,6 +40,11 @@
|
||||
height: 100%;
|
||||
overflow: visible;
|
||||
}
|
||||
/* The textPath carrier is geometry only — without fill:none it inherits
|
||||
SVG's default black fill and renders as a solid blob under the text. */
|
||||
#hw-pt-path {
|
||||
fill: none;
|
||||
}
|
||||
#hw-pt-guide {
|
||||
fill: none;
|
||||
stroke: rgba(244, 242, 236, 0.22);
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
"type": "hyperframes:block",
|
||||
"title": "Text Along a Path",
|
||||
"description": "Handwriting flowing along any SVG path with a per-character reveal; the path is the only geometry input",
|
||||
"tags": ["handwritten", "typography", "path"],
|
||||
"tags": ["handwritten", "typography", "path", "overlay"],
|
||||
"dimensions": {
|
||||
"width": 1920,
|
||||
"height": 1080
|
||||
|
||||
+271
@@ -0,0 +1,271 @@
|
||||
<!doctype html>
|
||||
<!-- Demo plate for the catalog preview. Identical to hw-pipeline.html except the
|
||||
root background, which is opaque here so the transparent overlay reads.
|
||||
Not installed by `hyperframes add` - registry-item.json lists hw-pipeline.html only. -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: "Caveat";
|
||||
src: url("assets/fonts/Caveat-700-latin.woff2") format("woff2");
|
||||
font-weight: 700;
|
||||
font-display: block;
|
||||
}
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
background: #17161a;
|
||||
overflow: hidden;
|
||||
}
|
||||
#hw-pl-root {
|
||||
--hw-font-print: "Caveat", cursive;
|
||||
--hw-font-script: "Caveat", cursive;
|
||||
--hw-ink: #f4f2ec;
|
||||
--hw-accent: #6d6dff;
|
||||
|
||||
position: relative;
|
||||
width: 1920px;
|
||||
height: 1080px;
|
||||
overflow: hidden;
|
||||
background: transparent; /* overlays footage or boards */
|
||||
font-family: var(--hw-font-print, "Caveat", cursive);
|
||||
}
|
||||
#hw-pl-svg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: visible;
|
||||
}
|
||||
#hw-pl-svg path {
|
||||
fill: none;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
.hw-pl-box {
|
||||
stroke: var(--hw-ink);
|
||||
stroke-width: 6;
|
||||
}
|
||||
.hw-pl-conn {
|
||||
stroke: var(--hw-accent);
|
||||
stroke-width: 5;
|
||||
}
|
||||
.hw-pl-label {
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
font-weight: 700;
|
||||
color: var(--hw-ink);
|
||||
white-space: nowrap;
|
||||
opacity: 0;
|
||||
text-shadow: 0 2px 14px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div
|
||||
id="hw-pl-root"
|
||||
data-composition-id="hw-pipeline"
|
||||
data-start="0"
|
||||
data-duration="7"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
>
|
||||
<svg id="hw-pl-svg" viewBox="0 0 1920 1080"></svg>
|
||||
<div id="hw-pl-labels"></div>
|
||||
<div
|
||||
id="hw-pl-drv"
|
||||
class="clip"
|
||||
data-start="0"
|
||||
data-duration="7"
|
||||
data-track-index="0"
|
||||
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
|
||||
></div>
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
window.__timelines = window.__timelines || {};
|
||||
|
||||
/* ---- published parameters ----
|
||||
A flowchart "pipeline": wobbled boxes joined by curved
|
||||
connectors, drawing on in sequence, all boiling. */
|
||||
var CONFIG = {
|
||||
nodes: [{ label: "Idea" }, { label: "Record" }, { label: "Shine!" }],
|
||||
boxW: 320,
|
||||
boxH: 170,
|
||||
gap: 190, // px between boxes
|
||||
y: 455, // top of the row; x centers automatically
|
||||
fontSize: 56,
|
||||
seed: 11,
|
||||
boil: { enabled: true, amp: 1.6, rot: 0.4, frameDrop: 3 },
|
||||
animationOut: true,
|
||||
};
|
||||
|
||||
var DUR = 7;
|
||||
function clamp(v, lo, hi) {
|
||||
return Math.min(hi, Math.max(lo, v));
|
||||
}
|
||||
var OUT = clamp(DUR * 0.06, 0.25, 0.6);
|
||||
|
||||
/* hw family runtime */
|
||||
window.hwOnUpdate = function (tl, fn) {
|
||||
if (!tl.__hwRenders) {
|
||||
tl.__hwRenders = [];
|
||||
tl.eventCallback("onUpdate", function () {
|
||||
for (var i = 0; i < tl.__hwRenders.length; i++) tl.__hwRenders[i]();
|
||||
});
|
||||
}
|
||||
tl.__hwRenders.push(fn);
|
||||
fn();
|
||||
};
|
||||
window.hwHash = function (n, seed) {
|
||||
var x = Math.sin(n * 127.1 + (seed || 1) * 311.7) * 43758.5453;
|
||||
return (x - Math.floor(x)) * 2 - 1;
|
||||
};
|
||||
window.hwBoil = function (tl, target, opts) {
|
||||
opts = opts || {};
|
||||
var amp = opts.amp !== undefined ? opts.amp : 1.6;
|
||||
var rot = opts.rot !== undefined ? opts.rot : 0.5;
|
||||
var fps = opts.fps || 30;
|
||||
var drop = opts.frameDrop || 3;
|
||||
var seed = opts.seed || 1;
|
||||
var els = gsap.utils.toArray(target);
|
||||
window.hwOnUpdate(tl, function () {
|
||||
var step = Math.floor((tl.time() * fps) / drop);
|
||||
for (var i = 0; i < els.length; i++) {
|
||||
gsap.set(els[i], {
|
||||
x: window.hwHash(step * 3 + i * 97, seed) * amp,
|
||||
y: window.hwHash(step * 3 + 1 + i * 97, seed) * amp,
|
||||
rotation: (opts.baseRot || 0) + window.hwHash(step * 3 + 2 + i * 97, seed) * rot,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
window.hwWobbleRect = function (w, h, r, seed, amp) {
|
||||
amp = amp === undefined ? 3.5 : amp;
|
||||
function wob(n) {
|
||||
return window.hwHash(n, seed) * amp;
|
||||
}
|
||||
var d = "M " + (r + wob(1)) + " " + wob(2);
|
||||
d += " L " + (w / 2 + wob(3)) + " " + wob(4);
|
||||
d += " L " + (w - r + wob(5)) + " " + wob(6);
|
||||
d += " Q " + (w + wob(7)) + " " + wob(8) + " " + (w + wob(9)) + " " + (r + wob(10));
|
||||
d += " L " + (w + wob(11)) + " " + (h / 2 + wob(12));
|
||||
d += " L " + (w + wob(13)) + " " + (h - r + wob(14));
|
||||
d += " Q " + (w + wob(15)) + " " + (h + wob(16)) + " " + (w - r + wob(17)) + " " + (h + wob(18));
|
||||
d += " L " + (w / 2 + wob(19)) + " " + (h + wob(20));
|
||||
d += " L " + (r + wob(21)) + " " + (h + wob(22));
|
||||
d += " Q " + wob(23) + " " + (h + wob(24)) + " " + wob(25) + " " + (h - r + wob(26));
|
||||
d += " L " + wob(27) + " " + (h / 2 + wob(28));
|
||||
d += " L " + wob(29) + " " + (r + wob(30));
|
||||
d += " Q " + wob(31) + " " + wob(32) + " " + (r + wob(33)) + " " + wob(34);
|
||||
return d;
|
||||
};
|
||||
|
||||
var NS = "http://www.w3.org/2000/svg";
|
||||
var svg = document.getElementById("hw-pl-svg");
|
||||
var labels = document.getElementById("hw-pl-labels");
|
||||
var n = CONFIG.nodes.length;
|
||||
var totalW = n * CONFIG.boxW + (n - 1) * CONFIG.gap;
|
||||
var x0 = (1920 - totalW) / 2;
|
||||
|
||||
var boxes = [],
|
||||
conns = [],
|
||||
labelEls = [];
|
||||
CONFIG.nodes.forEach(function (node, i) {
|
||||
var bx = x0 + i * (CONFIG.boxW + CONFIG.gap);
|
||||
/* outer g owns the translate (GSAP transforms REPLACE the SVG
|
||||
transform attribute — never boil the element that carries it);
|
||||
inner g is the boil target */
|
||||
var gOuter = document.createElementNS(NS, "g");
|
||||
gOuter.setAttribute("transform", "translate(" + bx + " " + CONFIG.y + ")");
|
||||
var g = document.createElementNS(NS, "g");
|
||||
g.setAttribute("id", "hw-pl-box-g-" + i);
|
||||
gOuter.appendChild(g);
|
||||
var p = document.createElementNS(NS, "path");
|
||||
p.setAttribute("class", "hw-pl-box");
|
||||
p.setAttribute("id", "hw-pl-box-" + i);
|
||||
p.setAttribute("d", window.hwWobbleRect(CONFIG.boxW, CONFIG.boxH, 24, CONFIG.seed + i));
|
||||
g.appendChild(p);
|
||||
svg.appendChild(gOuter);
|
||||
boxes.push(p);
|
||||
|
||||
var label = document.createElement("div");
|
||||
label.className = "hw-pl-label";
|
||||
label.id = "hw-pl-label-" + i;
|
||||
label.textContent = node.label;
|
||||
label.style.left = bx + "px";
|
||||
label.style.top = CONFIG.y + CONFIG.boxH / 2 - CONFIG.fontSize * 0.62 + "px";
|
||||
label.style.width = CONFIG.boxW + "px";
|
||||
label.style.fontSize = CONFIG.fontSize + "px";
|
||||
labels.appendChild(label);
|
||||
labelEls.push(label);
|
||||
|
||||
if (i < n - 1) {
|
||||
var cx = bx + CONFIG.boxW,
|
||||
cy = CONFIG.y + CONFIG.boxH / 2;
|
||||
var ex = cx + CONFIG.gap,
|
||||
ey = cy;
|
||||
var c = document.createElementNS(NS, "path");
|
||||
c.setAttribute("class", "hw-pl-conn");
|
||||
c.setAttribute("id", "hw-pl-conn-" + i);
|
||||
c.setAttribute(
|
||||
"d",
|
||||
"M " + (cx + 10) + " " + cy +
|
||||
" Q " + (cx + CONFIG.gap / 2 + window.hwHash(i * 9 + 2, CONFIG.seed) * 16) + " " + (cy - 46 - window.hwHash(i * 9 + 3, CONFIG.seed) * 12) +
|
||||
" " + (ex - 10) + " " + ey +
|
||||
" M " + (ex - 10) + " " + ey + " l -30 -14 M " + (ex - 10) + " " + ey + " l -26 18"
|
||||
);
|
||||
svg.appendChild(c);
|
||||
conns.push(c);
|
||||
}
|
||||
});
|
||||
|
||||
var tl = gsap.timeline({ paused: true });
|
||||
|
||||
/* sequence: box draws -> label -> connector -> next box... */
|
||||
var t = 0.3;
|
||||
CONFIG.nodes.forEach(function (_, i) {
|
||||
var p = boxes[i];
|
||||
var len = p.getTotalLength();
|
||||
gsap.set(p, { strokeDasharray: len, strokeDashoffset: len });
|
||||
tl.to(p, { strokeDashoffset: 0, duration: 0.65, ease: "power2.inOut" }, t);
|
||||
tl.to(labelEls[i], { opacity: 1, duration: 0.35, ease: "power2.out" }, t + 0.45);
|
||||
t += 0.7;
|
||||
if (i < conns.length) {
|
||||
var c = conns[i];
|
||||
var cl = c.getTotalLength();
|
||||
gsap.set(c, { strokeDasharray: cl, strokeDashoffset: cl });
|
||||
tl.to(c, { strokeDashoffset: 0, duration: 0.45, ease: "power2.inOut" }, t);
|
||||
t += 0.5;
|
||||
}
|
||||
});
|
||||
|
||||
if (CONFIG.boil.enabled) {
|
||||
var targets = boxes.map(function (_, i) {
|
||||
return "#hw-pl-box-g-" + i;
|
||||
});
|
||||
window.hwBoil(tl, targets.concat([".hw-pl-conn", ".hw-pl-label"]), {
|
||||
amp: CONFIG.boil.amp,
|
||||
rot: CONFIG.boil.rot,
|
||||
frameDrop: CONFIG.boil.frameDrop,
|
||||
seed: CONFIG.seed,
|
||||
});
|
||||
}
|
||||
|
||||
if (CONFIG.animationOut) {
|
||||
tl.to([svg, labels], { opacity: 0, duration: OUT, ease: "power2.in" }, DUR - OUT - 0.05);
|
||||
}
|
||||
tl.set("#hw-pl-root", { visibility: "hidden" }, DUR - 0.02);
|
||||
|
||||
window.__timelines["hw-pipeline"] = tl;
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -4,7 +4,7 @@
|
||||
"type": "hyperframes:block",
|
||||
"title": "Hand-Drawn Flowchart",
|
||||
"description": "Wobbled boxes with handwritten labels joined by curved connectors, drawing on in sequence from a node list",
|
||||
"tags": ["handwritten", "diagram", "data-viz"],
|
||||
"tags": ["handwritten", "diagram", "data-viz", "overlay"],
|
||||
"dimensions": {
|
||||
"width": 1920,
|
||||
"height": 1080
|
||||
|
||||
Vendored
+246
@@ -0,0 +1,246 @@
|
||||
<!doctype html>
|
||||
<!-- Demo plate for the catalog preview. Identical to hw-title.html except the
|
||||
root background, which is opaque here so the transparent overlay reads.
|
||||
Not installed by `hyperframes add` - registry-item.json lists hw-title.html only. -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: "Caveat";
|
||||
src: url("assets/fonts/Caveat-700-latin.woff2") format("woff2");
|
||||
font-weight: 700;
|
||||
font-display: block;
|
||||
}
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
background: #17161a;
|
||||
overflow: hidden;
|
||||
}
|
||||
#hw-t-root {
|
||||
/* ---- hw tokens ---- */
|
||||
--hw-font-print: "Caveat", cursive;
|
||||
--hw-font-script: "Caveat", cursive;
|
||||
--hw-ink: #f4f2ec;
|
||||
--hw-ink-dark: #26241f;
|
||||
--hw-accent-warm: #ffb020;
|
||||
|
||||
position: relative;
|
||||
width: 1920px;
|
||||
height: 1080px;
|
||||
overflow: hidden;
|
||||
background: transparent; /* overlays footage or a board */
|
||||
font-family: var(--hw-font-print, "Caveat", cursive);
|
||||
}
|
||||
#hw-t-wrap {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
#hw-t-line {
|
||||
font-weight: 700;
|
||||
line-height: 1.1;
|
||||
white-space: nowrap;
|
||||
text-shadow: 0 2px 18px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
#hw-t-line .w {
|
||||
display: inline;
|
||||
}
|
||||
#hw-t-under {
|
||||
overflow: visible;
|
||||
display: block;
|
||||
}
|
||||
#hw-t-under path {
|
||||
fill: none;
|
||||
stroke: var(--hw-accent-warm);
|
||||
stroke-width: 7;
|
||||
stroke-linecap: round;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div
|
||||
id="hw-t-root"
|
||||
data-composition-id="hw-title"
|
||||
data-start="0"
|
||||
data-duration="6"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
>
|
||||
<div id="hw-t-wrap">
|
||||
<div id="hw-t-line"></div>
|
||||
<svg id="hw-t-under" width="10" height="34"><path id="hw-t-under-path"></path></svg>
|
||||
</div>
|
||||
<div
|
||||
id="hw-t-drv"
|
||||
class="clip"
|
||||
data-start="0"
|
||||
data-duration="6"
|
||||
data-track-index="0"
|
||||
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
|
||||
></div>
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
window.__timelines = window.__timelines || {};
|
||||
|
||||
/* ---- published parameters ----
|
||||
Handwritten title with the highlight-sweep scalar (the shared
|
||||
mk-callout-highlight mechanic) + squiggle underline + the boil.
|
||||
Demonstrates the dispatcher composing sweep + boil on one
|
||||
timeline (learning: one onUpdate — everything through hwOnUpdate). */
|
||||
var CONFIG = {
|
||||
text: "shine like the star you are",
|
||||
fontSize: 120,
|
||||
scheme: "chalk", // "chalk" (white, over footage) | "ink" (dark, boards)
|
||||
sweep: { enabled: true, start: 0.8, end: 3.2 },
|
||||
underline: { enabled: true, wobble: 6 },
|
||||
boil: { enabled: true, amp: 1.4, rot: 0.3, frameDrop: 3 },
|
||||
seed: 8,
|
||||
x: null, // null = centered
|
||||
y: null,
|
||||
animationIn: true,
|
||||
animationOut: true,
|
||||
};
|
||||
|
||||
var DUR = 6;
|
||||
function clamp(v, lo, hi) {
|
||||
return Math.min(hi, Math.max(lo, v));
|
||||
}
|
||||
var IN = clamp(DUR * 0.08, 0.3, 0.8);
|
||||
var OUT = clamp(DUR * 0.06, 0.25, 0.6);
|
||||
|
||||
/* hw family runtime */
|
||||
window.hwOnUpdate = function (tl, fn) {
|
||||
if (!tl.__hwRenders) {
|
||||
tl.__hwRenders = [];
|
||||
tl.eventCallback("onUpdate", function () {
|
||||
for (var i = 0; i < tl.__hwRenders.length; i++) tl.__hwRenders[i]();
|
||||
});
|
||||
}
|
||||
tl.__hwRenders.push(fn);
|
||||
fn();
|
||||
};
|
||||
window.hwHash = function (n, seed) {
|
||||
var x = Math.sin(n * 127.1 + (seed || 1) * 311.7) * 43758.5453;
|
||||
return (x - Math.floor(x)) * 2 - 1;
|
||||
};
|
||||
window.hwBoil = function (tl, target, opts) {
|
||||
opts = opts || {};
|
||||
var amp = opts.amp !== undefined ? opts.amp : 1.6;
|
||||
var rot = opts.rot !== undefined ? opts.rot : 0.5;
|
||||
var fps = opts.fps || 30;
|
||||
var drop = opts.frameDrop || 3;
|
||||
var seed = opts.seed || 1;
|
||||
var els = gsap.utils.toArray(target);
|
||||
window.hwOnUpdate(tl, function () {
|
||||
var step = Math.floor((tl.time() * fps) / drop);
|
||||
for (var i = 0; i < els.length; i++) {
|
||||
gsap.set(els[i], {
|
||||
x: window.hwHash(step * 3 + i * 97, seed) * amp,
|
||||
y: window.hwHash(step * 3 + 1 + i * 97, seed) * amp,
|
||||
rotation: (opts.baseRot || 0) + window.hwHash(step * 3 + 2 + i * 97, seed) * rot,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var FULL = CONFIG.scheme === "ink" ? "#26241f" : "#f4f2ec";
|
||||
var FAINT = CONFIG.scheme === "ink" ? "rgba(38,36,31,0.3)" : "rgba(244,242,236,0.32)";
|
||||
|
||||
var wrap = document.getElementById("hw-t-wrap");
|
||||
var line = document.getElementById("hw-t-line");
|
||||
line.style.fontSize = CONFIG.fontSize + "px";
|
||||
|
||||
/* word spans */
|
||||
var wordEls = [];
|
||||
CONFIG.text.split(/\s+/).forEach(function (w, i) {
|
||||
if (i > 0) line.appendChild(document.createTextNode(" "));
|
||||
var span = document.createElement("span");
|
||||
span.className = "w";
|
||||
span.textContent = w;
|
||||
span.style.color = CONFIG.sweep.enabled ? FAINT : FULL;
|
||||
line.appendChild(span);
|
||||
wordEls.push(span);
|
||||
});
|
||||
|
||||
/* measure + position; build squiggle underline to match width */
|
||||
var rect = line.getBoundingClientRect();
|
||||
wrap.style.left = (CONFIG.x === null ? Math.round((1920 - rect.width) / 2) : CONFIG.x) + "px";
|
||||
wrap.style.top = (CONFIG.y === null ? Math.round((1080 - rect.height) / 2) - 20 : CONFIG.y) + "px";
|
||||
|
||||
var underPath = document.getElementById("hw-t-under-path");
|
||||
if (CONFIG.underline.enabled) {
|
||||
var uw = Math.round(rect.width);
|
||||
var svg = document.getElementById("hw-t-under");
|
||||
svg.setAttribute("width", uw);
|
||||
svg.setAttribute("viewBox", "0 0 " + uw + " 34");
|
||||
var segs = Math.max(6, Math.round(uw / 90));
|
||||
var d = "M 2 " + (16 + window.hwHash(0, CONFIG.seed) * 4).toFixed(1);
|
||||
for (var i = 1; i <= segs; i++) {
|
||||
var x = (uw / segs) * i;
|
||||
var y = 16 + window.hwHash(i * 5 + 1, CONFIG.seed) * CONFIG.underline.wobble;
|
||||
var cxp = x - uw / segs / 2 + window.hwHash(i * 5 + 2, CONFIG.seed) * 10;
|
||||
var cyp = 16 + (i % 2 ? -1 : 1) * (8 + window.hwHash(i * 5 + 3, CONFIG.seed) * 4);
|
||||
d += " Q " + cxp.toFixed(1) + " " + cyp.toFixed(1) + " " + x.toFixed(1) + " " + y.toFixed(1);
|
||||
}
|
||||
underPath.setAttribute("d", d);
|
||||
} else {
|
||||
document.getElementById("hw-t-under").style.display = "none";
|
||||
}
|
||||
|
||||
var tl = gsap.timeline({ paused: true });
|
||||
|
||||
if (CONFIG.animationIn) {
|
||||
gsap.set(wrap, { opacity: 0 });
|
||||
tl.to(wrap, { opacity: 1, duration: IN, ease: "power2.out" }, 0.1);
|
||||
}
|
||||
|
||||
/* the sweep scalar — registered through the dispatcher so it
|
||||
coexists with the boil on the same timeline */
|
||||
if (CONFIG.sweep.enabled) {
|
||||
var mix = gsap.utils.interpolate(FAINT, FULL);
|
||||
var st = { p: 0 };
|
||||
window.hwOnUpdate(tl, function () {
|
||||
var k = st.p * wordEls.length;
|
||||
for (var i = 0; i < wordEls.length; i++) {
|
||||
var f = Math.max(0, Math.min(1, k - i));
|
||||
wordEls[i].style.color = mix(f);
|
||||
}
|
||||
});
|
||||
tl.to(st, { p: 1, duration: CONFIG.sweep.end - CONFIG.sweep.start, ease: "none" }, CONFIG.sweep.start);
|
||||
}
|
||||
|
||||
/* underline draws as the sweep lands */
|
||||
if (CONFIG.underline.enabled) {
|
||||
var len = underPath.getTotalLength();
|
||||
gsap.set(underPath, { strokeDasharray: len, strokeDashoffset: len });
|
||||
var uAt = CONFIG.sweep.enabled ? CONFIG.sweep.end - 0.2 : 0.8;
|
||||
tl.to(underPath, { strokeDashoffset: 0, duration: 0.7, ease: "power2.inOut" }, uAt);
|
||||
}
|
||||
|
||||
if (CONFIG.boil.enabled) {
|
||||
window.hwBoil(tl, "#hw-t-wrap", {
|
||||
amp: CONFIG.boil.amp,
|
||||
rot: CONFIG.boil.rot,
|
||||
frameDrop: CONFIG.boil.frameDrop,
|
||||
seed: CONFIG.seed,
|
||||
});
|
||||
}
|
||||
|
||||
if (CONFIG.animationOut) {
|
||||
tl.to(wrap, { opacity: 0, duration: OUT, ease: "power2.in" }, DUR - OUT - 0.05);
|
||||
}
|
||||
tl.set("#hw-t-root", { visibility: "hidden" }, DUR - 0.02);
|
||||
|
||||
window.__timelines["hw-title"] = tl;
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -4,7 +4,7 @@
|
||||
"type": "hyperframes:block",
|
||||
"title": "Handwritten Title",
|
||||
"description": "Handwritten display text with a scalar-driven word-by-word highlight sweep and a seeded squiggle underline sized to the text; boils",
|
||||
"tags": ["handwritten", "typography", "title-card"],
|
||||
"tags": ["handwritten", "typography", "title-card", "overlay"],
|
||||
"dimensions": {
|
||||
"width": 1920,
|
||||
"height": 1080
|
||||
|
||||
+11
-2
@@ -57,7 +57,11 @@
|
||||
letter-spacing: -0.01em;
|
||||
color: var(--mk-ink);
|
||||
font-variant-numeric: tabular-nums;
|
||||
transform: translate(-50%, -100%);
|
||||
/* Horizontal centring only. The label's vertical offset is baked into
|
||||
`top` instead of a CSS translate, because the entrance tween animates
|
||||
`y` and GSAP rewrites this transform, dropping any vertical shift. */
|
||||
transform: translate(-50%, 0);
|
||||
line-height: 38px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.mk-lg-xlabel {
|
||||
@@ -197,11 +201,16 @@
|
||||
|
||||
if (CONFIG.showValues) {
|
||||
var val = document.createElement("div");
|
||||
/* Series after the first label downward. With every series
|
||||
labelling upward the numbers collide wherever the lines run
|
||||
close together. GAP clears the dot radius plus line stroke. */
|
||||
var below = si > 0;
|
||||
var GAP = 22;
|
||||
val.className = "mk-lg-value";
|
||||
val.id = "mk-lg-val-" + si + "-" + i;
|
||||
val.textContent = v;
|
||||
val.style.left = px(i) + "px";
|
||||
val.style.top = py(v) - 20 + "px";
|
||||
val.style.top = py(v) + (below ? GAP : -(GAP + 38)) + "px";
|
||||
labels.appendChild(val);
|
||||
vals.push({ el: val, si: si, i: i });
|
||||
}
|
||||
|
||||
-174
@@ -1,174 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<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;
|
||||
}
|
||||
body {
|
||||
background: transparent;
|
||||
overflow: hidden;
|
||||
}
|
||||
#mk-ls-root {
|
||||
/* ---- mk tokens: override in the host to re-skin the family ---- */
|
||||
--mk-font: "Inter", -apple-system, sans-serif;
|
||||
--mk-ink: #1d1d1f;
|
||||
--mk-ink-dim: #6e6e73;
|
||||
--mk-hairline: 3px;
|
||||
|
||||
position: relative;
|
||||
width: 1920px;
|
||||
height: 1080px;
|
||||
overflow: hidden;
|
||||
background: transparent; /* overlays footage or mk-background */
|
||||
font-family: var(--mk-font);
|
||||
}
|
||||
#mk-ls-svg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
}
|
||||
#mk-ls-outline {
|
||||
fill: none;
|
||||
stroke: var(--mk-ink);
|
||||
stroke-width: var(--mk-hairline);
|
||||
}
|
||||
#mk-ls-logo {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
}
|
||||
#mk-ls-logo img {
|
||||
width: 72%;
|
||||
height: 72%;
|
||||
object-fit: contain;
|
||||
}
|
||||
#mk-ls-text {
|
||||
font-weight: 600;
|
||||
font-size: 120px;
|
||||
letter-spacing: -0.03em;
|
||||
color: var(--mk-ink);
|
||||
}
|
||||
#mk-ls-caption {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-weight: 500;
|
||||
font-size: 32px;
|
||||
color: var(--mk-ink-dim);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div
|
||||
id="mk-ls-root"
|
||||
data-composition-id="mk-logo-sting"
|
||||
data-start="0"
|
||||
data-duration="3.5"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
>
|
||||
<svg id="mk-ls-svg" viewBox="0 0 1920 1080">
|
||||
<rect id="mk-ls-outline"></rect>
|
||||
</svg>
|
||||
<div id="mk-ls-logo"><span id="mk-ls-text"></span></div>
|
||||
<div id="mk-ls-caption"></div>
|
||||
<div
|
||||
id="mk-ls-drv"
|
||||
class="clip"
|
||||
data-start="0"
|
||||
data-duration="3.5"
|
||||
data-track-index="0"
|
||||
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
|
||||
></div>
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
window.__timelines = window.__timelines || {};
|
||||
|
||||
/* ---- published parameters (inspector-style CONFIG) ----
|
||||
A logo preset: hairline rounded-square outline draws on, the
|
||||
logo lands inside. logoType switches drop-zone image <-> wordmark
|
||||
text (same animation shell — one logoType toggle). */
|
||||
var CONFIG = {
|
||||
// no scheme param — scheme-agnostic; re-skin via the --mk-* tokens
|
||||
logoType: "text", // "text" | "image"
|
||||
text: "HF",
|
||||
src: null, // image path when logoType === "image"
|
||||
boxSize: 340, // outline square, px
|
||||
radius: 72,
|
||||
caption: "Built with HyperFrames",
|
||||
animationOut: true,
|
||||
};
|
||||
|
||||
var W = 1920,
|
||||
H = 1080,
|
||||
DUR = 3.5;
|
||||
|
||||
var root = document.getElementById("mk-ls-root");
|
||||
var outline = document.getElementById("mk-ls-outline");
|
||||
var logo = document.getElementById("mk-ls-logo");
|
||||
var textEl = document.getElementById("mk-ls-text");
|
||||
var caption = document.getElementById("mk-ls-caption");
|
||||
|
||||
var S = CONFIG.boxSize;
|
||||
var bx = (W - S) / 2,
|
||||
by = (H - S) / 2 - 40;
|
||||
outline.setAttribute("x", bx);
|
||||
outline.setAttribute("y", by);
|
||||
outline.setAttribute("width", S);
|
||||
outline.setAttribute("height", S);
|
||||
outline.setAttribute("rx", CONFIG.radius);
|
||||
|
||||
logo.style.left = bx + "px";
|
||||
logo.style.top = by + "px";
|
||||
logo.style.width = S + "px";
|
||||
logo.style.height = S + "px";
|
||||
logo.style.borderRadius = CONFIG.radius + "px";
|
||||
|
||||
if (CONFIG.logoType === "image" && CONFIG.src) {
|
||||
textEl.remove();
|
||||
var img = document.createElement("img");
|
||||
img.src = CONFIG.src;
|
||||
logo.appendChild(img);
|
||||
} else {
|
||||
textEl.textContent = CONFIG.text;
|
||||
}
|
||||
|
||||
caption.textContent = CONFIG.caption;
|
||||
caption.style.top = by + S + 48 + "px";
|
||||
|
||||
var tl = gsap.timeline({ paused: true });
|
||||
|
||||
/* outline draws on */
|
||||
var len = outline.getTotalLength();
|
||||
gsap.set(outline, { strokeDasharray: len, strokeDashoffset: len });
|
||||
tl.to(outline, { strokeDashoffset: 0, duration: 1.0, ease: "power2.inOut" }, 0.15);
|
||||
|
||||
/* logo lands inside */
|
||||
gsap.set(logo, { opacity: 0, scale: 0.82 });
|
||||
tl.to(logo, { opacity: 1, scale: 1, duration: 0.6, ease: "back.out(1.2)" }, 0.85);
|
||||
|
||||
/* caption */
|
||||
gsap.set(caption, { opacity: 0, y: 12 });
|
||||
tl.to(caption, { opacity: 1, y: 0, duration: 0.5, ease: "power2.out" }, 1.35);
|
||||
|
||||
/* exit + hard kill */
|
||||
if (CONFIG.animationOut) {
|
||||
tl.to([logo, caption], { opacity: 0, duration: 0.35, ease: "power2.in" }, DUR - 0.55);
|
||||
tl.to(outline, { opacity: 0, duration: 0.35, ease: "power2.in" }, DUR - 0.5);
|
||||
}
|
||||
tl.set(root, { visibility: "hidden" }, DUR - 0.02);
|
||||
|
||||
window.__timelines["mk-logo-sting"] = tl;
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
|
||||
"name": "mk-logo-sting",
|
||||
"type": "hyperframes:block",
|
||||
"title": "Outline Logo Sting",
|
||||
"description": "A hairline rounded-square outline draws on and the logo lands inside \u2014 image or text wordmark in the same shell",
|
||||
"tags": ["logo", "sting", "minimal"],
|
||||
"dimensions": {
|
||||
"width": 1920,
|
||||
"height": 1080
|
||||
},
|
||||
"duration": 3.5,
|
||||
"files": [
|
||||
{
|
||||
"path": "mk-logo-sting.html",
|
||||
"target": "compositions/mk-logo-sting.html",
|
||||
"type": "hyperframes:composition"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
{
|
||||
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
|
||||
"name": "yt-doc-lower-third",
|
||||
"type": "hyperframes:block",
|
||||
"title": "Documentary Lower Third",
|
||||
"description": "Name and role lower third: accent bar draws down, text slides in, hairline rule grows; overlay and light schemes",
|
||||
"tags": ["lower-third", "title", "creator"],
|
||||
"dimensions": {
|
||||
"width": 1920,
|
||||
"height": 1080
|
||||
},
|
||||
"duration": 6,
|
||||
"files": [
|
||||
{
|
||||
"path": "yt-doc-lower-third.html",
|
||||
"target": "compositions/yt-doc-lower-third.html",
|
||||
"type": "hyperframes:composition"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,169 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<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;
|
||||
}
|
||||
body {
|
||||
background: transparent;
|
||||
overflow: hidden;
|
||||
}
|
||||
#yt-lt-root {
|
||||
/* ---- yt tokens ---- */
|
||||
--yt-font: "Inter", -apple-system, sans-serif;
|
||||
--yt-ink: #1d1d1f;
|
||||
--yt-ink-dim: #5a5a5f;
|
||||
/* overlay-scheme text over footage — distinct from the --yt-ink board tokens */
|
||||
--yt-text: #ffffff;
|
||||
--yt-text-dim: rgba(255, 255, 255, 0.75);
|
||||
--yt-red: #e62e2e;
|
||||
|
||||
position: relative;
|
||||
width: 1920px;
|
||||
height: 1080px;
|
||||
overflow: hidden;
|
||||
background: transparent; /* overlays footage */
|
||||
font-family: var(--yt-font);
|
||||
}
|
||||
#yt-lt-group {
|
||||
position: absolute;
|
||||
display: flex;
|
||||
gap: 26px;
|
||||
align-items: stretch;
|
||||
}
|
||||
#yt-lt-bar {
|
||||
flex: none;
|
||||
width: 7px;
|
||||
border-radius: 4px;
|
||||
background: var(--yt-red);
|
||||
transform-origin: top center;
|
||||
}
|
||||
#yt-lt-name {
|
||||
font-weight: 700;
|
||||
font-size: 56px;
|
||||
line-height: 1.05;
|
||||
letter-spacing: -0.015em;
|
||||
}
|
||||
#yt-lt-role {
|
||||
margin-top: 8px;
|
||||
font-weight: 500;
|
||||
font-size: 30px;
|
||||
letter-spacing: 0.02em;
|
||||
}
|
||||
#yt-lt-rule {
|
||||
margin-top: 14px;
|
||||
height: 2px;
|
||||
width: 320px;
|
||||
border-radius: 1px;
|
||||
transform-origin: left center;
|
||||
}
|
||||
.yt-overlay #yt-lt-name {
|
||||
color: var(--yt-text);
|
||||
text-shadow: 0 2px 18px rgba(0, 0, 0, 0.55);
|
||||
}
|
||||
.yt-overlay #yt-lt-role {
|
||||
color: var(--yt-text-dim);
|
||||
text-shadow: 0 2px 14px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
.yt-overlay #yt-lt-rule {
|
||||
background: rgba(255, 255, 255, 0.35);
|
||||
}
|
||||
.yt-light #yt-lt-name {
|
||||
color: var(--yt-ink);
|
||||
}
|
||||
.yt-light #yt-lt-role {
|
||||
color: var(--yt-ink-dim);
|
||||
}
|
||||
.yt-light #yt-lt-rule {
|
||||
background: rgba(0, 0, 0, 0.18);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div
|
||||
id="yt-lt-root"
|
||||
data-composition-id="yt-doc-lower-third"
|
||||
data-start="0"
|
||||
data-duration="6"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
>
|
||||
<div id="yt-lt-group">
|
||||
<div id="yt-lt-bar"></div>
|
||||
<div>
|
||||
<div id="yt-lt-name"></div>
|
||||
<div id="yt-lt-role"></div>
|
||||
<div id="yt-lt-rule"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
id="yt-lt-drv"
|
||||
class="clip"
|
||||
data-start="0"
|
||||
data-duration="6"
|
||||
data-track-index="0"
|
||||
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
|
||||
></div>
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
window.__timelines = window.__timelines || {};
|
||||
|
||||
/* ---- published parameters ----
|
||||
The documentary lower third ("Lower 03" energy): red bar draws
|
||||
down, name slides in, role follows, hairline rule grows. */
|
||||
var CONFIG = {
|
||||
scheme: "overlay", // "overlay" (white over footage) | "light" (ink)
|
||||
name: "Alex Winters",
|
||||
role: "Field Producer",
|
||||
x: 120,
|
||||
y: 800, // top of the group
|
||||
animationIn: true,
|
||||
animationOut: true,
|
||||
};
|
||||
|
||||
var DUR = 6;
|
||||
function clamp(v, lo, hi) {
|
||||
return Math.min(hi, Math.max(lo, v));
|
||||
}
|
||||
var OUT = clamp(DUR * 0.06, 0.25, 0.6);
|
||||
|
||||
var root = document.getElementById("yt-lt-root");
|
||||
var group = document.getElementById("yt-lt-group");
|
||||
root.classList.add(CONFIG.scheme === "light" ? "yt-light" : "yt-overlay");
|
||||
document.getElementById("yt-lt-name").textContent = CONFIG.name;
|
||||
document.getElementById("yt-lt-role").textContent = CONFIG.role;
|
||||
group.style.left = CONFIG.x + "px";
|
||||
group.style.top = CONFIG.y + "px";
|
||||
|
||||
var tl = gsap.timeline({ paused: true });
|
||||
|
||||
if (CONFIG.animationIn) {
|
||||
gsap.set("#yt-lt-bar", { scaleY: 0 });
|
||||
gsap.set(["#yt-lt-name", "#yt-lt-role"], { opacity: 0, x: -28 });
|
||||
gsap.set("#yt-lt-rule", { scaleX: 0 });
|
||||
tl.to("#yt-lt-bar", { scaleY: 1, duration: 0.5, ease: "power2.inOut" }, 0.15);
|
||||
tl.to("#yt-lt-name", { opacity: 1, x: 0, duration: 0.55, ease: "power3.out" }, 0.4);
|
||||
tl.to("#yt-lt-role", { opacity: 1, x: 0, duration: 0.55, ease: "power3.out" }, 0.55);
|
||||
tl.to("#yt-lt-rule", { scaleX: 1, duration: 0.6, ease: "power2.inOut" }, 0.7);
|
||||
} else {
|
||||
gsap.set("#yt-lt-bar", { scaleY: 1 });
|
||||
}
|
||||
|
||||
if (CONFIG.animationOut) {
|
||||
tl.to(group, { opacity: 0, x: -20, duration: OUT, ease: "power2.in" }, DUR - OUT - 0.05);
|
||||
}
|
||||
tl.set(root, { visibility: "hidden" }, DUR - 0.02);
|
||||
|
||||
window.__timelines["yt-doc-lower-third"] = tl;
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+185
@@ -0,0 +1,185 @@
|
||||
<!doctype html>
|
||||
<!-- Demo plate for the catalog preview. Identical to hw-arrow.html except the
|
||||
root background, which is opaque here so the transparent overlay reads.
|
||||
Not installed by `hyperframes add` - registry-item.json lists hw-arrow.html only. -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: "Caveat";
|
||||
src: url("assets/fonts/Caveat-700-latin.woff2") format("woff2");
|
||||
font-weight: 700;
|
||||
font-display: block;
|
||||
}
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
background: #17161a;
|
||||
overflow: hidden;
|
||||
}
|
||||
/* ---- hw-arrow ------------------------------------------------------
|
||||
Hand-wobbled draw-on arrow, boiling. Position the wrapper, pick a
|
||||
curve preset, call the helpers. Requires the hw-boil helpers
|
||||
(hwOnUpdate / hwHash / hwBoil) in the host script. */
|
||||
.hw-arrow {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
.hw-arrow svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: visible;
|
||||
}
|
||||
.hw-arrow path {
|
||||
fill: none;
|
||||
stroke: var(--hw-arrow-color, var(--hw-ink, #f4f2ec));
|
||||
stroke-width: var(--hw-arrow-w, 6);
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
.hw-arrow.hw-soft path {
|
||||
filter: blur(0.6px);
|
||||
}
|
||||
/* self-preview scene */
|
||||
#hw-ar-scene {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(135deg, #274060 0%, #123 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: var(--hw-font-print, "Caveat", cursive);
|
||||
font-weight: 700;
|
||||
font-size: 80px;
|
||||
color: rgba(244, 242, 236, 0.92);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Self-preview. WIRING: copy the .hw-arrow CSS, the markup pattern,
|
||||
and the hwArrowPath / hwArrowOn / hwArrowOff helpers (plus the
|
||||
hw-boil helpers) into your host. Then:
|
||||
|
||||
document.querySelector("#hw-my-arrow path")
|
||||
.setAttribute("d", hwArrowPath(360, 160, "swoop", 3));
|
||||
hwArrowOn(tl, "#hw-my-arrow", 2.0);
|
||||
hwBoil(tl, "#hw-my-arrow", { frameDrop: 3, seed: 3 });
|
||||
hwArrowOff(tl, "#hw-my-arrow", 8.0);
|
||||
-->
|
||||
<div
|
||||
id="hw-ar-root"
|
||||
data-composition-id="hw-arrow"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
>
|
||||
<div id="hw-ar-scene"><span>point at things</span></div>
|
||||
<div class="hw-arrow" id="hw-ar-demo" style="left: 560px; top: 320px; width: 380px; height: 180px">
|
||||
<svg viewBox="0 0 380 180"><path id="hw-ar-demo-path"></path></svg>
|
||||
</div>
|
||||
<div
|
||||
id="hw-ar-drv"
|
||||
class="clip"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-track-index="0"
|
||||
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
|
||||
></div>
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
window.__timelines = window.__timelines || {};
|
||||
|
||||
/* ---- hw-boil helpers (shared family runtime — copy once per host) ---- */
|
||||
window.hwOnUpdate = function (tl, fn) {
|
||||
if (!tl.__hwRenders) {
|
||||
tl.__hwRenders = [];
|
||||
tl.eventCallback("onUpdate", function () {
|
||||
for (var i = 0; i < tl.__hwRenders.length; i++) tl.__hwRenders[i]();
|
||||
});
|
||||
}
|
||||
tl.__hwRenders.push(fn);
|
||||
fn();
|
||||
};
|
||||
window.hwHash = function (n, seed) {
|
||||
var x = Math.sin(n * 127.1 + (seed || 1) * 311.7) * 43758.5453;
|
||||
return (x - Math.floor(x)) * 2 - 1;
|
||||
};
|
||||
window.hwBoil = function (tl, target, opts) {
|
||||
opts = opts || {};
|
||||
var amp = opts.amp !== undefined ? opts.amp : 1.6;
|
||||
var rot = opts.rot !== undefined ? opts.rot : 0.5;
|
||||
var fps = opts.fps || 30;
|
||||
var drop = opts.frameDrop || 3;
|
||||
var seed = opts.seed || 1;
|
||||
var els = gsap.utils.toArray(target);
|
||||
window.hwOnUpdate(tl, function () {
|
||||
var step = Math.floor((tl.time() * fps) / drop);
|
||||
for (var i = 0; i < els.length; i++) {
|
||||
gsap.set(els[i], {
|
||||
x: window.hwHash(step * 3 + i * 97, seed) * amp,
|
||||
y: window.hwHash(step * 3 + 1 + i * 97, seed) * amp,
|
||||
rotation: (opts.baseRot || 0) + window.hwHash(step * 3 + 2 + i * 97, seed) * rot,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// ---- hw-arrow helpers (copy these too) ----
|
||||
// Wobbled arrow path in a w x h box, tail at left, head at right.
|
||||
// curve: "straight" | "gentle" | "swoop"
|
||||
window.hwArrowPath = function (w, h, curve, seed) {
|
||||
seed = seed || 1;
|
||||
var bend = curve === "swoop" ? 0.55 : curve === "gentle" ? 0.28 : 0.06;
|
||||
function wob(v, n, range) {
|
||||
return v + window.hwHash(n, seed) * range;
|
||||
}
|
||||
var x0 = wob(4, 1, 3),
|
||||
y0 = wob(h * 0.72, 2, 4),
|
||||
x1 = wob(w - 10, 3, 4),
|
||||
y1 = wob(h * 0.3, 4, 4);
|
||||
var cx1 = wob(w * 0.35, 5, 10),
|
||||
cy1 = wob(h * (0.72 + bend * 0.5), 6, 8);
|
||||
var cx2 = wob(w * 0.7, 7, 10),
|
||||
cy2 = wob(h * (0.3 + bend), 8, 8);
|
||||
var ang = Math.atan2(y1 - cy2, x1 - cx2);
|
||||
function head(da, len) {
|
||||
var a = ang + Math.PI + da;
|
||||
return "M " + x1.toFixed(1) + " " + y1.toFixed(1) + " l " + (Math.cos(a) * len).toFixed(1) + " " + (Math.sin(a) * len).toFixed(1);
|
||||
}
|
||||
return (
|
||||
"M " + x0.toFixed(1) + " " + y0.toFixed(1) +
|
||||
" C " + cx1.toFixed(1) + " " + cy1.toFixed(1) + ", " + cx2.toFixed(1) + " " + cy2.toFixed(1) + ", " + x1.toFixed(1) + " " + y1.toFixed(1) +
|
||||
" " + head(0.5, wob(34, 9, 6)) + " " + head(-0.5, wob(34, 10, 6))
|
||||
);
|
||||
};
|
||||
window.hwArrowOn = function (tl, target, at, dur) {
|
||||
var p = document.querySelector(target + " path");
|
||||
var len = p.getTotalLength();
|
||||
gsap.set(p, { strokeDasharray: len, strokeDashoffset: len });
|
||||
tl.to(p, { strokeDashoffset: 0, duration: dur === undefined ? 0.7 : dur, ease: "power2.inOut" }, at);
|
||||
};
|
||||
window.hwArrowOff = function (tl, target, at) {
|
||||
tl.to(target, { opacity: 0, duration: 0.3, ease: "power2.in" }, at);
|
||||
};
|
||||
|
||||
// ---- self-preview ----
|
||||
document.getElementById("hw-ar-demo-path").setAttribute("d", window.hwArrowPath(380, 180, "swoop", 7));
|
||||
var tl = gsap.timeline({ paused: true });
|
||||
window.hwArrowOn(tl, "#hw-ar-demo", 0.4);
|
||||
window.hwBoil(tl, "#hw-ar-demo", { amp: 2, rot: 0.6, frameDrop: 3, seed: 7 });
|
||||
window.hwArrowOff(tl, "#hw-ar-demo", 3.4);
|
||||
tl.set("#hw-ar-root", { visibility: "hidden" }, 3.98);
|
||||
window.__timelines["hw-arrow"] = tl;
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
<!doctype html>
|
||||
<!-- Demo plate for the catalog preview. Identical to hw-boil.html except the
|
||||
root background, which is opaque here so the transparent overlay reads.
|
||||
Not installed by `hyperframes add` - registry-item.json lists hw-boil.html only. -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<style>
|
||||
/* Caveat is NOT auto-resolved by the renderer — bundle it. Asset
|
||||
paths are ROOT-RELATIVE (the project root is the base URL for all
|
||||
compositions; ../ traversal is a lint error). Registry installs
|
||||
ship the woff2 as an asset file targeting assets/fonts/. */
|
||||
@font-face {
|
||||
font-family: "Caveat";
|
||||
src: url("assets/fonts/Caveat-700-latin.woff2") format("woff2");
|
||||
font-weight: 700;
|
||||
font-display: block;
|
||||
}
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
background: #17161a;
|
||||
overflow: hidden;
|
||||
}
|
||||
/* ---- hw-boil -----------------------------------------------------
|
||||
The hand-drawn "boil": jitter that re-poses only every N frames
|
||||
(quake + frame-drop style). Pose is a pure function of quantized
|
||||
timeline time through a seeded hash — fully seek-safe, renders
|
||||
identically on every pass over the same frame.
|
||||
|
||||
RULES:
|
||||
- hwBoil owns x/y/rotation of the boiled element. Entrance/exit
|
||||
tweens go on a WRAPPER around it, never the boiled element.
|
||||
- One timeline has ONE onUpdate — hwOnUpdate is the dispatcher
|
||||
that lets the boil coexist with sweep scalars etc. Register
|
||||
every onUpdate-style render through it. */
|
||||
#hw-boil-root {
|
||||
--hw-font-print: "Caveat", cursive;
|
||||
--hw-font-script: "Caveat", cursive;
|
||||
--hw-ink: #f4f2ec;
|
||||
--hw-accent: #6d6dff;
|
||||
|
||||
position: relative;
|
||||
width: 1920px;
|
||||
height: 1080px;
|
||||
overflow: hidden;
|
||||
background: #17161a;
|
||||
font-family: var(--hw-font-print, "Caveat", cursive);
|
||||
}
|
||||
#hw-boil-root svg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
#hw-boil-root path {
|
||||
fill: none;
|
||||
stroke: var(--hw-ink);
|
||||
stroke-width: 6;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
#hw-b-arrow {
|
||||
stroke: var(--hw-accent);
|
||||
stroke-width: 7;
|
||||
}
|
||||
.hw-b-label {
|
||||
position: absolute;
|
||||
font-weight: 700;
|
||||
font-size: 84px;
|
||||
color: var(--hw-ink);
|
||||
white-space: nowrap;
|
||||
}
|
||||
.hw-b-tag {
|
||||
position: absolute;
|
||||
font-family: "Inter", ui-sans-serif, sans-serif;
|
||||
font-weight: 500;
|
||||
font-size: 22px;
|
||||
letter-spacing: 0.12em;
|
||||
color: rgba(244, 242, 236, 0.45);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Self-preview. WIRING: copy the hwOnUpdate / hwHash / hwBoil /
|
||||
hwWobbleEllipse helpers into your host script, then:
|
||||
|
||||
hwBoil(tl, "#hw-my-annotation", { amp: 1.6, rot: 0.5, frameDrop: 3, seed: 2 });
|
||||
|
||||
frameDrop 3-4 = classic boil; 1 = frantic; boil: off = don't call it. -->
|
||||
<div
|
||||
id="hw-boil-root"
|
||||
data-composition-id="hw-boil"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
>
|
||||
<svg viewBox="0 0 1920 1080">
|
||||
<g id="hw-b-circle-g"><path id="hw-b-circle"></path></g>
|
||||
<g id="hw-b-arrow-g">
|
||||
<path id="hw-b-arrow" d="M 640 640 C 760 700, 980 720, 1130 620 M 1130 620 l -64 -6 M 1130 620 l -18 58"></path>
|
||||
</g>
|
||||
</svg>
|
||||
<div class="hw-b-label" id="hw-b-word" style="left: 1230px; top: 560px">the boil</div>
|
||||
<div class="hw-b-tag" style="left: 350px; top: 782px">FRAME DROP 3</div>
|
||||
<div class="hw-b-tag" style="left: 700px; top: 782px">FRAME DROP 1</div>
|
||||
<div class="hw-b-tag" style="left: 1230px; top: 700px">TEXT, AMP 1.2</div>
|
||||
<div
|
||||
id="hw-b-drv"
|
||||
class="clip"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-track-index="0"
|
||||
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
|
||||
></div>
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
window.__timelines = window.__timelines || {};
|
||||
|
||||
// ---- copy these helpers into the host script ----
|
||||
|
||||
// Dispatcher: a GSAP timeline has ONE onUpdate. Register every
|
||||
// per-frame render (boils, sweep scalars) through this instead of
|
||||
// setting eventCallback directly, and they all coexist.
|
||||
window.hwOnUpdate = function (tl, fn) {
|
||||
if (!tl.__hwRenders) {
|
||||
tl.__hwRenders = [];
|
||||
tl.eventCallback("onUpdate", function () {
|
||||
for (var i = 0; i < tl.__hwRenders.length; i++) tl.__hwRenders[i]();
|
||||
});
|
||||
}
|
||||
tl.__hwRenders.push(fn);
|
||||
fn();
|
||||
};
|
||||
|
||||
// Seeded hash -> [-1, 1]. Pure function of n — no Math.random().
|
||||
window.hwHash = function (n, seed) {
|
||||
var x = Math.sin(n * 127.1 + (seed || 1) * 311.7) * 43758.5453;
|
||||
return (x - Math.floor(x)) * 2 - 1;
|
||||
};
|
||||
|
||||
// The boil. Owns x/y/rotation of its targets — put entrance tweens
|
||||
// on a wrapper. frameDrop = re-pose every N frames (3-4 classic).
|
||||
window.hwBoil = function (tl, target, opts) {
|
||||
opts = opts || {};
|
||||
var amp = opts.amp !== undefined ? opts.amp : 1.6;
|
||||
var rot = opts.rot !== undefined ? opts.rot : 0.5;
|
||||
var fps = opts.fps || 30;
|
||||
var drop = opts.frameDrop || 3;
|
||||
var seed = opts.seed || 1;
|
||||
var els = gsap.utils.toArray(target);
|
||||
window.hwOnUpdate(tl, function () {
|
||||
var step = Math.floor((tl.time() * fps) / drop);
|
||||
for (var i = 0; i < els.length; i++) {
|
||||
gsap.set(els[i], {
|
||||
x: window.hwHash(step * 3 + i * 97, seed) * amp,
|
||||
y: window.hwHash(step * 3 + 1 + i * 97, seed) * amp,
|
||||
rotation: (opts.baseRot || 0) + window.hwHash(step * 3 + 2 + i * 97, seed) * rot,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// Seeded hand-wobbled ellipse path (smooth quadratic chain).
|
||||
window.hwWobbleEllipse = function (cx, cy, rx, ry, seed, wobblePct) {
|
||||
wobblePct = wobblePct === undefined ? 3 : wobblePct;
|
||||
var N = 14,
|
||||
pts = [];
|
||||
for (var i = 0; i < N; i++) {
|
||||
var a = (i / N) * Math.PI * 2;
|
||||
var wr = 1 + window.hwHash(i * 13 + 5, seed) * (wobblePct / 100);
|
||||
pts.push([cx + Math.cos(a) * rx * wr, cy + Math.sin(a) * ry * wr]);
|
||||
}
|
||||
var mx = (pts[0][0] + pts[N - 1][0]) / 2,
|
||||
my = (pts[0][1] + pts[N - 1][1]) / 2;
|
||||
var d = "M" + mx.toFixed(1) + " " + my.toFixed(1);
|
||||
for (var j = 0; j < N; j++) {
|
||||
var p = pts[j],
|
||||
q = pts[(j + 1) % N];
|
||||
d += " Q" + p[0].toFixed(1) + " " + p[1].toFixed(1) + " " + ((p[0] + q[0]) / 2).toFixed(1) + " " + ((p[1] + q[1]) / 2).toFixed(1);
|
||||
}
|
||||
return d;
|
||||
};
|
||||
|
||||
// ---- self-preview (not part of the snippet you copy) ----
|
||||
var circle = document.getElementById("hw-b-circle");
|
||||
circle.setAttribute("d", window.hwWobbleEllipse(460, 620, 190, 130, 4, 3.2));
|
||||
|
||||
var tl = gsap.timeline({ paused: true });
|
||||
|
||||
/* draw-on entrances (strokeDashoffset — never x/y, the boil owns those) */
|
||||
[circle, document.getElementById("hw-b-arrow")].forEach(function (p, i) {
|
||||
var len = p.getTotalLength();
|
||||
gsap.set(p, { strokeDasharray: len, strokeDashoffset: len });
|
||||
tl.to(p, { strokeDashoffset: 0, duration: 0.8, ease: "power2.inOut" }, 0.3 + i * 0.35);
|
||||
});
|
||||
gsap.set("#hw-b-word", { opacity: 0 });
|
||||
tl.to("#hw-b-word", { opacity: 1, duration: 0.5, ease: "power2.out" }, 1.1);
|
||||
|
||||
/* three boils with different characters, one dispatcher */
|
||||
window.hwBoil(tl, "#hw-b-circle-g", { amp: 2.2, rot: 0.4, frameDrop: 3, seed: 2 });
|
||||
window.hwBoil(tl, "#hw-b-arrow-g", { amp: 2.6, rot: 0.7, frameDrop: 1, seed: 5 });
|
||||
window.hwBoil(tl, "#hw-b-word", { amp: 1.2, rot: 0.35, frameDrop: 3, seed: 9 });
|
||||
|
||||
tl.set("#hw-boil-root", { visibility: "hidden" }, 3.98);
|
||||
window.__timelines["hw-boil"] = tl;
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+198
@@ -0,0 +1,198 @@
|
||||
<!doctype html>
|
||||
<!-- Demo plate for the catalog preview. Identical to hw-box-label.html except the
|
||||
root background, which is opaque here so the transparent overlay reads.
|
||||
Not installed by `hyperframes add` - registry-item.json lists hw-box-label.html only. -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: "Caveat";
|
||||
src: url("assets/fonts/Caveat-700-latin.woff2") format("woff2");
|
||||
font-weight: 700;
|
||||
font-display: block;
|
||||
}
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
background: #17161a;
|
||||
overflow: hidden;
|
||||
}
|
||||
/* ---- hw-box-label ----------------------------------------------------
|
||||
Rounded hand-drawn box + marker label (map-label style annotations).
|
||||
Requires the hw-boil helpers in the host. */
|
||||
.hw-box {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
.hw-box svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: visible;
|
||||
}
|
||||
.hw-box path {
|
||||
fill: none;
|
||||
stroke: var(--hw-box-ink, var(--hw-ink, #f4f2ec));
|
||||
stroke-width: var(--hw-box-w, 6);
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
.hw-box .hw-bx-label {
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
font-family: var(--hw-font-print, "Caveat", cursive);
|
||||
font-weight: 700;
|
||||
font-size: 52px;
|
||||
color: var(--hw-box-ink, var(--hw-ink, #f4f2ec));
|
||||
white-space: nowrap;
|
||||
opacity: 0;
|
||||
text-shadow: 0 2px 14px rgba(0, 0, 0, 0.4);
|
||||
}
|
||||
/* self-preview scene */
|
||||
#hw-bx-scene {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(140deg, #4a3560 0%, #1c1423 100%);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Self-preview. WIRING: copy the .hw-box CSS + markup pattern, the
|
||||
hwWobbleRect / hwBoxBuild / hwBoxOn / hwBoxOff helpers, AND the
|
||||
hw-boil helpers. Size the wrapper over your target, then:
|
||||
|
||||
hwBoxBuild("#hw-my-box", { seed: 3, label: "Europe", labelPos: "above" });
|
||||
hwBoxOn(tl, "#hw-my-box", 2.0);
|
||||
hwBoil(tl, "#hw-my-box", { frameDrop: 3, seed: 3 });
|
||||
hwBoxOff(tl, "#hw-my-box", 8.0);
|
||||
-->
|
||||
<div
|
||||
id="hw-bx-root"
|
||||
data-composition-id="hw-box-label"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
>
|
||||
<div id="hw-bx-scene"></div>
|
||||
<div class="hw-box" id="hw-bx-demo" style="left: 700px; top: 420px; width: 420px; height: 230px">
|
||||
<svg viewBox="0 0 420 230"><path id="hw-bx-demo-path"></path></svg>
|
||||
<div class="hw-bx-label" id="hw-bx-demo-label"></div>
|
||||
</div>
|
||||
<div
|
||||
id="hw-bx-drv"
|
||||
class="clip"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-track-index="0"
|
||||
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
|
||||
></div>
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
window.__timelines = window.__timelines || {};
|
||||
|
||||
/* ---- hw-boil helpers (shared family runtime — copy once per host) ---- */
|
||||
window.hwOnUpdate = function (tl, fn) {
|
||||
if (!tl.__hwRenders) {
|
||||
tl.__hwRenders = [];
|
||||
tl.eventCallback("onUpdate", function () {
|
||||
for (var i = 0; i < tl.__hwRenders.length; i++) tl.__hwRenders[i]();
|
||||
});
|
||||
}
|
||||
tl.__hwRenders.push(fn);
|
||||
fn();
|
||||
};
|
||||
window.hwHash = function (n, seed) {
|
||||
var x = Math.sin(n * 127.1 + (seed || 1) * 311.7) * 43758.5453;
|
||||
return (x - Math.floor(x)) * 2 - 1;
|
||||
};
|
||||
window.hwBoil = function (tl, target, opts) {
|
||||
opts = opts || {};
|
||||
var amp = opts.amp !== undefined ? opts.amp : 1.6;
|
||||
var rot = opts.rot !== undefined ? opts.rot : 0.5;
|
||||
var fps = opts.fps || 30;
|
||||
var drop = opts.frameDrop || 3;
|
||||
var seed = opts.seed || 1;
|
||||
var els = gsap.utils.toArray(target);
|
||||
window.hwOnUpdate(tl, function () {
|
||||
var step = Math.floor((tl.time() * fps) / drop);
|
||||
for (var i = 0; i < els.length; i++) {
|
||||
gsap.set(els[i], {
|
||||
x: window.hwHash(step * 3 + i * 97, seed) * amp,
|
||||
y: window.hwHash(step * 3 + 1 + i * 97, seed) * amp,
|
||||
rotation: (opts.baseRot || 0) + window.hwHash(step * 3 + 2 + i * 97, seed) * rot,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// ---- hw-box helpers (copy these too) ----
|
||||
// Wobbled rounded-rect path in a w x h box.
|
||||
window.hwWobbleRect = function (w, h, r, seed, amp) {
|
||||
amp = amp === undefined ? 3.5 : amp;
|
||||
function wob(n) {
|
||||
return window.hwHash(n, seed) * amp;
|
||||
}
|
||||
var d = "M " + (r + wob(1)) + " " + wob(2);
|
||||
d += " L " + (w / 2 + wob(3)) + " " + wob(4);
|
||||
d += " L " + (w - r + wob(5)) + " " + wob(6);
|
||||
d += " Q " + (w + wob(7)) + " " + wob(8) + " " + (w + wob(9)) + " " + (r + wob(10));
|
||||
d += " L " + (w + wob(11)) + " " + (h / 2 + wob(12));
|
||||
d += " L " + (w + wob(13)) + " " + (h - r + wob(14));
|
||||
d += " Q " + (w + wob(15)) + " " + (h + wob(16)) + " " + (w - r + wob(17)) + " " + (h + wob(18));
|
||||
d += " L " + (w / 2 + wob(19)) + " " + (h + wob(20));
|
||||
d += " L " + (r + wob(21)) + " " + (h + wob(22));
|
||||
d += " Q " + wob(23) + " " + (h + wob(24)) + " " + wob(25) + " " + (h - r + wob(26));
|
||||
d += " L " + wob(27) + " " + (h / 2 + wob(28));
|
||||
d += " L " + wob(29) + " " + (r + wob(30));
|
||||
d += " Q " + wob(31) + " " + wob(32) + " " + (r + wob(33)) + " " + wob(34);
|
||||
return d;
|
||||
};
|
||||
window.hwBoxBuild = function (target, opts) {
|
||||
opts = opts || {};
|
||||
var el = document.querySelector(target);
|
||||
var svg = el.querySelector("svg");
|
||||
var vb = svg.getAttribute("viewBox").split(" ");
|
||||
var W = parseFloat(vb[2]),
|
||||
H = parseFloat(vb[3]);
|
||||
el.querySelector("path").setAttribute("d", window.hwWobbleRect(W, H, Math.min(26, W * 0.1), opts.seed || 1));
|
||||
var label = el.querySelector(".hw-bx-label");
|
||||
label.textContent = opts.label || "";
|
||||
if ((opts.labelPos || "above") === "above") {
|
||||
label.style.top = "-64px";
|
||||
} else {
|
||||
label.style.top = "50%";
|
||||
label.style.transform = "translateY(-50%)";
|
||||
}
|
||||
};
|
||||
window.hwBoxOn = function (tl, target, at) {
|
||||
var p = document.querySelector(target + " path");
|
||||
var len = p.getTotalLength();
|
||||
gsap.set(p, { strokeDasharray: len, strokeDashoffset: len });
|
||||
tl.to(p, { strokeDashoffset: 0, duration: 0.75, ease: "power2.inOut" }, at);
|
||||
tl.to(document.querySelector(target + " .hw-bx-label"), { opacity: 1, duration: 0.4, ease: "power2.out" }, at + 0.55);
|
||||
};
|
||||
window.hwBoxOff = function (tl, target, at) {
|
||||
tl.to(target, { opacity: 0, duration: 0.35, ease: "power2.in" }, at);
|
||||
};
|
||||
|
||||
// ---- self-preview ----
|
||||
window.hwBoxBuild("#hw-bx-demo", { seed: 3, label: "Europe", labelPos: "above" });
|
||||
var tl = gsap.timeline({ paused: true });
|
||||
window.hwBoxOn(tl, "#hw-bx-demo", 0.4);
|
||||
window.hwBoil(tl, "#hw-bx-demo", { amp: 1.8, rot: 0.4, frameDrop: 3, seed: 3 });
|
||||
window.hwBoxOff(tl, "#hw-bx-demo", 3.4);
|
||||
tl.set("#hw-bx-root", { visibility: "hidden" }, 3.98);
|
||||
window.__timelines["hw-box-label"] = tl;
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+277
@@ -0,0 +1,277 @@
|
||||
<!doctype html>
|
||||
<!-- Demo plate for the catalog preview. Identical to hw-callout-circle.html except the
|
||||
root background, which is opaque here so the transparent overlay reads.
|
||||
Not installed by `hyperframes add` - registry-item.json lists hw-callout-circle.html only. -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: "Caveat";
|
||||
src: url("assets/fonts/Caveat-700-latin.woff2") format("woff2");
|
||||
font-weight: 700;
|
||||
font-display: block;
|
||||
}
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
background: #17161a;
|
||||
overflow: hidden;
|
||||
}
|
||||
/* ---- hw-callout-circle ----------------------------------------------
|
||||
The full callout anatomy: wobbled ellipse outline draws around a
|
||||
target, optional scribble fill sketches in, a curved connector leads
|
||||
to a handwritten label. Requires the hw-boil helpers in the host. */
|
||||
.hw-callout {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
.hw-callout svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: visible;
|
||||
}
|
||||
.hw-callout path {
|
||||
fill: none;
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
.hw-callout .hw-co-outline {
|
||||
stroke: var(--hw-co-ink, var(--hw-ink, #f4f2ec));
|
||||
stroke-width: var(--hw-co-w, 6);
|
||||
}
|
||||
.hw-callout .hw-co-scribble {
|
||||
stroke: var(--hw-co-accent, var(--hw-accent, #6d6dff));
|
||||
stroke-width: 4;
|
||||
opacity: 0.7;
|
||||
}
|
||||
.hw-callout .hw-co-connector {
|
||||
stroke: var(--hw-co-ink, var(--hw-ink, #f4f2ec));
|
||||
stroke-width: 4;
|
||||
}
|
||||
.hw-callout .hw-co-label {
|
||||
position: absolute;
|
||||
font-family: var(--hw-font-print, "Caveat", cursive);
|
||||
font-weight: 700;
|
||||
font-size: 54px;
|
||||
color: var(--hw-co-ink, var(--hw-ink, #f4f2ec));
|
||||
white-space: nowrap;
|
||||
opacity: 0;
|
||||
text-shadow: 0 2px 14px rgba(0, 0, 0, 0.45);
|
||||
}
|
||||
/* self-preview scene */
|
||||
#hw-co-scene {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(150deg, #35604a 0%, #14231c 100%);
|
||||
}
|
||||
#hw-co-target {
|
||||
position: absolute;
|
||||
left: 700px;
|
||||
top: 420px;
|
||||
width: 220px;
|
||||
height: 150px;
|
||||
border-radius: 18px;
|
||||
background: rgba(244, 242, 236, 0.18);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: "Inter", ui-sans-serif, sans-serif;
|
||||
font-weight: 600;
|
||||
font-size: 30px;
|
||||
color: rgba(244, 242, 236, 0.8);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Self-preview. WIRING: copy the .hw-callout CSS + markup pattern, the
|
||||
hwCalloutBuild / hwCalloutOn / hwCalloutOff helpers, AND the hw-boil
|
||||
helpers. Size the wrapper around your target, then:
|
||||
|
||||
hwCalloutBuild("#hw-my-callout", { scribble: true, seed: 4,
|
||||
label: "look here!", labelAt: "right" });
|
||||
hwCalloutOn(tl, "#hw-my-callout", 2.0);
|
||||
hwBoil(tl, "#hw-my-callout", { frameDrop: 3, seed: 4 });
|
||||
hwCalloutOff(tl, "#hw-my-callout", 8.0);
|
||||
-->
|
||||
<div
|
||||
id="hw-co-root"
|
||||
data-composition-id="hw-callout-circle"
|
||||
data-start="0"
|
||||
data-duration="5"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
>
|
||||
<div id="hw-co-scene"></div>
|
||||
<div id="hw-co-target">subject</div>
|
||||
<div class="hw-callout" id="hw-co-demo" style="left: 640px; top: 370px; width: 340px; height: 250px">
|
||||
<svg viewBox="0 0 340 250">
|
||||
<path class="hw-co-scribble" id="hw-co-demo-scribble"></path>
|
||||
<path class="hw-co-outline" id="hw-co-demo-outline"></path>
|
||||
<path class="hw-co-connector" id="hw-co-demo-connector"></path>
|
||||
</svg>
|
||||
<div class="hw-co-label" id="hw-co-demo-label"></div>
|
||||
</div>
|
||||
<div
|
||||
id="hw-co-drv"
|
||||
class="clip"
|
||||
data-start="0"
|
||||
data-duration="5"
|
||||
data-track-index="0"
|
||||
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
|
||||
></div>
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
window.__timelines = window.__timelines || {};
|
||||
|
||||
/* ---- hw-boil helpers (shared family runtime — copy once per host) ---- */
|
||||
window.hwOnUpdate = function (tl, fn) {
|
||||
if (!tl.__hwRenders) {
|
||||
tl.__hwRenders = [];
|
||||
tl.eventCallback("onUpdate", function () {
|
||||
for (var i = 0; i < tl.__hwRenders.length; i++) tl.__hwRenders[i]();
|
||||
});
|
||||
}
|
||||
tl.__hwRenders.push(fn);
|
||||
fn();
|
||||
};
|
||||
window.hwHash = function (n, seed) {
|
||||
var x = Math.sin(n * 127.1 + (seed || 1) * 311.7) * 43758.5453;
|
||||
return (x - Math.floor(x)) * 2 - 1;
|
||||
};
|
||||
window.hwBoil = function (tl, target, opts) {
|
||||
opts = opts || {};
|
||||
var amp = opts.amp !== undefined ? opts.amp : 1.6;
|
||||
var rot = opts.rot !== undefined ? opts.rot : 0.5;
|
||||
var fps = opts.fps || 30;
|
||||
var drop = opts.frameDrop || 3;
|
||||
var seed = opts.seed || 1;
|
||||
var els = gsap.utils.toArray(target);
|
||||
window.hwOnUpdate(tl, function () {
|
||||
var step = Math.floor((tl.time() * fps) / drop);
|
||||
for (var i = 0; i < els.length; i++) {
|
||||
gsap.set(els[i], {
|
||||
x: window.hwHash(step * 3 + i * 97, seed) * amp,
|
||||
y: window.hwHash(step * 3 + 1 + i * 97, seed) * amp,
|
||||
rotation: (opts.baseRot || 0) + window.hwHash(step * 3 + 2 + i * 97, seed) * rot,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
window.hwWobbleEllipse = function (cx, cy, rx, ry, seed, wobblePct) {
|
||||
wobblePct = wobblePct === undefined ? 3 : wobblePct;
|
||||
var N = 14,
|
||||
pts = [];
|
||||
for (var i = 0; i < N; i++) {
|
||||
var a = (i / N) * Math.PI * 2;
|
||||
var wr = 1 + window.hwHash(i * 13 + 5, seed) * (wobblePct / 100);
|
||||
pts.push([cx + Math.cos(a) * rx * wr, cy + Math.sin(a) * ry * wr]);
|
||||
}
|
||||
var mx = (pts[0][0] + pts[N - 1][0]) / 2,
|
||||
my = (pts[0][1] + pts[N - 1][1]) / 2;
|
||||
var d = "M" + mx.toFixed(1) + " " + my.toFixed(1);
|
||||
for (var j = 0; j < N; j++) {
|
||||
var p = pts[j],
|
||||
q = pts[(j + 1) % N];
|
||||
d += " Q" + p[0].toFixed(1) + " " + p[1].toFixed(1) + " " + ((p[0] + q[0]) / 2).toFixed(1) + " " + ((p[1] + q[1]) / 2).toFixed(1);
|
||||
}
|
||||
return d;
|
||||
};
|
||||
|
||||
// ---- hw-callout helpers (copy these too) ----
|
||||
// Builds outline + scribble + connector + label inside the wrapper.
|
||||
// opts: scribble (bool), seed, label, labelAt ("right"|"left"|"below"),
|
||||
// connector (bool)
|
||||
window.hwCalloutBuild = function (target, opts) {
|
||||
opts = opts || {};
|
||||
var seed = opts.seed || 1;
|
||||
var el = document.querySelector(target);
|
||||
var svg = el.querySelector("svg");
|
||||
var vb = svg.getAttribute("viewBox").split(" ");
|
||||
var W = parseFloat(vb[2]),
|
||||
H = parseFloat(vb[3]);
|
||||
var cx = W / 2,
|
||||
cy = H / 2,
|
||||
rx = W * 0.42,
|
||||
ry = H * 0.36;
|
||||
|
||||
el.querySelector(".hw-co-outline").setAttribute("d", window.hwWobbleEllipse(cx, cy, rx, ry, seed, 4));
|
||||
|
||||
var scr = el.querySelector(".hw-co-scribble");
|
||||
if (opts.scribble === false) {
|
||||
scr.style.display = "none";
|
||||
} else {
|
||||
var d = "",
|
||||
rows = 6;
|
||||
for (var i = 0; i < rows; i++) {
|
||||
var t = (i + 0.5) / rows;
|
||||
var yy = cy - ry + t * ry * 2;
|
||||
var half = Math.sqrt(Math.max(0.05, 1 - Math.pow((yy - cy) / ry, 2)));
|
||||
var xw = rx * half * 0.86;
|
||||
var xa = cx - xw + window.hwHash(i * 7 + 2, seed) * 8;
|
||||
var xb = cx + xw + window.hwHash(i * 7 + 3, seed) * 8;
|
||||
d += (i === 0 ? "M " : " L ") + (i % 2 ? xa : xb).toFixed(1) + " " + yy.toFixed(1);
|
||||
d += " L " + (i % 2 ? xb : xa).toFixed(1) + " " + (yy + window.hwHash(i * 7 + 4, seed) * 4).toFixed(1);
|
||||
}
|
||||
scr.setAttribute("d", d);
|
||||
}
|
||||
|
||||
var conn = el.querySelector(".hw-co-connector");
|
||||
var label = el.querySelector(".hw-co-label");
|
||||
if (opts.connector === false) {
|
||||
conn.style.display = "none";
|
||||
} else {
|
||||
var at = opts.labelAt || "right";
|
||||
var sx = at === "left" ? cx - rx : at === "below" ? cx : cx + rx;
|
||||
var sy = at === "below" ? cy + ry : cy + ry * 0.35;
|
||||
var ex = at === "left" ? sx - 70 : at === "below" ? sx + 40 : sx + 70;
|
||||
var ey = sy + 55;
|
||||
conn.setAttribute(
|
||||
"d",
|
||||
"M " + sx.toFixed(1) + " " + sy.toFixed(1) + " Q " + ((sx + ex) / 2 + window.hwHash(21, seed) * 14).toFixed(1) + " " + (sy + 46).toFixed(1) + " " + ex.toFixed(1) + " " + ey.toFixed(1)
|
||||
);
|
||||
label.style.left = (at === "left" ? ex - 180 : ex + 12) + "px";
|
||||
label.style.top = ey - 24 + "px";
|
||||
}
|
||||
label.textContent = opts.label || "";
|
||||
};
|
||||
// Draw sequence: outline -> scribble -> connector -> label pop.
|
||||
window.hwCalloutOn = function (tl, target, at) {
|
||||
var el = document.querySelector(target);
|
||||
var t = at;
|
||||
["outline", "scribble", "connector"].forEach(function (k, idx) {
|
||||
var p = el.querySelector(".hw-co-" + k);
|
||||
if (!p || p.style.display === "none") return;
|
||||
var len = p.getTotalLength();
|
||||
gsap.set(p, { strokeDasharray: len, strokeDashoffset: len });
|
||||
var dur = k === "scribble" ? 0.55 : k === "connector" ? 0.35 : 0.7;
|
||||
tl.to(p, { strokeDashoffset: 0, duration: dur, ease: "power2.inOut" }, t);
|
||||
t += dur * 0.85;
|
||||
});
|
||||
var label = el.querySelector(".hw-co-label");
|
||||
gsap.set(label, { scale: 0.8, transformOrigin: "0% 50%" });
|
||||
tl.to(label, { opacity: 1, scale: 1, duration: 0.4, ease: "back.out(1.6)" }, t);
|
||||
};
|
||||
window.hwCalloutOff = function (tl, target, at) {
|
||||
tl.to(target, { opacity: 0, duration: 0.35, ease: "power2.in" }, at);
|
||||
};
|
||||
|
||||
// ---- self-preview ----
|
||||
window.hwCalloutBuild("#hw-co-demo", { scribble: true, seed: 4, label: "look here!", labelAt: "right" });
|
||||
var tl = gsap.timeline({ paused: true });
|
||||
window.hwCalloutOn(tl, "#hw-co-demo", 0.4);
|
||||
window.hwBoil(tl, "#hw-co-demo", { amp: 1.8, rot: 0.4, frameDrop: 3, seed: 4 });
|
||||
window.hwCalloutOff(tl, "#hw-co-demo", 4.3);
|
||||
tl.set("#hw-co-root", { visibility: "hidden" }, 4.98);
|
||||
window.__timelines["hw-callout-circle"] = tl;
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+203
@@ -0,0 +1,203 @@
|
||||
<!doctype html>
|
||||
<!-- Demo plate for the catalog preview. Identical to hw-underline.html except the
|
||||
root background, which is opaque here so the transparent overlay reads.
|
||||
Not installed by `hyperframes add` - registry-item.json lists hw-underline.html only. -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: "Caveat";
|
||||
src: url("assets/fonts/Caveat-700-latin.woff2") format("woff2");
|
||||
font-weight: 700;
|
||||
font-display: block;
|
||||
}
|
||||
*,
|
||||
*::before,
|
||||
*::after {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
body {
|
||||
background: #17161a;
|
||||
overflow: hidden;
|
||||
}
|
||||
/* ---- hw-underline ------------------------------------------------
|
||||
Squiggle underline / strikethrough / bracket marks that draw on.
|
||||
Size the wrapper over the text they annotate. Requires the hw-boil
|
||||
helpers in the host. */
|
||||
.hw-mark {
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
.hw-mark svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: visible;
|
||||
}
|
||||
.hw-mark path {
|
||||
fill: none;
|
||||
stroke: var(--hw-mark-color, var(--hw-accent-warm, #ffb020));
|
||||
stroke-width: var(--hw-mark-w, 7);
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
/* self-preview scene */
|
||||
#hw-un-scene {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: #17161a;
|
||||
font-family: var(--hw-font-print, "Caveat", cursive);
|
||||
font-weight: 700;
|
||||
font-size: 84px;
|
||||
color: #f4f2ec;
|
||||
}
|
||||
#hw-un-scene div {
|
||||
position: absolute;
|
||||
left: 480px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Self-preview. WIRING: copy the .hw-mark CSS + markup pattern, the
|
||||
hwMarkPath / hwMarkOn / hwMarkOff helpers, AND the hw-boil helpers.
|
||||
Then:
|
||||
|
||||
document.querySelector("#hw-my-mark path")
|
||||
.setAttribute("d", hwMarkPath(420, 34, "underline", 4));
|
||||
hwMarkOn(tl, "#hw-my-mark", 2.0);
|
||||
hwBoil(tl, "#hw-my-mark", { frameDrop: 3, seed: 4 });
|
||||
-->
|
||||
<div
|
||||
id="hw-un-root"
|
||||
data-composition-id="hw-underline"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
>
|
||||
<div id="hw-un-scene">
|
||||
<div style="top: 260px">underline the point</div>
|
||||
<div style="top: 460px">cross this out</div>
|
||||
<div style="top: 660px">bracket the aside</div>
|
||||
</div>
|
||||
<div class="hw-mark" id="hw-un-m1" style="left: 470px; top: 350px; width: 620px; height: 34px">
|
||||
<svg viewBox="0 0 620 34"><path></path></svg>
|
||||
</div>
|
||||
<div class="hw-mark" id="hw-un-m2" style="left: 470px; top: 500px; width: 480px; height: 34px">
|
||||
<svg viewBox="0 0 480 34"><path></path></svg>
|
||||
</div>
|
||||
<div class="hw-mark" id="hw-un-m3" style="left: 440px; top: 650px; width: 40px; height: 110px">
|
||||
<svg viewBox="0 0 40 110"><path></path></svg>
|
||||
</div>
|
||||
<div
|
||||
id="hw-un-drv"
|
||||
class="clip"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-track-index="0"
|
||||
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
|
||||
></div>
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
window.__timelines = window.__timelines || {};
|
||||
|
||||
/* ---- hw-boil helpers (shared family runtime — copy once per host) ---- */
|
||||
window.hwOnUpdate = function (tl, fn) {
|
||||
if (!tl.__hwRenders) {
|
||||
tl.__hwRenders = [];
|
||||
tl.eventCallback("onUpdate", function () {
|
||||
for (var i = 0; i < tl.__hwRenders.length; i++) tl.__hwRenders[i]();
|
||||
});
|
||||
}
|
||||
tl.__hwRenders.push(fn);
|
||||
fn();
|
||||
};
|
||||
window.hwHash = function (n, seed) {
|
||||
var x = Math.sin(n * 127.1 + (seed || 1) * 311.7) * 43758.5453;
|
||||
return (x - Math.floor(x)) * 2 - 1;
|
||||
};
|
||||
window.hwBoil = function (tl, target, opts) {
|
||||
opts = opts || {};
|
||||
var amp = opts.amp !== undefined ? opts.amp : 1.6;
|
||||
var rot = opts.rot !== undefined ? opts.rot : 0.5;
|
||||
var fps = opts.fps || 30;
|
||||
var drop = opts.frameDrop || 3;
|
||||
var seed = opts.seed || 1;
|
||||
var els = gsap.utils.toArray(target);
|
||||
window.hwOnUpdate(tl, function () {
|
||||
var step = Math.floor((tl.time() * fps) / drop);
|
||||
for (var i = 0; i < els.length; i++) {
|
||||
gsap.set(els[i], {
|
||||
x: window.hwHash(step * 3 + i * 97, seed) * amp,
|
||||
y: window.hwHash(step * 3 + 1 + i * 97, seed) * amp,
|
||||
rotation: (opts.baseRot || 0) + window.hwHash(step * 3 + 2 + i * 97, seed) * rot,
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// ---- hw-mark helpers (copy these too) ----
|
||||
// style: "underline" (squiggle) | "strike" (double pass) | "bracket"
|
||||
window.hwMarkPath = function (w, h, style, seed) {
|
||||
seed = seed || 1;
|
||||
function wob(n, range) {
|
||||
return window.hwHash(n, seed) * (range === undefined ? 5 : range);
|
||||
}
|
||||
var mid = h / 2;
|
||||
if (style === "bracket") {
|
||||
return (
|
||||
"M " + (w * 0.9 + wob(1, 3)) + " " + (2 + wob(2, 3)) +
|
||||
" Q " + wob(3, 4) + " " + (2 + wob(4)) + " " + (4 + wob(5, 3)) + " " + (h * 0.5 + wob(6)) +
|
||||
" Q " + wob(7, 4) + " " + (h - 2 + wob(8)) + " " + (w * 0.9 + wob(9, 3)) + " " + (h - 2 + wob(10, 3))
|
||||
);
|
||||
}
|
||||
if (style === "strike") {
|
||||
return (
|
||||
"M " + (2 + wob(1, 3)) + " " + (mid + wob(2)) +
|
||||
" L " + (w - 4 + wob(3, 3)) + " " + (mid - 3 + wob(4)) +
|
||||
" M " + (w - 10 + wob(5, 4)) + " " + (mid + 7 + wob(6)) +
|
||||
" L " + (8 + wob(7, 4)) + " " + (mid + 3 + wob(8))
|
||||
);
|
||||
}
|
||||
/* underline squiggle */
|
||||
var segs = Math.max(5, Math.round(w / 95));
|
||||
var d = "M 2 " + (mid + wob(0, 4));
|
||||
for (var i = 1; i <= segs; i++) {
|
||||
var x = (w / segs) * i;
|
||||
d +=
|
||||
" Q " + (x - w / segs / 2 + wob(i * 4 + 1, 9)) + " " + (mid + (i % 2 ? -1 : 1) * (9 + wob(i * 4 + 2, 4))) +
|
||||
" " + x.toFixed(1) + " " + (mid + wob(i * 4 + 3, 4));
|
||||
}
|
||||
return d;
|
||||
};
|
||||
window.hwMarkOn = function (tl, target, at, dur) {
|
||||
var p = document.querySelector(target + " path");
|
||||
var len = p.getTotalLength();
|
||||
gsap.set(p, { strokeDasharray: len, strokeDashoffset: len });
|
||||
tl.to(p, { strokeDashoffset: 0, duration: dur === undefined ? 0.55 : dur, ease: "power2.inOut" }, at);
|
||||
};
|
||||
window.hwMarkOff = function (tl, target, at) {
|
||||
tl.to(target, { opacity: 0, duration: 0.3, ease: "power2.in" }, at);
|
||||
};
|
||||
|
||||
// ---- self-preview ----
|
||||
document.querySelector("#hw-un-m1 path").setAttribute("d", window.hwMarkPath(620, 34, "underline", 4));
|
||||
document.querySelector("#hw-un-m2 path").setAttribute("d", window.hwMarkPath(480, 34, "strike", 6));
|
||||
document.querySelector("#hw-un-m3 path").setAttribute("d", window.hwMarkPath(40, 110, "bracket", 8));
|
||||
var tl = gsap.timeline({ paused: true });
|
||||
window.hwMarkOn(tl, "#hw-un-m1", 0.4);
|
||||
window.hwMarkOn(tl, "#hw-un-m2", 1.0);
|
||||
window.hwMarkOn(tl, "#hw-un-m3", 1.6);
|
||||
window.hwBoil(tl, [".hw-mark"], { amp: 1.5, rot: 0.4, frameDrop: 3, seed: 5 });
|
||||
tl.to([".hw-mark"], { opacity: 0, duration: 0.3, ease: "power2.in" }, 3.5);
|
||||
tl.set("#hw-un-root", { visibility: "hidden" }, 3.98);
|
||||
window.__timelines["hw-underline"] = tl;
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,135 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<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;
|
||||
}
|
||||
body {
|
||||
background: transparent;
|
||||
overflow: hidden;
|
||||
}
|
||||
/* ---- mk-cta-button ---------------------------------------------------
|
||||
Pill CTA ("Buy", "Subscribe"). Set --mk-cta-bg/--mk-cta-fg to recolor
|
||||
(any css color); add .mk-cta--framed for the outlined ring
|
||||
with drop shadow, .mk-cta--frost for frosted glass over footage. */
|
||||
.mk-cta {
|
||||
--mk-font: "Inter", -apple-system, sans-serif;
|
||||
--mk-ink: #1d1d1f;
|
||||
--mk-accent: #0071e3;
|
||||
--mk-cta-bg: var(--mk-accent);
|
||||
--mk-cta-fg: #ffffff;
|
||||
--mk-cta-frame: rgba(29, 29, 31, 0.22);
|
||||
--mk-cta-arrow-w: 3.5;
|
||||
--mk-shadow: 0 12px 36px rgba(0, 0, 0, 0.14);
|
||||
|
||||
position: absolute;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 20px;
|
||||
padding: 24px 48px;
|
||||
border-radius: 999px;
|
||||
font-family: var(--mk-font);
|
||||
font-weight: 600;
|
||||
font-size: 42px;
|
||||
letter-spacing: -0.01em;
|
||||
white-space: nowrap;
|
||||
background: var(--mk-cta-bg);
|
||||
color: var(--mk-cta-fg);
|
||||
pointer-events: none;
|
||||
}
|
||||
.mk-cta--framed::before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
inset: -16px;
|
||||
border: 2px solid var(--mk-cta-frame);
|
||||
border-radius: 999px;
|
||||
box-shadow: var(--mk-shadow);
|
||||
}
|
||||
.mk-cta--frost {
|
||||
background: rgba(255, 255, 255, 0.42);
|
||||
color: var(--mk-ink);
|
||||
backdrop-filter: blur(24px) saturate(1.4);
|
||||
-webkit-backdrop-filter: blur(24px) saturate(1.4);
|
||||
box-shadow: var(--mk-shadow);
|
||||
}
|
||||
.mk-cta-arrow {
|
||||
display: inline-flex;
|
||||
width: 0.62em;
|
||||
}
|
||||
.mk-cta-arrow svg {
|
||||
width: 100%;
|
||||
stroke: currentColor;
|
||||
stroke-width: var(--mk-cta-arrow-w);
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
fill: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Self-preview wrapper. WIRING: copy the .mk-cta CSS above, the .mk-cta
|
||||
markup below, and the helpers into your host composition. Position
|
||||
via top/right/bottom/left; add class="clip" + data-start/duration/
|
||||
track-index for host windowing. -->
|
||||
<div
|
||||
id="mk-cta-root"
|
||||
data-composition-id="mk-cta-button"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
>
|
||||
<div class="mk-cta mk-cta--framed" id="mk-cta-demo" style="top: 480px; left: 780px">
|
||||
<span>Subscribe</span>
|
||||
<span class="mk-cta-arrow"
|
||||
><svg viewBox="0 0 24 24"><path d="M4 12h15M13 5l7 7-7 7" /></svg
|
||||
></span>
|
||||
</div>
|
||||
<div
|
||||
id="mk-cta-drv"
|
||||
class="clip"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-track-index="0"
|
||||
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
|
||||
></div>
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
window.__timelines = window.__timelines || {};
|
||||
|
||||
// ---- copy these helpers into the host script, then call them on the
|
||||
// paused host timeline:
|
||||
// mkCtaIn(tl, "#mk-my-cta", 1.0);
|
||||
// mkCtaNudge(tl, "#mk-my-cta", 2.0); // arrow beckons once
|
||||
// mkCtaOut(tl, "#mk-my-cta", 8.5);
|
||||
window.mkCtaIn = function (tl, target, at) {
|
||||
gsap.set(target, { opacity: 0, y: 26, scale: 0.95 });
|
||||
tl.to(target, { opacity: 1, y: 0, scale: 1, duration: 0.65, ease: "back.out(1.2)" }, at);
|
||||
};
|
||||
window.mkCtaNudge = function (tl, target, at) {
|
||||
var arrow = document.querySelector(target + " .mk-cta-arrow");
|
||||
if (arrow) tl.to(arrow, { x: 7, duration: 0.28, ease: "sine.inOut", yoyo: true, repeat: 3 }, at);
|
||||
};
|
||||
window.mkCtaOut = function (tl, target, at) {
|
||||
tl.to(target, { opacity: 0, y: -16, duration: 0.35, ease: "power2.in" }, at);
|
||||
};
|
||||
|
||||
// Self-preview timeline (not part of the snippet you copy).
|
||||
var tl = gsap.timeline({ paused: true });
|
||||
window.mkCtaIn(tl, "#mk-cta-demo", 0.4);
|
||||
window.mkCtaNudge(tl, "#mk-cta-demo", 1.5);
|
||||
window.mkCtaOut(tl, "#mk-cta-demo", 3.4);
|
||||
tl.set("#mk-cta-root", { visibility: "hidden" }, 3.98);
|
||||
window.__timelines["mk-cta-button"] = tl;
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
|
||||
"name": "mk-cta-button",
|
||||
"type": "hyperframes:component",
|
||||
"title": "Pill CTA Button",
|
||||
"description": "Rounded call-to-action button with arrow, optional outline frame with shadow, frosted variant, and an arrow-nudge helper",
|
||||
"tags": ["cta", "button", "minimal", "overlay"],
|
||||
"files": [
|
||||
{
|
||||
"path": "mk-cta-button.html",
|
||||
"target": "compositions/components/mk-cta-button.html",
|
||||
"type": "hyperframes:snippet"
|
||||
}
|
||||
]
|
||||
}
|
||||
+100
@@ -0,0 +1,100 @@
|
||||
<!doctype html>
|
||||
<!-- Demo plate for the catalog preview. Identical to mk-emphasis-type.html except the
|
||||
root background, which is opaque here so the transparent overlay reads.
|
||||
Not installed by `hyperframes add` - registry-item.json lists mk-emphasis-type.html only. -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<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;
|
||||
}
|
||||
body {
|
||||
background: #f2f4f7;
|
||||
overflow: hidden;
|
||||
}
|
||||
/* ---- mk-emphasis-type ------------------------------------------------
|
||||
Oversized low-contrast background word sliding slowly behind the
|
||||
subject (the promo's "EMPHASIS" texture type). Decorative only —
|
||||
keep it behind real content and mark it aria-hidden. */
|
||||
.mk-emphasis {
|
||||
--mk-font: "Inter", -apple-system, sans-serif;
|
||||
--mk-emph-color: rgba(29, 29, 31, 0.07); /* dark scheme: rgba(245,245,247,0.08) */
|
||||
|
||||
position: absolute;
|
||||
font-family: var(--mk-font);
|
||||
font-weight: 600;
|
||||
font-size: 260px;
|
||||
letter-spacing: -0.02em;
|
||||
line-height: 1;
|
||||
white-space: nowrap;
|
||||
text-transform: uppercase;
|
||||
color: var(--mk-emph-color);
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Self-preview wrapper. WIRING: copy the .mk-emphasis CSS, the markup
|
||||
below (keep aria-hidden + data-layout-allow-overflow — the word is
|
||||
decorative and usually wider than the canvas), and the helpers. -->
|
||||
<div
|
||||
id="mk-emph-root"
|
||||
data-composition-id="mk-emphasis-type"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
>
|
||||
<div
|
||||
class="mk-emphasis"
|
||||
id="mk-emph-demo"
|
||||
aria-hidden="true"
|
||||
data-layout-allow-overflow
|
||||
style="top: 400px; left: -40px"
|
||||
>
|
||||
Emphasis
|
||||
</div>
|
||||
<div
|
||||
id="mk-emph-drv"
|
||||
class="clip"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-track-index="0"
|
||||
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
|
||||
></div>
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
window.__timelines = window.__timelines || {};
|
||||
|
||||
// ---- copy these helpers into the host script, then:
|
||||
// mkEmphasisIn(tl, "#mk-my-word", 1.0, { drift: -120, hold: 6 });
|
||||
// mkEmphasisOut(tl, "#mk-my-word", 7.5);
|
||||
window.mkEmphasisIn = function (tl, target, at, opts) {
|
||||
opts = opts || {};
|
||||
var drift = opts.drift === undefined ? -100 : opts.drift;
|
||||
var hold = opts.hold === undefined ? 5 : opts.hold;
|
||||
gsap.set(target, { opacity: 0, x: 40 });
|
||||
tl.to(target, { opacity: 1, duration: 1.0, ease: "power2.out" }, at);
|
||||
tl.to(target, { x: 40 + drift, duration: hold, ease: "sine.inOut" }, at);
|
||||
};
|
||||
window.mkEmphasisOut = function (tl, target, at) {
|
||||
tl.to(target, { opacity: 0, duration: 0.5, ease: "power2.in" }, at);
|
||||
};
|
||||
|
||||
// Self-preview timeline (not part of the snippet you copy).
|
||||
var tl = gsap.timeline({ paused: true });
|
||||
window.mkEmphasisIn(tl, "#mk-emph-demo", 0.3, { drift: -110, hold: 3.2 });
|
||||
window.mkEmphasisOut(tl, "#mk-emph-demo", 3.4);
|
||||
tl.set("#mk-emph-root", { visibility: "hidden" }, 3.98);
|
||||
window.__timelines["mk-emphasis-type"] = tl;
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,123 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<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;
|
||||
}
|
||||
body {
|
||||
background: transparent;
|
||||
overflow: hidden;
|
||||
}
|
||||
/* ---- mk-pill-callout ----------------------------------------------
|
||||
Capsule chip in a minimal presentation style: @handles, tags, URLs, CTAs.
|
||||
Variants: --accent (iOS-blue fill), --paper (white card + shadow),
|
||||
--frost (frosted glass over footage/gradient).
|
||||
Tokens fall back to the mk family defaults; override in the host. */
|
||||
.mk-pill {
|
||||
--mk-font: "Inter", -apple-system, sans-serif;
|
||||
--mk-ink: #1d1d1f;
|
||||
--mk-paper: #ffffff;
|
||||
--mk-accent: #0071e3;
|
||||
--mk-radius-pill: 999px;
|
||||
--mk-shadow: 0 12px 36px rgba(0, 0, 0, 0.14);
|
||||
|
||||
position: absolute;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
padding: 22px 44px;
|
||||
border-radius: var(--mk-radius-pill);
|
||||
font-family: var(--mk-font);
|
||||
font-weight: 600;
|
||||
font-size: 40px;
|
||||
letter-spacing: -0.01em;
|
||||
white-space: nowrap;
|
||||
pointer-events: none;
|
||||
}
|
||||
.mk-pill--accent {
|
||||
background: var(--mk-accent);
|
||||
color: #ffffff;
|
||||
}
|
||||
.mk-pill--paper {
|
||||
background: var(--mk-paper);
|
||||
color: var(--mk-ink);
|
||||
box-shadow: var(--mk-shadow);
|
||||
}
|
||||
.mk-pill--frost {
|
||||
background: rgba(255, 255, 255, 0.42);
|
||||
color: var(--mk-ink);
|
||||
backdrop-filter: blur(24px) saturate(1.4);
|
||||
-webkit-backdrop-filter: blur(24px) saturate(1.4);
|
||||
box-shadow: var(--mk-shadow);
|
||||
}
|
||||
.mk-pill-dot {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 50%;
|
||||
background: currentColor;
|
||||
opacity: 0.9;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Self-preview wrapper. WIRING: copy only the .mk-pill CSS above, the
|
||||
.mk-pill markup below, and the mkPillIn/mkPillOut helpers into your
|
||||
host composition — position instances via top/right/bottom/left and
|
||||
add class="clip" + data-start/data-duration/data-track-index when the
|
||||
host should window their visibility. -->
|
||||
<div
|
||||
id="mk-pill-root"
|
||||
data-composition-id="mk-pill-callout"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
>
|
||||
<div class="mk-pill mk-pill--accent" id="mk-pill-demo" style="top: 120px; right: 140px">
|
||||
<span class="mk-pill-dot"></span><span>@hyperframes</span>
|
||||
</div>
|
||||
<div
|
||||
id="mk-pill-drv"
|
||||
class="clip"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-track-index="0"
|
||||
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
window.__timelines = window.__timelines || {};
|
||||
|
||||
// ---- copy these helpers into the host script (before your timeline
|
||||
// code), then call them on your paused host timeline:
|
||||
//
|
||||
// mkPillIn(tl, "#mk-my-pill", 0.8); // slide+fade entrance
|
||||
// mkPillOut(tl, "#mk-my-pill", 9.2); // fade exit
|
||||
//
|
||||
window.mkPillIn = function (tl, target, at) {
|
||||
gsap.set(target, { opacity: 0, y: 24, scale: 0.96 });
|
||||
tl.to(target, { opacity: 1, y: 0, scale: 1, duration: 0.6, ease: "power3.out" }, at);
|
||||
};
|
||||
window.mkPillOut = function (tl, target, at) {
|
||||
tl.to(target, { opacity: 0, y: -16, duration: 0.35, ease: "power2.in" }, at);
|
||||
};
|
||||
|
||||
// Self-preview timeline (not part of the snippet you copy).
|
||||
var tl = gsap.timeline({ paused: true });
|
||||
window.mkPillIn(tl, "#mk-pill-demo", 0.4);
|
||||
window.mkPillOut(tl, "#mk-pill-demo", 3.4);
|
||||
tl.set("#mk-pill-root", { visibility: "hidden" }, 3.98);
|
||||
window.__timelines["mk-pill-callout"] = tl;
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
|
||||
"name": "mk-pill-callout",
|
||||
"type": "hyperframes:component",
|
||||
"title": "Capsule Callout Chip",
|
||||
"description": "Rounded capsule chip for handles, tags, and URLs with solid, paper, and frosted-glass variants; slide in/out helpers for the host timeline",
|
||||
"tags": ["callout", "social", "minimal", "overlay"],
|
||||
"files": [
|
||||
{
|
||||
"path": "mk-pill-callout.html",
|
||||
"target": "compositions/components/mk-pill-callout.html",
|
||||
"type": "hyperframes:snippet"
|
||||
}
|
||||
]
|
||||
}
|
||||
+152
@@ -0,0 +1,152 @@
|
||||
<!doctype html>
|
||||
<!-- Demo plate for the catalog preview. Identical to mk-usage-arc.html except the
|
||||
root background, which is opaque here so the transparent overlay reads.
|
||||
Not installed by `hyperframes add` - registry-item.json lists mk-usage-arc.html only. -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<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;
|
||||
}
|
||||
body {
|
||||
background: #f2f4f7;
|
||||
overflow: hidden;
|
||||
}
|
||||
/* ---- mk-usage-arc ----------------------------------------------------
|
||||
Hairline circular gauge: draw-on arc + centered value + tiny label.
|
||||
The promo's "Usage" arc. Recolor via --mk-arc-color. */
|
||||
.mk-arc {
|
||||
--mk-font: "Inter", -apple-system, sans-serif;
|
||||
--mk-ink: #1d1d1f;
|
||||
--mk-ink-dim: #6e6e73;
|
||||
--mk-accent: #0071e3;
|
||||
--mk-arc-color: var(--mk-accent);
|
||||
--mk-hairline: 3px;
|
||||
|
||||
position: absolute;
|
||||
width: 220px;
|
||||
height: 220px;
|
||||
pointer-events: none;
|
||||
}
|
||||
.mk-arc svg {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.mk-arc-track {
|
||||
fill: none;
|
||||
stroke: rgba(29, 29, 31, 0.1);
|
||||
stroke-width: var(--mk-hairline);
|
||||
}
|
||||
.mk-arc-fill {
|
||||
fill: none;
|
||||
stroke: var(--mk-arc-color);
|
||||
stroke-width: var(--mk-hairline);
|
||||
stroke-linecap: round;
|
||||
}
|
||||
.mk-arc-center {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 4px;
|
||||
font-family: var(--mk-font);
|
||||
}
|
||||
.mk-arc-num {
|
||||
font-weight: 600;
|
||||
font-size: 46px;
|
||||
letter-spacing: -0.02em;
|
||||
color: var(--mk-ink);
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
.mk-arc-label {
|
||||
font-weight: 500;
|
||||
font-size: 20px;
|
||||
color: var(--mk-ink);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Self-preview wrapper. WIRING: copy the .mk-arc CSS, the markup below,
|
||||
and the helpers into your host. r=92 with 220px box; the helper reads
|
||||
the circle's radius, so resizing the box + r just works. -->
|
||||
<div
|
||||
id="mk-arc-root"
|
||||
data-composition-id="mk-usage-arc"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
>
|
||||
<div class="mk-arc" id="mk-arc-demo" style="top: 430px; left: 850px">
|
||||
<svg viewBox="0 0 220 220">
|
||||
<circle class="mk-arc-track" cx="110" cy="110" r="92"></circle>
|
||||
<circle class="mk-arc-fill" cx="110" cy="110" r="92" transform="rotate(-90 110 110)"></circle>
|
||||
</svg>
|
||||
<div class="mk-arc-center">
|
||||
<span class="mk-arc-num">0%</span>
|
||||
<span class="mk-arc-label">Usage</span>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
id="mk-arc-drv"
|
||||
class="clip"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-track-index="0"
|
||||
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
|
||||
></div>
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
window.__timelines = window.__timelines || {};
|
||||
|
||||
// ---- copy these helpers into the host script, then:
|
||||
// mkArcIn(tl, "#mk-my-arc", 1.0, 0.76); // draw to 76%, count the numeral
|
||||
// mkArcOut(tl, "#mk-my-arc", 8.5);
|
||||
window.mkArcIn = function (tl, target, at, value) {
|
||||
var el = document.querySelector(target);
|
||||
var fill = el.querySelector(".mk-arc-fill");
|
||||
var num = el.querySelector(".mk-arc-num");
|
||||
var C = 2 * Math.PI * parseFloat(fill.getAttribute("r"));
|
||||
gsap.set(el, { opacity: 0, y: 16 });
|
||||
gsap.set(fill, { strokeDasharray: C, strokeDashoffset: C });
|
||||
tl.to(el, { opacity: 1, y: 0, duration: 0.6, ease: "power3.out" }, at);
|
||||
tl.to(fill, { strokeDashoffset: C * (1 - value), duration: 1.1, ease: "power2.inOut" }, at + 0.15);
|
||||
var st = { v: 0 };
|
||||
tl.to(
|
||||
st,
|
||||
{
|
||||
v: value,
|
||||
duration: 1.1,
|
||||
ease: "power2.inOut",
|
||||
onUpdate: function () {
|
||||
num.textContent = Math.round(st.v * 100) + "%";
|
||||
},
|
||||
},
|
||||
at + 0.15
|
||||
);
|
||||
};
|
||||
window.mkArcOut = function (tl, target, at) {
|
||||
tl.to(target, { opacity: 0, y: -12, duration: 0.35, ease: "power2.in" }, at);
|
||||
};
|
||||
|
||||
// Self-preview timeline (not part of the snippet you copy).
|
||||
var tl = gsap.timeline({ paused: true });
|
||||
window.mkArcIn(tl, "#mk-arc-demo", 0.4, 0.76);
|
||||
window.mkArcOut(tl, "#mk-arc-demo", 3.4);
|
||||
tl.set("#mk-arc-root", { visibility: "hidden" }, 3.98);
|
||||
window.__timelines["mk-usage-arc"] = tl;
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,15 +0,0 @@
|
||||
{
|
||||
"$schema": "https://hyperframes.heygen.com/schema/registry-item.json",
|
||||
"name": "yt-avatar-pip",
|
||||
"type": "hyperframes:component",
|
||||
"title": "Circle PiP Window",
|
||||
"description": "Circular or rounded-rect masked picture-in-picture window with ring and shadow for a second video or image; pop in/out helpers",
|
||||
"tags": ["pip", "overlay", "creator"],
|
||||
"files": [
|
||||
{
|
||||
"path": "yt-avatar-pip.html",
|
||||
"target": "compositions/components/yt-avatar-pip.html",
|
||||
"type": "hyperframes:snippet"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,115 +0,0 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<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;
|
||||
}
|
||||
body {
|
||||
background: transparent;
|
||||
overflow: hidden;
|
||||
}
|
||||
/* ---- yt-avatar-pip ---------------------------------------------------
|
||||
The screen-recording PiP: a circular (or rounded-rect) masked window
|
||||
for a second video/image — one drop replaces the manual
|
||||
crop-mask-resize dance. Size via --yt-pip-size, framing via an
|
||||
inline transform scale on the inner media (an "inside scale"). */
|
||||
.yt-pip {
|
||||
--yt-pip-size: 300px;
|
||||
--yt-pip-ring: rgba(255, 255, 255, 0.92);
|
||||
--yt-pip-ring-w: 4px;
|
||||
|
||||
position: absolute;
|
||||
width: var(--yt-pip-size);
|
||||
height: var(--yt-pip-size);
|
||||
border-radius: 50%;
|
||||
overflow: hidden;
|
||||
border: var(--yt-pip-ring-w) solid var(--yt-pip-ring);
|
||||
box-shadow: 0 18px 50px rgba(0, 0, 0, 0.4);
|
||||
pointer-events: none;
|
||||
}
|
||||
.yt-pip--rect {
|
||||
border-radius: 22px;
|
||||
width: calc(var(--yt-pip-size) * 1.55);
|
||||
}
|
||||
.yt-pip > img,
|
||||
.yt-pip > video,
|
||||
.yt-pip > .yt-pip-fill {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
/* self-preview stand-in (not part of the snippet) */
|
||||
.yt-pip-fill {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: "Inter", ui-sans-serif, sans-serif;
|
||||
font-weight: 700;
|
||||
font-size: 92px;
|
||||
color: rgba(255, 255, 255, 0.95);
|
||||
background: linear-gradient(135deg, #ff8fb8 0%, #7d5cff 100%);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Self-preview wrapper. WIRING: copy the .yt-pip CSS and the markup
|
||||
pattern below into your host; put a <video data-start ...> or <img>
|
||||
inside. The media element must live in the HOST root (this pattern
|
||||
is copied in) — never inside a mounted sub-composition: sub-comps
|
||||
are never seeked, so framework-owned media there renders black. Position via top/right/bottom/left; add class="clip" +
|
||||
data-start/duration/track-index for host windowing. Framing: style
|
||||
the inner media with transform: scale(1.15) etc. Then:
|
||||
|
||||
ytPipIn(tl, "#yt-my-pip", 2.0);
|
||||
ytPipOut(tl, "#yt-my-pip", 12.0);
|
||||
-->
|
||||
<div
|
||||
id="yt-pip-root"
|
||||
data-composition-id="yt-avatar-pip"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
>
|
||||
<div class="yt-pip" id="yt-pip-demo" style="left: 120px; bottom: 100px">
|
||||
<div class="yt-pip-fill">AB</div>
|
||||
</div>
|
||||
<div
|
||||
id="yt-pip-drv"
|
||||
class="clip"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-track-index="0"
|
||||
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
|
||||
></div>
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
window.__timelines = window.__timelines || {};
|
||||
|
||||
// ---- copy these helpers into the host script ----
|
||||
window.ytPipIn = function (tl, target, at) {
|
||||
gsap.set(target, { opacity: 0, scale: 0.55, y: 26 });
|
||||
tl.to(target, { opacity: 1, scale: 1, y: 0, duration: 0.55, ease: "back.out(1.5)" }, at);
|
||||
};
|
||||
window.ytPipOut = function (tl, target, at) {
|
||||
tl.to(target, { opacity: 0, scale: 0.75, y: 18, duration: 0.35, ease: "power2.in" }, at);
|
||||
};
|
||||
|
||||
// Self-preview timeline (not part of the snippet you copy).
|
||||
var tl = gsap.timeline({ paused: true });
|
||||
window.ytPipIn(tl, "#yt-pip-demo", 0.4);
|
||||
window.ytPipOut(tl, "#yt-pip-demo", 3.4);
|
||||
tl.set("#yt-pip-root", { visibility: "hidden" }, 3.98);
|
||||
window.__timelines["yt-avatar-pip"] = tl;
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+173
@@ -0,0 +1,173 @@
|
||||
<!doctype html>
|
||||
<!-- Demo plate for the catalog preview. Identical to yt-camera-move.html except the
|
||||
root background, which is opaque here so the transparent overlay reads.
|
||||
Not installed by `hyperframes add` - registry-item.json lists yt-camera-move.html only. -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<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;
|
||||
}
|
||||
body {
|
||||
background: #14161c;
|
||||
overflow: hidden;
|
||||
}
|
||||
/* ---- yt-camera-move --------------------------------------------------
|
||||
The creator-video punch-in as reusable GSAP helpers: dynamic
|
||||
zoom + slide + 3D tilt/pan on any wrapper (a #video-wrap, a
|
||||
sub-composition include, an image card). A talking-head recipe
|
||||
is the default: cubic easing, ~11% zoom.
|
||||
|
||||
Optional: .yt-cm-defocus is an edge-blur overlay that pulses during
|
||||
the move to sell the 3D feel — place it ABOVE the moving layer,
|
||||
covering the same region. */
|
||||
.yt-cm-defocus {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
backdrop-filter: blur(7px);
|
||||
-webkit-backdrop-filter: blur(7px);
|
||||
/* blur only the frame edges; center stays sharp */
|
||||
-webkit-mask-image: radial-gradient(
|
||||
ellipse 62% 58% at 50% 50%,
|
||||
transparent 58%,
|
||||
rgba(0, 0, 0, 0.85) 92%
|
||||
);
|
||||
mask-image: radial-gradient(
|
||||
ellipse 62% 58% at 50% 50%,
|
||||
transparent 58%,
|
||||
rgba(0, 0, 0, 0.85) 92%
|
||||
);
|
||||
}
|
||||
/* ---- self-preview stand-in "footage" (not part of the snippet) ---- */
|
||||
#yt-cm-stage {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
overflow: hidden;
|
||||
background: linear-gradient(135deg, #35c8d9 0%, #2a7fff 55%, #7d5cff 100%);
|
||||
}
|
||||
#yt-cm-stage .yt-cm-grid {
|
||||
position: absolute;
|
||||
inset: -60px;
|
||||
background:
|
||||
repeating-linear-gradient(0deg, rgba(255, 255, 255, 0.14) 0 2px, transparent 2px 120px),
|
||||
repeating-linear-gradient(90deg, rgba(255, 255, 255, 0.14) 0 2px, transparent 2px 120px);
|
||||
}
|
||||
#yt-cm-stage .yt-cm-label {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: "Inter", ui-sans-serif, sans-serif;
|
||||
font-weight: 700;
|
||||
font-size: 120px;
|
||||
letter-spacing: -0.02em;
|
||||
color: rgba(255, 255, 255, 0.92);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Self-preview wrapper. WIRING: copy the .yt-cm-defocus CSS (if you want
|
||||
the edge blur), add <div class="yt-cm-defocus" id="yt-my-defocus"></div>
|
||||
as a sibling ABOVE your moving layer, and copy the helpers into your
|
||||
host script. Then drive any wrapper:
|
||||
|
||||
ytCameraMove(tl, "#video-wrap", 5.0, { zoom: 0.11, slideY: -24 });
|
||||
ytDefocusPulse(tl, "#yt-my-defocus", 5.0, 1.2);
|
||||
ytCameraReset(tl, "#video-wrap", 9.0);
|
||||
-->
|
||||
<div
|
||||
id="yt-cm-root"
|
||||
data-composition-id="yt-camera-move"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
>
|
||||
<div id="yt-cm-stage">
|
||||
<div class="yt-cm-grid" data-layout-allow-overflow></div>
|
||||
<div class="yt-cm-label">FOOTAGE</div>
|
||||
</div>
|
||||
<div class="yt-cm-defocus" id="yt-cm-defocus"></div>
|
||||
<div
|
||||
id="yt-cm-drv"
|
||||
class="clip"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-track-index="0"
|
||||
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
|
||||
></div>
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
window.__timelines = window.__timelines || {};
|
||||
|
||||
// ---- copy these helpers into the host script ----
|
||||
// Punch-in / camera move on any wrapper. A talking-head base:
|
||||
// zoom 0.11, cubic ease. tilt/pan are degrees (rotationX/rotationY,
|
||||
// perspective is set for you); slideX/slideY in px; roll in degrees.
|
||||
window.ytCameraMove = function (tl, target, at, opts) {
|
||||
opts = opts || {};
|
||||
var dur = opts.dur === undefined ? 1.2 : opts.dur;
|
||||
gsap.set(target, { transformPerspective: 1200, transformOrigin: opts.origin || "50% 50%" });
|
||||
tl.to(
|
||||
target,
|
||||
{
|
||||
scale: 1 + (opts.zoom === undefined ? 0.11 : opts.zoom),
|
||||
x: opts.slideX || 0,
|
||||
y: opts.slideY || 0,
|
||||
rotationX: opts.tilt || 0,
|
||||
rotationY: opts.pan || 0,
|
||||
rotation: opts.roll || 0,
|
||||
duration: dur,
|
||||
ease: opts.ease || "power2.inOut",
|
||||
},
|
||||
at
|
||||
);
|
||||
};
|
||||
// Return to rest (same easing family).
|
||||
window.ytCameraReset = function (tl, target, at, opts) {
|
||||
opts = opts || {};
|
||||
tl.to(
|
||||
target,
|
||||
{
|
||||
scale: 1,
|
||||
x: 0,
|
||||
y: 0,
|
||||
rotationX: 0,
|
||||
rotationY: 0,
|
||||
rotation: 0,
|
||||
duration: opts.dur === undefined ? 1.2 : opts.dur,
|
||||
ease: opts.ease || "power2.inOut",
|
||||
},
|
||||
at
|
||||
);
|
||||
};
|
||||
// Edge-defocus pulse synced to a move (overlay fades in then out).
|
||||
window.ytDefocusPulse = function (tl, target, at, dur) {
|
||||
dur = dur === undefined ? 1.2 : dur;
|
||||
tl.to(target, { opacity: 1, duration: dur * 0.45, ease: "power2.out" }, at);
|
||||
tl.to(target, { opacity: 0, duration: dur * 0.55, ease: "power2.in" }, at + dur * 0.45);
|
||||
};
|
||||
|
||||
// Self-preview timeline (not part of the snippet you copy):
|
||||
// punch in with a slight tilt, hold, return.
|
||||
var tl = gsap.timeline({ paused: true });
|
||||
window.ytCameraMove(tl, "#yt-cm-stage", 0.4, { zoom: 0.11, slideY: -26, tilt: 3, pan: -2 });
|
||||
window.ytDefocusPulse(tl, "#yt-cm-defocus", 0.4, 1.2);
|
||||
window.ytCameraReset(tl, "#yt-cm-stage", 2.5);
|
||||
window.ytDefocusPulse(tl, "#yt-cm-defocus", 2.5, 1.2);
|
||||
tl.set("#yt-cm-root", { visibility: "hidden" }, 3.98);
|
||||
window.__timelines["yt-camera-move"] = tl;
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+163
@@ -0,0 +1,163 @@
|
||||
<!doctype html>
|
||||
<!-- Demo plate for the catalog preview. Identical to yt-circle-pointer.html except the
|
||||
root background, which is opaque here so the transparent overlay reads.
|
||||
Not installed by `hyperframes add` - registry-item.json lists yt-circle-pointer.html only. -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<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;
|
||||
}
|
||||
body {
|
||||
background: #14161c;
|
||||
overflow: hidden;
|
||||
}
|
||||
/* ---- yt-circle-pointer -----------------------------------------------
|
||||
Annotation tools: a hand-drawn-feel circle that draws on around a
|
||||
target, and a countdown chip. Position instances via top/left with
|
||||
explicit width/height. */
|
||||
.yt-circle {
|
||||
--yt-red: #e62e2e;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
.yt-circle svg {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: visible;
|
||||
}
|
||||
.yt-circle ellipse {
|
||||
fill: none;
|
||||
stroke: var(--yt-ptr-color, var(--yt-red));
|
||||
stroke-width: 6;
|
||||
stroke-linecap: round;
|
||||
transform: rotate(-6deg);
|
||||
transform-origin: 50% 50%;
|
||||
}
|
||||
.yt-count {
|
||||
position: absolute;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 14px;
|
||||
padding: 16px 32px;
|
||||
border-radius: 999px;
|
||||
font-family: "Inter", ui-sans-serif, sans-serif;
|
||||
font-weight: 700;
|
||||
font-size: 40px;
|
||||
background: var(--yt-count-bg, var(--yt-red));
|
||||
color: #ffffff;
|
||||
box-shadow: 0 12px 32px rgba(0, 0, 0, 0.3);
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
}
|
||||
.yt-count .num {
|
||||
font-variant-numeric: tabular-nums;
|
||||
min-width: 1.2em;
|
||||
text-align: center;
|
||||
}
|
||||
/* self-preview scene */
|
||||
#yt-ptr-scene {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(135deg, #35c8d9 0%, #2a7fff 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: "Inter", ui-sans-serif, sans-serif;
|
||||
font-weight: 700;
|
||||
font-size: 60px;
|
||||
color: rgba(255, 255, 255, 0.92);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Self-preview wrapper. WIRING: copy the .yt-circle / .yt-count CSS and
|
||||
markup patterns, position over your target, then:
|
||||
|
||||
ytCircleOn(tl, "#yt-my-circle", 3.0); // draws the ring
|
||||
ytCircleOff(tl, "#yt-my-circle", 6.0);
|
||||
ytCountIn(tl, "#yt-my-count", 2.0);
|
||||
ytCountdown(tl, "#yt-my-count", 2.4, 5); // 5 -> 0, pulsing each tick
|
||||
-->
|
||||
<div
|
||||
id="yt-ptr-root"
|
||||
data-composition-id="yt-circle-pointer"
|
||||
data-start="0"
|
||||
data-duration="5"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
>
|
||||
<div id="yt-ptr-scene"><span>the thing</span></div>
|
||||
<div class="yt-circle" id="yt-ptr-circle" style="left: 760px; top: 440px; width: 420px; height: 210px">
|
||||
<svg viewBox="0 0 420 210"><ellipse cx="210" cy="105" rx="196" ry="88"></ellipse></svg>
|
||||
</div>
|
||||
<div class="yt-count" id="yt-ptr-count" style="right: 140px; top: 110px">
|
||||
<span class="num">5</span><span>sec</span>
|
||||
</div>
|
||||
<div
|
||||
id="yt-ptr-drv"
|
||||
class="clip"
|
||||
data-start="0"
|
||||
data-duration="5"
|
||||
data-track-index="0"
|
||||
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
|
||||
></div>
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
window.__timelines = window.__timelines || {};
|
||||
|
||||
// ---- copy these helpers into the host script ----
|
||||
window.ytCircleOn = function (tl, target, at, dur) {
|
||||
var el = document.querySelector(target + " ellipse");
|
||||
var len = el.getTotalLength();
|
||||
gsap.set(el, { strokeDasharray: len, strokeDashoffset: len });
|
||||
tl.to(el, { strokeDashoffset: 0, duration: dur === undefined ? 0.7 : dur, ease: "power2.inOut" }, at);
|
||||
};
|
||||
window.ytCircleOff = function (tl, target, at) {
|
||||
tl.to(target, { opacity: 0, duration: 0.35, ease: "power2.in" }, at);
|
||||
};
|
||||
window.ytCountIn = function (tl, target, at) {
|
||||
gsap.set(target, { y: -18, scale: 0.9 });
|
||||
tl.to(target, { opacity: 1, y: 0, scale: 1, duration: 0.45, ease: "back.out(1.5)" }, at);
|
||||
};
|
||||
window.ytCountdown = function (tl, target, at, from) {
|
||||
var num = document.querySelector(target + " .num");
|
||||
var st = { v: from };
|
||||
tl.to(
|
||||
st,
|
||||
{
|
||||
v: 0,
|
||||
duration: from,
|
||||
ease: "none",
|
||||
onUpdate: function () {
|
||||
num.textContent = String(Math.ceil(st.v));
|
||||
},
|
||||
},
|
||||
at
|
||||
);
|
||||
for (var i = 1; i <= from; i++) {
|
||||
tl.to(target, { scale: 1.07, duration: 0.09, ease: "sine.out" }, at + i - 0.09);
|
||||
tl.to(target, { scale: 1, duration: 0.14, ease: "sine.in" }, at + i);
|
||||
}
|
||||
};
|
||||
|
||||
// Self-preview timeline.
|
||||
var tl = gsap.timeline({ paused: true });
|
||||
window.ytCountIn(tl, "#yt-ptr-count", 0.3);
|
||||
window.ytCountdown(tl, "#yt-ptr-count", 0.7, 3);
|
||||
window.ytCircleOn(tl, "#yt-ptr-circle", 1.0);
|
||||
window.ytCircleOff(tl, "#yt-ptr-circle", 4.2);
|
||||
tl.to("#yt-ptr-count", { opacity: 0, duration: 0.3, ease: "power2.in" }, 4.3);
|
||||
tl.set("#yt-ptr-root", { visibility: "hidden" }, 4.98);
|
||||
window.__timelines["yt-circle-pointer"] = tl;
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+148
@@ -0,0 +1,148 @@
|
||||
<!doctype html>
|
||||
<!-- Demo plate for the catalog preview. Identical to yt-feather-highlight.html except the
|
||||
root background, which is opaque here so the transparent overlay reads.
|
||||
Not installed by `hyperframes add` - registry-item.json lists yt-feather-highlight.html only. -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<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;
|
||||
}
|
||||
body {
|
||||
background: #14161c;
|
||||
overflow: hidden;
|
||||
}
|
||||
/* ---- yt-feather-highlight --------------------------------------------
|
||||
Spotlight part of the frame: a dim layer with a feathered ellipse
|
||||
hole. The hole's position/size are CSS variables, so the helpers
|
||||
tween them directly (GSAP animates CSS vars). */
|
||||
.yt-hl {
|
||||
--yt-hl-x: 50%;
|
||||
--yt-hl-y: 50%;
|
||||
--yt-hl-rx: 22%;
|
||||
--yt-hl-ry: 26%;
|
||||
--yt-hl-dim: 0.55;
|
||||
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
background: rgba(6, 8, 12, var(--yt-hl-dim));
|
||||
-webkit-mask-image: radial-gradient(
|
||||
ellipse var(--yt-hl-rx) var(--yt-hl-ry) at var(--yt-hl-x) var(--yt-hl-y),
|
||||
transparent 0%,
|
||||
transparent 52%,
|
||||
black 88%
|
||||
);
|
||||
mask-image: radial-gradient(
|
||||
ellipse var(--yt-hl-rx) var(--yt-hl-ry) at var(--yt-hl-x) var(--yt-hl-y),
|
||||
transparent 0%,
|
||||
transparent 52%,
|
||||
black 88%
|
||||
);
|
||||
}
|
||||
/* self-preview stand-in scene (not part of the snippet) */
|
||||
#yt-hl-scene {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(135deg, #2a7fff 0%, #7d5cff 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
font-family: "Inter", ui-sans-serif, sans-serif;
|
||||
font-weight: 700;
|
||||
font-size: 64px;
|
||||
color: rgba(255, 255, 255, 0.92);
|
||||
}
|
||||
#yt-hl-scene .box {
|
||||
width: 420px;
|
||||
height: 300px;
|
||||
border-radius: 24px;
|
||||
background: rgba(255, 255, 255, 0.16);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Self-preview wrapper. WIRING: copy the .yt-hl CSS and drop
|
||||
<div class="yt-hl" id="yt-my-hl"></div> ABOVE the content to spotlight
|
||||
(same region). Then:
|
||||
|
||||
ytHighlightIn(tl, "#yt-my-hl", 3.0, { x: "28%", y: "50%", rx: "20%", ry: "30%" });
|
||||
ytHighlightTo(tl, "#yt-my-hl", 5.0, { x: "72%" }, 0.8); // glide to the next target
|
||||
ytHighlightOut(tl, "#yt-my-hl", 8.0);
|
||||
-->
|
||||
<div
|
||||
id="yt-hl-root"
|
||||
data-composition-id="yt-feather-highlight"
|
||||
data-start="0"
|
||||
data-duration="5"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
>
|
||||
<div id="yt-hl-scene">
|
||||
<div class="box">A</div>
|
||||
<div class="box">B</div>
|
||||
</div>
|
||||
<div class="yt-hl" id="yt-hl-demo"></div>
|
||||
<div
|
||||
id="yt-hl-drv"
|
||||
class="clip"
|
||||
data-start="0"
|
||||
data-duration="5"
|
||||
data-track-index="0"
|
||||
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
|
||||
></div>
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
window.__timelines = window.__timelines || {};
|
||||
|
||||
// ---- copy these helpers into the host script ----
|
||||
window.ytHighlightIn = function (tl, target, at, opts) {
|
||||
opts = opts || {};
|
||||
var vars = { opacity: 1, duration: 0.5, ease: "power2.out" };
|
||||
if (opts.x !== undefined) vars["--yt-hl-x"] = opts.x;
|
||||
if (opts.y !== undefined) vars["--yt-hl-y"] = opts.y;
|
||||
if (opts.rx !== undefined) vars["--yt-hl-rx"] = opts.rx;
|
||||
if (opts.ry !== undefined) vars["--yt-hl-ry"] = opts.ry;
|
||||
gsap.set(target, {
|
||||
"--yt-hl-x": opts.x !== undefined ? opts.x : "50%",
|
||||
"--yt-hl-y": opts.y !== undefined ? opts.y : "50%",
|
||||
"--yt-hl-rx": opts.rx !== undefined ? opts.rx : "22%",
|
||||
"--yt-hl-ry": opts.ry !== undefined ? opts.ry : "26%",
|
||||
});
|
||||
tl.to(target, { opacity: 1, duration: 0.5, ease: "power2.out" }, at);
|
||||
};
|
||||
window.ytHighlightTo = function (tl, target, at, opts, dur) {
|
||||
opts = opts || {};
|
||||
var vars = { duration: dur === undefined ? 0.8 : dur, ease: "power2.inOut" };
|
||||
if (opts.x !== undefined) vars["--yt-hl-x"] = opts.x;
|
||||
if (opts.y !== undefined) vars["--yt-hl-y"] = opts.y;
|
||||
if (opts.rx !== undefined) vars["--yt-hl-rx"] = opts.rx;
|
||||
if (opts.ry !== undefined) vars["--yt-hl-ry"] = opts.ry;
|
||||
tl.to(target, vars, at);
|
||||
};
|
||||
window.ytHighlightOut = function (tl, target, at) {
|
||||
tl.to(target, { opacity: 0, duration: 0.4, ease: "power2.in" }, at);
|
||||
};
|
||||
|
||||
// Self-preview timeline: spotlight box A, glide to box B, release.
|
||||
var tl = gsap.timeline({ paused: true });
|
||||
window.ytHighlightIn(tl, "#yt-hl-demo", 0.5, { x: "26%", y: "50%", rx: "17%", ry: "24%" });
|
||||
window.ytHighlightTo(tl, "#yt-hl-demo", 2.2, { x: "74%" }, 0.9);
|
||||
window.ytHighlightOut(tl, "#yt-hl-demo", 4.2);
|
||||
tl.set("#yt-hl-root", { visibility: "hidden" }, 4.98);
|
||||
window.__timelines["yt-feather-highlight"] = tl;
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
<!doctype html>
|
||||
<!-- Demo plate for the catalog preview. Identical to yt-screen-warp.html except the
|
||||
root background, which is opaque here so the transparent overlay reads.
|
||||
Not installed by `hyperframes add` - registry-item.json lists yt-screen-warp.html only. -->
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<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;
|
||||
}
|
||||
body {
|
||||
background: #14161c;
|
||||
overflow: hidden;
|
||||
}
|
||||
/* ---- yt-screen-warp ---------------------------------------------------
|
||||
Make footage read as a physical display: add .yt-screen-warp3d to the
|
||||
media WRAPPER (subtle perspective), and place .yt-screen-fx overlay
|
||||
inside it (pixel grid + scanlines + vignette + sheen). */
|
||||
.yt-screen-warp3d {
|
||||
transform: perspective(1400px) rotateY(-3.5deg) rotateX(1.6deg);
|
||||
}
|
||||
.yt-screen-fx {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
pointer-events: none;
|
||||
opacity: 0;
|
||||
background:
|
||||
/* sheen */
|
||||
linear-gradient(115deg, rgba(255, 255, 255, 0.09) 0%, transparent 30%),
|
||||
/* scanlines */
|
||||
repeating-linear-gradient(0deg, rgba(0, 0, 0, 0.1) 0 1px, transparent 1px 3px),
|
||||
/* pixel grid */
|
||||
repeating-linear-gradient(90deg, rgba(0, 0, 0, 0.06) 0 1px, transparent 1px 4px);
|
||||
box-shadow:
|
||||
inset 0 0 90px rgba(0, 0, 0, 0.4),
|
||||
inset 0 0 8px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
/* self-preview stand-in */
|
||||
#yt-sw-wrap {
|
||||
position: absolute;
|
||||
left: 360px;
|
||||
top: 190px;
|
||||
width: 1200px;
|
||||
height: 700px;
|
||||
border-radius: 18px;
|
||||
overflow: hidden;
|
||||
}
|
||||
#yt-sw-fill {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background: linear-gradient(150deg, #ff8fb8 0%, #7d5cff 60%, #2a7fff 100%);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-family: "Inter", ui-sans-serif, sans-serif;
|
||||
font-weight: 700;
|
||||
font-size: 92px;
|
||||
color: rgba(255, 255, 255, 0.93);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<!-- Self-preview wrapper. WIRING: copy the two CSS classes; add
|
||||
class="yt-screen-warp3d" to your media wrapper and
|
||||
<div class="yt-screen-fx" id="yt-my-fx"></div> as its LAST child. Then:
|
||||
|
||||
ytScreenOn(tl, "#yt-my-fx", 2.0);
|
||||
ytScreenOff(tl, "#yt-my-fx", 8.0);
|
||||
-->
|
||||
<div
|
||||
id="yt-sw-root"
|
||||
data-composition-id="yt-screen-warp"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-width="1920"
|
||||
data-height="1080"
|
||||
>
|
||||
<div id="yt-sw-wrap" class="yt-screen-warp3d">
|
||||
<div id="yt-sw-fill">on screen</div>
|
||||
<div class="yt-screen-fx" id="yt-sw-fx"></div>
|
||||
</div>
|
||||
<div
|
||||
id="yt-sw-drv"
|
||||
class="clip"
|
||||
data-start="0"
|
||||
data-duration="4"
|
||||
data-track-index="0"
|
||||
style="position:absolute;width:1px;height:1px;opacity:0;pointer-events:none"
|
||||
></div>
|
||||
</div>
|
||||
<script>
|
||||
(function () {
|
||||
window.__timelines = window.__timelines || {};
|
||||
|
||||
// ---- copy these helpers into the host script ----
|
||||
window.ytScreenOn = function (tl, target, at) {
|
||||
tl.to(target, { opacity: 1, duration: 0.5, ease: "power2.out" }, at);
|
||||
};
|
||||
window.ytScreenOff = function (tl, target, at) {
|
||||
tl.to(target, { opacity: 0, duration: 0.4, ease: "power2.in" }, at);
|
||||
};
|
||||
|
||||
// Self-preview timeline.
|
||||
var tl = gsap.timeline({ paused: true });
|
||||
gsap.set("#yt-sw-wrap", { opacity: 0, scale: 0.96 });
|
||||
tl.to("#yt-sw-wrap", { opacity: 1, scale: 1, duration: 0.5, ease: "power2.out" }, 0.2);
|
||||
window.ytScreenOn(tl, "#yt-sw-fx", 0.8);
|
||||
window.ytScreenOff(tl, "#yt-sw-fx", 3.1);
|
||||
tl.to("#yt-sw-wrap", { opacity: 0, duration: 0.35, ease: "power2.in" }, 3.5);
|
||||
tl.set("#yt-sw-root", { visibility: "hidden" }, 3.98);
|
||||
window.__timelines["yt-screen-warp"] = tl;
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -615,18 +615,6 @@
|
||||
"name": "mk-line-graph",
|
||||
"type": "hyperframes:block"
|
||||
},
|
||||
{
|
||||
"name": "mk-logo-sting",
|
||||
"type": "hyperframes:block"
|
||||
},
|
||||
{
|
||||
"name": "mk-pill-callout",
|
||||
"type": "hyperframes:component"
|
||||
},
|
||||
{
|
||||
"name": "mk-cta-button",
|
||||
"type": "hyperframes:component"
|
||||
},
|
||||
{
|
||||
"name": "mk-usage-arc",
|
||||
"type": "hyperframes:component"
|
||||
@@ -655,18 +643,10 @@
|
||||
"name": "yt-vertical-fill",
|
||||
"type": "hyperframes:block"
|
||||
},
|
||||
{
|
||||
"name": "yt-doc-lower-third",
|
||||
"type": "hyperframes:block"
|
||||
},
|
||||
{
|
||||
"name": "yt-camera-move",
|
||||
"type": "hyperframes:component"
|
||||
},
|
||||
{
|
||||
"name": "yt-avatar-pip",
|
||||
"type": "hyperframes:component"
|
||||
},
|
||||
{
|
||||
"name": "yt-feather-highlight",
|
||||
"type": "hyperframes:component"
|
||||
|
||||
Reference in New Issue
Block a user