mirror of
https://github.com/heygen-com/hyperframes.git
synced 2026-09-13 15:49:53 +00:00
Restores the 208 catalog items reverted after their previews 404'd in production, this time on the payload mechanism rather than the .html files that caused the outage. The generator no longer writes a preview document to docs/public. That writer, and the machinery under it, existed only to produce files the docs host discards, so it is gone rather than bypassed. Items now embed the composition itself via a payload, which is what the previous change already does for the items that were already in the catalog. The variables explorer is parked, not restored: it drove its preview through the same unpublished .html path, so it would have shown an empty frame. Items that declare variables get the live player plus the static variables table, and reconnecting the explorer to payloads is a follow-up.
449 lines
15 KiB
Plaintext
449 lines
15 KiB
Plaintext
---
|
|
title: "US Map"
|
|
description: "Animated US choropleth map with staggered state reveals, value labels, and gradient legend — pure inline SVG with GSAP"
|
|
---
|
|
|
|
import { InstallCommand } from "/snippets/install-command.jsx";
|
|
|
|
<iframe
|
|
className="w-full aspect-video rounded-xl border-0 bg-zinc-100 dark:bg-zinc-800"
|
|
title="us-map preview"
|
|
loading="lazy"
|
|
srcDoc={`<!doctype html><html><head><meta charset="utf-8"><style>html,body{margin:0;height:100%;overflow:hidden;background:transparent}hyperframes-player{display:block;width:100%;height:100%}</style><script src="https://cdn.jsdelivr.net/npm/@hyperframes/player@0.7/dist/hyperframes-player.global.js"><\/script></head><body><script>fetch("/public/catalog/blocks/us-map.json").then(function(r){return r.json()}).then(function(d){var p=document.createElement("hyperframes-player");p.setAttribute("srcdoc",d.html);p.setAttribute("controls","");p.setAttribute("autoplay","");p.setAttribute("loop","");p.setAttribute("muted","");p.setAttribute("poster","https://static.heygen.ai/hyperframes-oss/docs/images/catalog/blocks/us-map.png");document.body.appendChild(p)});<\/script></body></html>`}
|
|
/>
|
|
|
|
## Install
|
|
|
|
<InstallCommand command="npx hyperframes add us-map" />
|
|
|
|
That writes one file: `compositions/us-map.html`.
|
|
|
|
## Source
|
|
|
|
<Accordion title={`us-map.html`}>
|
|
|
|
```html
|
|
<!doctype html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8" />
|
|
<meta name="viewport" content="width=1920, height=1080" />
|
|
<title>US Map</title>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
<link
|
|
href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=block"
|
|
rel="stylesheet"
|
|
/>
|
|
<script src="https://cdn.jsdelivr.net/npm/gsap@3.14.2/dist/gsap.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/d3@7/dist/d3.min.js"></script>
|
|
<script src="https://cdn.jsdelivr.net/npm/topojson-client@3.1.0/dist/topojson-client.min.js"></script>
|
|
<style>
|
|
* {
|
|
margin: 0;
|
|
padding: 0;
|
|
box-sizing: border-box;
|
|
}
|
|
html,
|
|
body {
|
|
margin: 0;
|
|
width: 1920px;
|
|
height: 1080px;
|
|
overflow: hidden;
|
|
background-color: #0f172a;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div
|
|
id="us-map"
|
|
data-composition-id="us-map"
|
|
data-width="1920"
|
|
data-height="1080"
|
|
data-start="0"
|
|
data-duration="12"
|
|
>
|
|
<div class="map-container">
|
|
<div class="header">
|
|
<h1 class="headline">Population Density by State</h1>
|
|
<p class="subtitle">Residents per square mile, 2024 Census estimates</p>
|
|
</div>
|
|
|
|
<svg class="map-svg" viewBox="0 0 1400 820" preserveAspectRatio="xMidYMid meet">
|
|
<g class="states-group"></g>
|
|
<g class="labels-group"></g>
|
|
</svg>
|
|
|
|
<div class="legend">
|
|
<span class="legend-label legend-low">Low</span>
|
|
<div class="legend-bar"></div>
|
|
<span class="legend-label legend-high">High</span>
|
|
</div>
|
|
|
|
<div class="source">Source: U.S. Census Bureau</div>
|
|
</div>
|
|
|
|
<style>
|
|
#us-map {
|
|
width: 1920px;
|
|
height: 1080px;
|
|
background: linear-gradient(145deg, #0f172a 0%, #1e293b 100%);
|
|
font-family: "Inter", sans-serif;
|
|
color: #e2e8f0;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
overflow: hidden;
|
|
}
|
|
|
|
#us-map .map-container {
|
|
width: 1700px;
|
|
height: 950px;
|
|
display: flex;
|
|
flex-direction: column;
|
|
align-items: center;
|
|
position: relative;
|
|
}
|
|
|
|
#us-map .header {
|
|
text-align: center;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
#us-map .headline {
|
|
font-size: 52px;
|
|
font-weight: 700;
|
|
letter-spacing: -0.02em;
|
|
color: #f8fafc;
|
|
clip-path: inset(0 100% 0 0);
|
|
}
|
|
|
|
#us-map .subtitle {
|
|
font-size: 20px;
|
|
font-weight: 300;
|
|
color: #94a3b8;
|
|
margin-top: 8px;
|
|
opacity: 0;
|
|
}
|
|
|
|
#us-map .map-svg {
|
|
width: 1500px;
|
|
height: 750px;
|
|
overflow: visible;
|
|
}
|
|
|
|
#us-map .state-path {
|
|
stroke: #1e293b;
|
|
stroke-width: 1.2;
|
|
opacity: 0;
|
|
cursor: default;
|
|
}
|
|
|
|
#us-map .state-label {
|
|
font-size: 10px;
|
|
font-weight: 500;
|
|
fill: #f8fafc;
|
|
text-anchor: middle;
|
|
dominant-baseline: central;
|
|
opacity: 0;
|
|
pointer-events: none;
|
|
text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
|
|
}
|
|
|
|
#us-map .legend {
|
|
display: flex;
|
|
align-items: center;
|
|
gap: 12px;
|
|
margin-top: 16px;
|
|
opacity: 0;
|
|
}
|
|
|
|
#us-map .legend-bar {
|
|
width: 300px;
|
|
height: 12px;
|
|
border-radius: 6px;
|
|
background: linear-gradient(90deg, #1e3a5f, #2563eb, #7c3aed, #ec4899);
|
|
}
|
|
|
|
#us-map .legend-label {
|
|
font-size: 14px;
|
|
font-weight: 500;
|
|
color: #94a3b8;
|
|
}
|
|
|
|
#us-map .source {
|
|
position: absolute;
|
|
bottom: 0;
|
|
right: 40px;
|
|
font-size: 12px;
|
|
color: #475569;
|
|
opacity: 0;
|
|
}
|
|
</style>
|
|
|
|
<script>
|
|
(function () {
|
|
const US_TOPO_URL = "https://cdn.jsdelivr.net/npm/us-atlas@3/states-10m.json";
|
|
|
|
const stateData = {
|
|
"01": { name: "Alabama", abbr: "AL", density: 99.2 },
|
|
"02": { name: "Alaska", abbr: "AK", density: 1.3 },
|
|
"04": { name: "Arizona", abbr: "AZ", density: 63.5 },
|
|
"05": { name: "Arkansas", abbr: "AR", density: 58.1 },
|
|
"06": { name: "California", abbr: "CA", density: 253.9 },
|
|
"08": { name: "Colorado", abbr: "CO", density: 56.5 },
|
|
"09": { name: "Connecticut", abbr: "CT", density: 738.1 },
|
|
10: { name: "Delaware", abbr: "DE", density: 508.0 },
|
|
12: { name: "Florida", abbr: "FL", density: 410.5 },
|
|
13: { name: "Georgia", abbr: "GA", density: 185.6 },
|
|
15: { name: "Hawaii", abbr: "HI", density: 226.6 },
|
|
16: { name: "Idaho", abbr: "ID", density: 22.8 },
|
|
17: { name: "Illinois", abbr: "IL", density: 228.2 },
|
|
18: { name: "Indiana", abbr: "IN", density: 186.7 },
|
|
19: { name: "Iowa", abbr: "IA", density: 57.1 },
|
|
20: { name: "Kansas", abbr: "KS", density: 35.9 },
|
|
21: { name: "Kentucky", abbr: "KY", density: 113.2 },
|
|
22: { name: "Louisiana", abbr: "LA", density: 107.7 },
|
|
23: { name: "Maine", abbr: "ME", density: 43.8 },
|
|
24: { name: "Maryland", abbr: "MD", density: 636.1 },
|
|
25: { name: "Massachusetts", abbr: "MA", density: 901.2 },
|
|
26: { name: "Michigan", abbr: "MI", density: 176.4 },
|
|
27: { name: "Minnesota", abbr: "MN", density: 71.7 },
|
|
28: { name: "Mississippi", abbr: "MS", density: 63.5 },
|
|
29: { name: "Missouri", abbr: "MO", density: 89.5 },
|
|
30: { name: "Montana", abbr: "MT", density: 7.6 },
|
|
31: { name: "Nebraska", abbr: "NE", density: 25.3 },
|
|
32: { name: "Nevada", abbr: "NV", density: 28.3 },
|
|
33: { name: "New Hampshire", abbr: "NH", density: 153.2 },
|
|
34: { name: "New Jersey", abbr: "NJ", density: 1263.0 },
|
|
35: { name: "New Mexico", abbr: "NM", density: 17.4 },
|
|
36: { name: "New York", abbr: "NY", density: 415.3 },
|
|
37: { name: "North Carolina", abbr: "NC", density: 214.7 },
|
|
38: { name: "North Dakota", abbr: "ND", density: 11.3 },
|
|
39: { name: "Ohio", abbr: "OH", density: 288.8 },
|
|
40: { name: "Oklahoma", abbr: "OK", density: 57.7 },
|
|
41: { name: "Oregon", abbr: "OR", density: 43.9 },
|
|
42: { name: "Pennsylvania", abbr: "PA", density: 286.1 },
|
|
44: { name: "Rhode Island", abbr: "RI", density: 1061.4 },
|
|
45: { name: "South Carolina", abbr: "SC", density: 170.2 },
|
|
46: { name: "South Dakota", abbr: "SD", density: 12.2 },
|
|
47: { name: "Tennessee", abbr: "TN", density: 167.0 },
|
|
48: { name: "Texas", abbr: "TX", density: 112.8 },
|
|
49: { name: "Utah", abbr: "UT", density: 40.3 },
|
|
50: { name: "Vermont", abbr: "VT", density: 68.0 },
|
|
51: { name: "Virginia", abbr: "VA", density: 218.6 },
|
|
53: { name: "Washington", abbr: "WA", density: 115.9 },
|
|
54: { name: "West Virginia", abbr: "WV", density: 74.6 },
|
|
55: { name: "Wisconsin", abbr: "WI", density: 108.8 },
|
|
56: { name: "Wyoming", abbr: "WY", density: 5.8 },
|
|
};
|
|
|
|
const maxDensity = 500;
|
|
|
|
function densityToColor(d) {
|
|
const t = Math.min(d / maxDensity, 1);
|
|
if (t < 0.33) {
|
|
const s = t / 0.33;
|
|
return lerpColor([30, 58, 95], [37, 99, 235], s);
|
|
} else if (t < 0.66) {
|
|
const s = (t - 0.33) / 0.33;
|
|
return lerpColor([37, 99, 235], [124, 58, 237], s);
|
|
} else {
|
|
const s = (t - 0.66) / 0.34;
|
|
return lerpColor([124, 58, 237], [236, 72, 153], s);
|
|
}
|
|
}
|
|
|
|
function lerpColor(a, b, t) {
|
|
const r = Math.round(a[0] + (b[0] - a[0]) * t);
|
|
const g = Math.round(a[1] + (b[1] - a[1]) * t);
|
|
const bl = Math.round(a[2] + (b[2] - a[2]) * t);
|
|
return "rgb(" + r + "," + g + "," + bl + ")";
|
|
}
|
|
|
|
const svg = document.querySelector("#us-map .map-svg");
|
|
const statesGroup = svg.querySelector(".states-group");
|
|
const labelsGroup = svg.querySelector(".labels-group");
|
|
const tl = gsap.timeline({ paused: true });
|
|
|
|
fetch(US_TOPO_URL)
|
|
.then((r) => r.json())
|
|
.then((us) => {
|
|
const states = topojson.feature(us, us.objects.states).features;
|
|
const projection = d3
|
|
.geoAlbersUsa()
|
|
.fitSize([1380, 800], { type: "FeatureCollection", features: states });
|
|
const path = d3.geoPath().projection(projection);
|
|
|
|
const sortedStates = states
|
|
.filter((f) => stateData[String(f.id).padStart(2, "0")])
|
|
.sort((a, b) => {
|
|
const aData = stateData[String(a.id).padStart(2, "0")];
|
|
const bData = stateData[String(b.id).padStart(2, "0")];
|
|
return aData.density - bData.density;
|
|
});
|
|
|
|
sortedStates.forEach((feature) => {
|
|
const fips = String(feature.id).padStart(2, "0");
|
|
const info = stateData[fips];
|
|
if (!info) return;
|
|
|
|
const d = path(feature);
|
|
if (!d) return;
|
|
|
|
const el = document.createElementNS("http://www.w3.org/2000/svg", "path");
|
|
el.setAttribute("d", d);
|
|
el.setAttribute("class", "state-path");
|
|
el.setAttribute("data-state", info.abbr);
|
|
el.setAttribute("data-density", info.density);
|
|
el.style.fill = densityToColor(info.density);
|
|
statesGroup.appendChild(el);
|
|
|
|
const centroid = path.centroid(feature);
|
|
if (centroid[0] && centroid[1]) {
|
|
const label = document.createElementNS("http://www.w3.org/2000/svg", "text");
|
|
label.setAttribute("x", centroid[0]);
|
|
label.setAttribute("y", centroid[1]);
|
|
label.setAttribute("class", "state-label");
|
|
label.textContent = info.abbr;
|
|
labelsGroup.appendChild(label);
|
|
}
|
|
});
|
|
|
|
buildTimeline();
|
|
});
|
|
|
|
function buildTimeline() {
|
|
const statePaths = statesGroup.querySelectorAll(".state-path");
|
|
const stateLabels = labelsGroup.querySelectorAll(".state-label");
|
|
|
|
tl.to(
|
|
"#us-map .headline",
|
|
{
|
|
clipPath: "inset(0 0% 0 0)",
|
|
duration: 1.0,
|
|
ease: "power2.inOut",
|
|
},
|
|
0,
|
|
);
|
|
|
|
tl.to(
|
|
"#us-map .subtitle",
|
|
{
|
|
opacity: 1,
|
|
duration: 0.6,
|
|
ease: "power2.out",
|
|
},
|
|
0.4,
|
|
);
|
|
|
|
tl.to(
|
|
statePaths,
|
|
{
|
|
opacity: 1,
|
|
scale: 1,
|
|
duration: 0.4,
|
|
stagger: {
|
|
each: 0.06,
|
|
from: "center",
|
|
},
|
|
ease: "back.out(1.4)",
|
|
},
|
|
1.0,
|
|
);
|
|
|
|
tl.to(
|
|
stateLabels,
|
|
{
|
|
opacity: 1,
|
|
duration: 0.3,
|
|
stagger: {
|
|
each: 0.04,
|
|
from: "center",
|
|
},
|
|
ease: "power2.out",
|
|
},
|
|
3.5,
|
|
);
|
|
|
|
tl.to(
|
|
"#us-map .legend",
|
|
{
|
|
opacity: 1,
|
|
y: 0,
|
|
duration: 0.6,
|
|
ease: "power2.out",
|
|
},
|
|
5.0,
|
|
);
|
|
|
|
tl.to(
|
|
"#us-map .source",
|
|
{
|
|
opacity: 1,
|
|
duration: 0.5,
|
|
ease: "power2.out",
|
|
},
|
|
5.5,
|
|
);
|
|
|
|
const highlightStates = ["CA", "NY", "TX", "FL", "NJ"];
|
|
highlightStates.forEach((abbr, i) => {
|
|
const el = statesGroup.querySelector('[data-state="' + abbr + '"]');
|
|
if (!el) return;
|
|
|
|
tl.to(
|
|
el,
|
|
{
|
|
filter: "brightness(1.6) drop-shadow(0 0 8px rgba(255,255,255,0.4))",
|
|
stroke: "#f8fafc",
|
|
strokeWidth: 2.5,
|
|
duration: 0.5,
|
|
ease: "power2.out",
|
|
},
|
|
6.5 + i * 0.8,
|
|
);
|
|
|
|
tl.to(
|
|
el,
|
|
{
|
|
filter: "brightness(1) drop-shadow(0 0 0px rgba(255,255,255,0))",
|
|
stroke: "#1e293b",
|
|
strokeWidth: 1.2,
|
|
duration: 0.5,
|
|
ease: "power2.in",
|
|
},
|
|
7.0 + i * 0.8,
|
|
);
|
|
});
|
|
|
|
window.__timelines = window.__timelines || {};
|
|
window.__timelines["us-map"] = tl;
|
|
}
|
|
})();
|
|
</script>
|
|
</div>
|
|
</body>
|
|
</html>
|
|
```
|
|
|
|
</Accordion>
|
|
|
|
## Usage
|
|
|
|
After installing, add the block to your host composition:
|
|
|
|
```html
|
|
<div data-composition-id="us-map" data-composition-src="compositions/us-map.html" data-start="0" data-duration="12" data-track-index="1" data-width="1920" data-height="1080"></div>
|
|
```
|
|
|
|
{/* hf:generated-footer */}
|
|
|
|
Tagged `data` `map` `geography` `usa` `choropleth`.
|
|
|
|
## Related topics
|
|
|
|
- [Browse the complete Catalog](/catalog)
|
|
- [Add assets and Catalog items in Studio](/studio/assets-and-blocks)
|
|
- [Build a richer composition](/go-further)
|