mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-10 22:20:14 +00:00
feat(skills): c2v mining pass — 7 new blueprints, 10 new rules, compacted recipe corpus (#2680)
* feat(skills): c2v mining pass over animation blueprints and rules Compacts ~45 existing animation rules/blueprints into tighter recipe form (net -3.4k lines) and adds 17 mined from the c2v corpus: - 7 blueprints: agent-progress-theater, camera-journey, fixed-anchor-cycle, panel-edit-live-sync, prompt-type-submit-generate, transcript-scroll-artifact-reveal, zoom-out-workspace-reveal - 10 rules: 3d-camera-flight, anchored-layout-expand, chart-scrub-readout, chromatic-glitch, control-target-sync, cursor-drag, gradient-text-sweep, multi-cursor-choreography, particle-burst, theme-crossfade-morph Both indexes updated. * feat(skills): sync product-launch script bank with mined blueprint roles The role->blueprint script bank in product-launch-video/story-design.md is kept 1:1 with blueprints-index role declarations, which the c2v mining pass expanded. Adds the 25 missing entries (script-shape descriptor + example lines + pattern): 13 for the 7 new blueprints, 12 for role widenings on 7 existing ones (cursor-ui-demo, dataviz-countup, titlecard-reveal, et al.), and states the 1:1 sync contract in the bank's intro. * docs(skills): cover constellation-hub scatter-drift variant in the script bank Review follow-up on #2680: the SOCIAL_PROOF constellation-hub entry patterned only the orbit shape; the c2v pass added a scatter-drift end-card variant with the opposite geometry (no hub, no ring). Adds an example line and extends the pattern so a scatter-drift beat's VO isn't steered toward the orbit shape.
This commit is contained in:
@@ -1,18 +1,12 @@
|
||||
# CSS Patterns for Marker Highlighting
|
||||
|
||||
Pure CSS + GSAP implementations of all five MarkerHighlight.js drawing modes. Use these for deterministic rendering in HyperFrames compositions — no external library dependency, full GSAP timeline control.
|
||||
Pure CSS + GSAP implementations of all five MarkerHighlight.js drawing modes — no external library dependency, full timeline control. Snippets show mechanism DOM only, inside a standard scene clip (hyperframes-core); assume `tl` exists.
|
||||
|
||||
## Contents
|
||||
|
||||
- [1. Highlight Mode](#1-highlight-mode) — Yellow marker sweep behind text
|
||||
- [2. Circle Mode](#2-circle-mode) — Hand-drawn ellipse around text
|
||||
- [3. Burst Mode](#3-burst-mode) — Radiating lines from text
|
||||
- [4. Scribble Mode](#4-scribble-mode) — Chaotic scribble over text
|
||||
- [5. Sketchout Mode](#5-sketchout-mode) — Rough rectangle outline
|
||||
Shared scaffold for every mode: the wrap is `position: relative; display: inline`; the text copy is `position: relative` and z-indexed **above** the accent (below it for sketchout, where the lines cross the text).
|
||||
|
||||
## 1. Highlight Mode
|
||||
|
||||
Yellow marker sweep behind text. The most common mode.
|
||||
Yellow marker sweep behind text — the most common mode.
|
||||
|
||||
```html
|
||||
<span class="mh-highlight-wrap">
|
||||
@@ -22,16 +16,9 @@ Yellow marker sweep behind text. The most common mode.
|
||||
```
|
||||
|
||||
```css
|
||||
.mh-highlight-wrap {
|
||||
position: relative;
|
||||
display: inline;
|
||||
}
|
||||
.mh-highlight-bar {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -6px;
|
||||
right: -6px;
|
||||
bottom: 0;
|
||||
inset: 0 -6px; /* bleed past the text edges */
|
||||
background: #fdd835;
|
||||
opacity: 0.35;
|
||||
transform: scaleX(0);
|
||||
@@ -39,113 +26,46 @@ Yellow marker sweep behind text. The most common mode.
|
||||
border-radius: 3px;
|
||||
z-index: 0;
|
||||
}
|
||||
.mh-highlight-text {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
// Sweep in from left
|
||||
tl.to("#hl-1", { scaleX: 1, duration: 0.5, ease: "power2.out" }, 0.6);
|
||||
|
||||
// Optional: skew for hand-drawn feel
|
||||
// gsap.set("#hl-1", { skewX: -2 });
|
||||
```
|
||||
|
||||
### Multi-line Highlight
|
||||
|
||||
Stagger bars across multiple lines:
|
||||
|
||||
```js
|
||||
tl.to(
|
||||
".mh-highlight-bar",
|
||||
{
|
||||
scaleX: 1,
|
||||
duration: 0.5,
|
||||
ease: "power2.out",
|
||||
stagger: 0.3,
|
||||
},
|
||||
0.6,
|
||||
);
|
||||
// Optional hand-drawn skew: gsap.set("#hl-1", { skewX: -2 });
|
||||
// Multi-line: tl.to(".mh-highlight-bar", { scaleX: 1, ..., stagger: 0.3 }, 0.6);
|
||||
```
|
||||
|
||||
## 2. Circle Mode
|
||||
|
||||
Hand-drawn circle around text. Use `border-radius: 50%` with a slight rotation for organic feel.
|
||||
Hand-drawn ellipse around text — `border-radius: 50%` plus a slight rotation for organic feel.
|
||||
|
||||
```html
|
||||
<span class="mh-circle-wrap">
|
||||
<span class="mh-circle-text" id="circle-word">IMPORTANT</span>
|
||||
<span class="mh-circle-text">IMPORTANT</span>
|
||||
<span class="mh-circle-ring" id="circle-1"></span>
|
||||
</span>
|
||||
```
|
||||
|
||||
```css
|
||||
.mh-circle-wrap {
|
||||
position: relative;
|
||||
display: inline;
|
||||
}
|
||||
.mh-circle-text {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
.mh-circle-ring {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 130%;
|
||||
width: 130%; /* tight (short words): 150%; rounded-rect: 120% + border-radius: 30% */
|
||||
height: 160%;
|
||||
transform: translate(-50%, -50%) rotate(-3deg) scale(0);
|
||||
border: 3px solid #e53935;
|
||||
border-radius: 50%;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
// Circle scales in with a wobble
|
||||
tl.to(
|
||||
"#circle-1",
|
||||
{
|
||||
scale: 1,
|
||||
rotation: -3,
|
||||
duration: 0.6,
|
||||
ease: "back.out(1.7)",
|
||||
transformOrigin: "center center",
|
||||
},
|
||||
0.7,
|
||||
);
|
||||
```
|
||||
|
||||
### Variations
|
||||
|
||||
```css
|
||||
/* Tighter circle (for short words) */
|
||||
.mh-circle-ring.tight {
|
||||
width: 150%;
|
||||
height: 180%;
|
||||
}
|
||||
|
||||
/* Squared circle (rounded rectangle) */
|
||||
.mh-circle-ring.rounded {
|
||||
border-radius: 30%;
|
||||
width: 120%;
|
||||
height: 140%;
|
||||
}
|
||||
|
||||
/* Ellipse (wider than tall) */
|
||||
.mh-circle-ring.ellipse {
|
||||
width: 150%;
|
||||
height: 130%;
|
||||
border-radius: 50%;
|
||||
}
|
||||
tl.to("#circle-1", { scale: 1, rotation: -3, duration: 0.6, ease: "back.out(1.7)" }, 0.7);
|
||||
```
|
||||
|
||||
## 3. Burst Mode
|
||||
|
||||
Radiating lines from text center. Each line is a positioned div rotated to its angle.
|
||||
Radiating lines from text center — each line a positioned span rotated to its angle. Use ~12 lines at 30° steps and **vary `--len` (40–80px)**; equal lengths look mechanical.
|
||||
|
||||
```html
|
||||
<span class="mh-burst-wrap">
|
||||
@@ -153,36 +73,19 @@ Radiating lines from text center. Each line is a positioned div rotated to its a
|
||||
<span class="mh-burst-container" id="burst-1">
|
||||
<span class="mh-burst-line" style="--angle: 0deg; --len: 70px;"></span>
|
||||
<span class="mh-burst-line" style="--angle: 30deg; --len: 55px;"></span>
|
||||
<span class="mh-burst-line" style="--angle: 60deg; --len: 80px;"></span>
|
||||
<span class="mh-burst-line" style="--angle: 90deg; --len: 45px;"></span>
|
||||
<span class="mh-burst-line" style="--angle: 120deg; --len: 65px;"></span>
|
||||
<span class="mh-burst-line" style="--angle: 150deg; --len: 75px;"></span>
|
||||
<span class="mh-burst-line" style="--angle: 180deg; --len: 50px;"></span>
|
||||
<span class="mh-burst-line" style="--angle: 210deg; --len: 60px;"></span>
|
||||
<span class="mh-burst-line" style="--angle: 240deg; --len: 80px;"></span>
|
||||
<span class="mh-burst-line" style="--angle: 270deg; --len: 40px;"></span>
|
||||
<span class="mh-burst-line" style="--angle: 300deg; --len: 70px;"></span>
|
||||
<span class="mh-burst-line" style="--angle: 330deg; --len: 55px;"></span>
|
||||
<!-- …one line per 30° step through 330deg, --len varied 40-80px -->
|
||||
</span>
|
||||
</span>
|
||||
```
|
||||
|
||||
```css
|
||||
.mh-burst-wrap {
|
||||
position: relative;
|
||||
display: inline;
|
||||
}
|
||||
.mh-burst-text {
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
.mh-burst-container {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
width: 0;
|
||||
height: 0;
|
||||
z-index: 1;
|
||||
z-index: 1; /* text copy at z-index: 2 */
|
||||
}
|
||||
.mh-burst-line {
|
||||
position: absolute;
|
||||
@@ -199,7 +102,6 @@ Radiating lines from text center. Each line is a positioned div rotated to its a
|
||||
```
|
||||
|
||||
```js
|
||||
// All lines burst outward simultaneously with slight stagger
|
||||
tl.fromTo(
|
||||
"#burst-1 .mh-burst-line",
|
||||
{ scaleY: 0, opacity: 0 },
|
||||
@@ -208,11 +110,9 @@ tl.fromTo(
|
||||
);
|
||||
```
|
||||
|
||||
**Vary line lengths** (40-80px range) for an organic, hand-drawn feel. Equal lengths look mechanical.
|
||||
|
||||
## 4. Scribble Mode
|
||||
|
||||
Wavy SVG underlines and strikethroughs that draw themselves via `stroke-dashoffset`.
|
||||
Wavy SVG underline that draws itself via `stroke-dashoffset`.
|
||||
|
||||
```html
|
||||
<span class="mh-scribble-wrap">
|
||||
@@ -227,22 +127,14 @@ Wavy SVG underlines and strikethroughs that draw themselves via `stroke-dashoffs
|
||||
stroke-linecap="round"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</span>
|
||||
```
|
||||
|
||||
```css
|
||||
.mh-scribble-wrap {
|
||||
position: relative;
|
||||
display: inline;
|
||||
}
|
||||
.mh-scribble-text {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
}
|
||||
.mh-scribble-svg {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: -6px;
|
||||
bottom: -6px; /* strikethrough variant: top: 50%; transform: translateY(-50%) */
|
||||
width: 100%;
|
||||
height: 24px;
|
||||
z-index: 0;
|
||||
@@ -250,38 +142,17 @@ Wavy SVG underlines and strikethroughs that draw themselves via `stroke-dashoffs
|
||||
```
|
||||
|
||||
```js
|
||||
// Measure path length and set initial dash state
|
||||
var path = document.querySelector("#scribble-1");
|
||||
var len = path.getTotalLength();
|
||||
const path = document.querySelector("#scribble-1");
|
||||
const len = path.getTotalLength();
|
||||
gsap.set(path, { strokeDasharray: len, strokeDashoffset: len });
|
||||
|
||||
// Draw the line
|
||||
tl.to(
|
||||
"#scribble-1",
|
||||
{
|
||||
strokeDashoffset: 0,
|
||||
duration: 0.8,
|
||||
ease: "power1.inOut",
|
||||
},
|
||||
0.7,
|
||||
);
|
||||
tl.to("#scribble-1", { strokeDashoffset: 0, duration: 0.8, ease: "power1.inOut" }, 0.7);
|
||||
```
|
||||
|
||||
### Strikethrough Variant
|
||||
|
||||
Position the SVG at `top: 50%; transform: translateY(-50%)` instead of `bottom: -6px`.
|
||||
|
||||
### Wavy Path Generator
|
||||
|
||||
Scale the path's viewBox width to match text width. The wave pattern `Q x1,y1 x2,y2` alternates between `y=0` and `y=24` for a natural wobble. Adjust the control points for tighter or looser waves:
|
||||
|
||||
- **Tight waves**: smaller x-increments (25px per half-wave)
|
||||
- **Loose waves**: larger x-increments (50px per half-wave)
|
||||
- **Amplitude**: change the y range (0-24 for standard, 0-16 for subtle)
|
||||
Path tuning: the `Q` control points alternate y between 0 and 24 for a natural wobble. Tighter waves = smaller x-increments (~25px per half-wave); looser = ~50px; subtler amplitude = y range 0–16.
|
||||
|
||||
## 5. Sketchout Mode
|
||||
|
||||
Cross-hatch lines over de-emphasized text. Multiple angled lines create a "crossed out" effect.
|
||||
Cross-hatch over de-emphasized text — two angled lines create a "crossed out" effect.
|
||||
|
||||
```html
|
||||
<span class="mh-sketchout-wrap">
|
||||
@@ -294,22 +165,11 @@ Cross-hatch lines over de-emphasized text. Multiple angled lines create a "cross
|
||||
```
|
||||
|
||||
```css
|
||||
.mh-sketchout-wrap {
|
||||
position: relative;
|
||||
display: inline;
|
||||
}
|
||||
.mh-sketchout-text {
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
}
|
||||
.mh-sketchout-lines {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: -4px;
|
||||
right: -4px;
|
||||
bottom: 0;
|
||||
inset: 0 -4px;
|
||||
overflow: hidden;
|
||||
z-index: 1;
|
||||
z-index: 1; /* text at z-index: 0 — the lines cross OVER it */
|
||||
}
|
||||
.mh-sketchout-line {
|
||||
position: absolute;
|
||||
@@ -320,7 +180,6 @@ Cross-hatch lines over de-emphasized text. Multiple angled lines create a "cross
|
||||
height: 2px;
|
||||
background: #e53935;
|
||||
transform-origin: left center;
|
||||
transform: scaleX(0);
|
||||
}
|
||||
.mh-sketchout-fwd {
|
||||
transform: scaleX(0) rotate(-12deg);
|
||||
@@ -331,43 +190,19 @@ Cross-hatch lines over de-emphasized text. Multiple angled lines create a "cross
|
||||
```
|
||||
|
||||
```js
|
||||
// Forward slash draws first
|
||||
tl.to(
|
||||
"#sketchout-1 .mh-sketchout-fwd",
|
||||
{
|
||||
scaleX: 1,
|
||||
duration: 0.3,
|
||||
ease: "power2.out",
|
||||
},
|
||||
1.0,
|
||||
);
|
||||
|
||||
// Backward slash follows
|
||||
tl.to(
|
||||
"#sketchout-1 .mh-sketchout-bwd",
|
||||
{
|
||||
scaleX: 1,
|
||||
duration: 0.3,
|
||||
ease: "power2.out",
|
||||
},
|
||||
1.15,
|
||||
);
|
||||
// Forward slash first, backward follows
|
||||
tl.to("#sketchout-1 .mh-sketchout-fwd", { scaleX: 1, duration: 0.3, ease: "power2.out" }, 1.0);
|
||||
tl.to("#sketchout-1 .mh-sketchout-bwd", { scaleX: 1, duration: 0.3, ease: "power2.out" }, 1.15);
|
||||
```
|
||||
|
||||
## Combining Modes in Captions
|
||||
|
||||
Use mode cycling for visual variety across caption groups:
|
||||
Cycle modes across caption groups for visual variety — every 2-3 groups for high energy, 3-4 for medium, 4-5 for low:
|
||||
|
||||
```js
|
||||
var MODES = ["highlight", "circle", "burst", "scribble"];
|
||||
|
||||
GROUPS.forEach(function (group, gi) {
|
||||
var mode = MODES[gi % MODES.length];
|
||||
// Apply the mode's CSS pattern to emphasis words in this group
|
||||
group.emphasisWords.forEach(function (word) {
|
||||
applyMode(word.el, mode, tl, word.start);
|
||||
});
|
||||
const MODES = ["highlight", "circle", "burst", "scribble"];
|
||||
GROUPS.forEach((group, gi) => {
|
||||
const mode = MODES[gi % MODES.length];
|
||||
group.emphasisWords.forEach((word) => applyMode(word.el, mode, tl, word.start));
|
||||
});
|
||||
```
|
||||
|
||||
Cycle every 2-3 groups for high energy, every 3-4 for medium, every 4-5 for low.
|
||||
|
||||
Reference in New Issue
Block a user