mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-11-03 04:07:03 +00:00
* docs: one Troubleshooting page, four tabs, and the start pages folded * docs: keep how Studio app updates arrive on the help page Folding the Studio app troubleshooting page into /help dropped its "How updates arrive" section: the update button, Settings > Updates and the Mac menu route, the Automatic updates toggle, and what to do when an update stalls or cannot check. It is back word for word; every label is still in the app.
214 lines
11 KiB
Plaintext
214 lines
11 KiB
Plaintext
---
|
|
title: "Pendulum Swing"
|
|
description: "Content hangs from a fixed pivot and swings into rest with gravity-damped oscillation, like a hanging sign settling on its hook."
|
|
mode: "frame"
|
|
---
|
|
|
|
import { CatalogDetail, CatalogSlot } from "/snippets/catalog-detail.jsx";
|
|
import { InstallCommand } from "/snippets/install-command.jsx";
|
|
|
|
<CatalogDetail
|
|
previewSrc="/public/catalog/components/pendulum-swing.json"
|
|
compositionId="pendulum-swing"
|
|
compositionSrc="compositions/components/pendulum-swing.html"
|
|
title="Pendulum Swing"
|
|
description="Content hangs from a fixed pivot and swings into rest with gravity-damped oscillation, like a hanging sign settling on its hook."
|
|
variables={[{"id":"pivot","type":"enum","role":"layout","label":"Pivot","description":"Where the content hangs from. The swing rotates about this point, so the top edge stays pinned.","default":"top-center","options":[{"value":"top-center","label":"Top center"},{"value":"top-left","label":"Top left"},{"value":"top-right","label":"Top right"}]},{"id":"amplitude","type":"enum","role":"motion","label":"Amplitude","description":"How far the first swing reaches before it settles. Every later swing scales with it, so the pendulum still comes to rest upright.","default":"standard","options":[{"value":"gentle","label":"Gentle"},{"value":"standard","label":"Standard"},{"value":"wide","label":"Wide"}]},{"id":"direction","type":"enum","role":"motion","label":"Direction","description":"Which side the pendulum swings in from: right lifts the first swing clockwise, left mirrors it.","default":"right","options":[{"value":"right","label":"Right"},{"value":"left","label":"Left"}]},{"id":"tone","type":"enum","role":"style","label":"Tone","description":"Content colour: ink for light frames, paper for dark ones, accent rides --brand.","default":"ink","options":[{"value":"ink","label":"Ink"},{"value":"paper","label":"Paper"},{"value":"accent","label":"Accent"}]}]}
|
|
meta={{"category":"Motion Scenes","badge":"Stable","codeLines":130}}
|
|
attribution={{"path":"registry/components/pendulum-swing","tags":["motion-primitive","physics","swing","settle","reveal"]}}
|
|
hasCode
|
|
rawUrl="https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry/components/pendulum-swing/pendulum-swing.html"
|
|
>
|
|
|
|
<CatalogSlot slot="code">
|
|
|
|
```html pendulum-swing.html
|
|
<!--
|
|
Pendulum Swing - motion primitive for HyperFrames.
|
|
|
|
Paste the markup, CSS and script into a composition. Drive the timeline
|
|
integration from a paused GSAP timeline so renders remain deterministic.
|
|
|
|
Variables. The script reads each one, falls back to the declared default on
|
|
anything missing or unrecognised, and writes the result as a custom property
|
|
the CSS below consumes. The timeline recipe stays untouched by them: it keeps
|
|
easing one unitless swing value from 1 down to 0, and these decide where the
|
|
content hangs from, how far the first swing reaches, which way it enters and
|
|
what colour it is.
|
|
|
|
- pivot (top-center | top-left | top-right, default top-center): where the
|
|
content hangs from. The swing rotates about this point, so the pinned top
|
|
edge never drifts.
|
|
- amplitude (gentle | standard | wide, default standard): how far the first
|
|
swing reaches, 18deg, 34deg or 52deg. Every later swing scales with it, so
|
|
whatever the reach the pendulum still comes to rest upright.
|
|
- direction (right | left, default right): which side the pendulum swings in
|
|
from. right lifts the first swing clockwise, left mirrors it.
|
|
- tone (ink | paper | accent, default ink): content colour. ink is
|
|
near-black for light frames, paper near-white for dark ones, accent rides
|
|
--brand.
|
|
|
|
On every default the result is identical to the original: a near-black sign
|
|
hanging from its top centre, entering from the right through a 34deg swing.
|
|
|
|
Motion note. The swing is a damped oscillation about the pivot: the content
|
|
passes through upright several times, each pass smaller, until it settles.
|
|
It rides a single unitless custom property (--hf-swing-angle) that the recipe
|
|
eases 1 -> 0, so rotation interpolates sub-pixel and stays smooth under the
|
|
seek-by-frame capture engine; nothing here animates a layout or reflow
|
|
property.
|
|
-->
|
|
|
|
<div
|
|
class="hf-catalog-pendulum-swing"
|
|
data-composition-variables='[
|
|
{ "id": "pivot", "type": "enum", "role": "layout", "label": "Pivot", "description": "Where the content hangs from. The swing rotates about this point, so the top edge stays pinned.", "default": "top-center", "options": [{ "value": "top-center", "label": "Top center" }, { "value": "top-left", "label": "Top left" }, { "value": "top-right", "label": "Top right" }] },
|
|
{ "id": "amplitude", "type": "enum", "role": "motion", "label": "Amplitude", "description": "How far the first swing reaches before it settles. Every later swing scales with it, so the pendulum still comes to rest upright.", "default": "standard", "options": [{ "value": "gentle", "label": "Gentle" }, { "value": "standard", "label": "Standard" }, { "value": "wide", "label": "Wide" }] },
|
|
{ "id": "direction", "type": "enum", "role": "motion", "label": "Direction", "description": "Which side the pendulum swings in from: right lifts the first swing clockwise, left mirrors it.", "default": "right", "options": [{ "value": "right", "label": "Right" }, { "value": "left", "label": "Left" }] },
|
|
{ "id": "tone", "type": "enum", "role": "style", "label": "Tone", "description": "Content colour: ink for light frames, paper for dark ones, accent rides --brand.", "default": "ink", "options": [{ "value": "ink", "label": "Ink" }, { "value": "paper", "label": "Paper" }, { "value": "accent", "label": "Accent" }] }
|
|
]'
|
|
>
|
|
<span class="hf-catalog-pendulum-swing__body">Hanging Sign</span>
|
|
</div>
|
|
|
|
<style>
|
|
.hf-catalog-pendulum-swing {
|
|
display: inline-block;
|
|
/* The pendulum swings about the hanging pivot. The angle stays a unitless
|
|
factor so the timeline can ease it 1 -> 0 (a damped oscillation through
|
|
upright) while amplitude and direction, set once from the variables,
|
|
decide the reach and the side. */
|
|
transform-origin: var(--hf-swing-origin, 50% 0%);
|
|
transform: rotate(
|
|
calc(var(--hf-swing-angle, 0) * var(--hf-swing-dir, 1) * var(--hf-swing-max, 34deg))
|
|
);
|
|
}
|
|
.hf-catalog-pendulum-swing__body {
|
|
display: inline-block;
|
|
padding: 22px 40px;
|
|
border-radius: 14px;
|
|
/* Tone sets a background/foreground pair so the sign stays legible whatever
|
|
the frame: the fill is the tone colour, the text its paired contrast. */
|
|
background: var(--hf-swing-bg, #18181b);
|
|
color: var(--hf-swing-fg, #fafafa);
|
|
font-family: Inter, system-ui, sans-serif;
|
|
font-weight: 800;
|
|
font-size: 54px;
|
|
letter-spacing: -0.02em;
|
|
white-space: nowrap;
|
|
}
|
|
</style>
|
|
|
|
<script>
|
|
(function () {
|
|
"use strict";
|
|
|
|
var vars =
|
|
window.__hyperframes && window.__hyperframes.getVariables
|
|
? window.__hyperframes.getVariables()
|
|
: {};
|
|
|
|
// Unrecognised overrides return to their declared defaults.
|
|
function pick(table, value, fallback) {
|
|
return Object.prototype.hasOwnProperty.call(table, value) ? value : fallback;
|
|
}
|
|
|
|
var pivots = {
|
|
"top-center": "50% 0%",
|
|
"top-left": "0% 0%",
|
|
"top-right": "100% 0%",
|
|
};
|
|
var amplitudes = { gentle: "18deg", standard: "34deg", wide: "52deg" };
|
|
var directions = { right: 1, left: -1 };
|
|
// Each tone is a fill/text pair so the sign keeps AA contrast on any frame.
|
|
var tones = {
|
|
ink: { bg: "#18181b", fg: "#fafafa" },
|
|
paper: { bg: "#fafafa", fg: "#18181b" },
|
|
accent: { bg: "var(--brand, #34d399)", fg: "#0b0b0c" },
|
|
};
|
|
|
|
var origin = pivots[pick(pivots, vars.pivot, "top-center")];
|
|
var maxAngle = amplitudes[pick(amplitudes, vars.amplitude, "standard")];
|
|
var dir = directions[pick(directions, vars.direction, "right")];
|
|
var tone = tones[pick(tones, vars.tone, "ink")];
|
|
|
|
var roots = document.querySelectorAll(".hf-catalog-pendulum-swing");
|
|
for (var i = 0; i < roots.length; i += 1) {
|
|
var root = roots[i];
|
|
root.style.setProperty("--hf-swing-origin", origin);
|
|
root.style.setProperty("--hf-swing-max", maxAngle);
|
|
root.style.setProperty("--hf-swing-dir", String(dir));
|
|
root.style.setProperty("--hf-swing-bg", tone.bg);
|
|
root.style.setProperty("--hf-swing-fg", tone.fg);
|
|
}
|
|
})();
|
|
</script>
|
|
|
|
<!--
|
|
Timeline integration. The swing settles like a real pendulum: it passes
|
|
through upright several times, each pass smaller, until it comes to rest. That
|
|
is a cosine (the swings) inside an exponential decay (the damping), normalised
|
|
so the tween still lands exactly upright at the end.
|
|
|
|
const swingSettle = (p) => 1 - Math.exp(-2.3 * p) * Math.cos(3.5 * Math.PI * p);
|
|
tl.fromTo('.hf-catalog-pendulum-swing', { '--hf-swing-angle': 1 }, { '--hf-swing-angle': 0, duration: 2.9, ease: swingSettle }, startTime);
|
|
-->
|
|
```
|
|
|
|
</CatalogSlot>
|
|
|
|
<CatalogSlot slot="install">
|
|
|
|
<InstallCommand command="npx hyperframes add pendulum-swing" item="pendulum-swing" />
|
|
|
|
That writes one file: `compositions/components/pendulum-swing.html`.
|
|
|
|
</CatalogSlot>
|
|
|
|
<CatalogSlot slot="docs">
|
|
|
|
## Paste it into your composition
|
|
|
|
Open `compositions/components/pendulum-swing.html` and copy what is inside into your own composition.
|
|
The file opens with a comment header that walks you through it.
|
|
|
|
A component has no size or duration of its own. It takes both from the composition
|
|
you paste it into.
|
|
|
|
## Variables
|
|
|
|
Every one of these has a default, so the piece works untouched. Set the ones you
|
|
want to change on the element:
|
|
|
|
| Variable | Default | Accepts | What it does |
|
|
| --- | --- | --- | --- |
|
|
| `pivot` | `top-center` | `top-center`, `top-left`, `top-right` | Where the content hangs from. The swing rotates about this point, so the top edge stays pinned. |
|
|
| `amplitude` | `standard` | `gentle`, `standard`, `wide` | How far the first swing reaches before it settles. Every later swing scales with it, so the pendulum still comes to rest upright. |
|
|
| `direction` | `right` | `right`, `left` | Which side the pendulum swings in from: right lifts the first swing clockwise, left mirrors it. |
|
|
| `tone` | `ink` | `ink`, `paper`, `accent` | Content colour: ink for light frames, paper for dark ones, accent rides --brand. |
|
|
|
|
Set them with `data-variable-values` on the element that mounts it. These are the
|
|
defaults, so this behaves exactly like the preview above until you change one:
|
|
|
|
```html wrap
|
|
<div
|
|
data-composition-id="pendulum-swing"
|
|
data-composition-src="compositions/components/pendulum-swing.html"
|
|
data-variable-values='{"pivot":"top-center","amplitude":"standard","direction":"right","tone":"ink"}'
|
|
></div>
|
|
```
|
|
|
|
{/* hf:generated-footer */}
|
|
|
|
Tagged `motion-primitive` `physics` `swing` `settle` `reveal`.
|
|
|
|
## Related topics
|
|
|
|
- [Browse the complete Catalog](/catalog)
|
|
- [Add assets and Catalog items in the browser editor](/studio/assets-and-blocks)
|
|
- [Put blocks together into one video](/concepts/compositions)
|
|
|
|
</CatalogSlot>
|
|
|
|
</CatalogDetail>
|