reszta poprawek
This commit is contained in:
+70
@@ -0,0 +1,70 @@
|
||||
import React from 'react';
|
||||
import {Easing, interpolate} from 'remotion';
|
||||
|
||||
// Reusable country label. Supply typography and final values from the consuming project; the CSS custom
|
||||
// properties below provide neutral fallbacks. Positioned by its centre (x,y in screen px).
|
||||
export const CountryLabel: React.FC<{
|
||||
name: string;
|
||||
color: string;
|
||||
reveal: number;
|
||||
x: number;
|
||||
y: number;
|
||||
}> = ({name, color, reveal, x, y}) => {
|
||||
const e = interpolate(reveal, [0, 1], [0, 1], {
|
||||
extrapolateLeft: 'clamp',
|
||||
extrapolateRight: 'clamp',
|
||||
easing: Easing.bezier(0.3333333333333333, 1, 0.6666666666666666, 1),
|
||||
});
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
position: 'absolute',
|
||||
left: x,
|
||||
top: y,
|
||||
transform: 'translate(-50%, -50%)',
|
||||
pointerEvents: 'none',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
opacity: e,
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
transform: `translateY(${(1 - e) * 16}px)`,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
{/* accent rule — draws out from the centre in the country's colour */}
|
||||
<div
|
||||
style={{
|
||||
width: 64,
|
||||
height: 3,
|
||||
borderRadius: 2,
|
||||
background: color,
|
||||
transform: `scaleX(${e})`,
|
||||
boxShadow: `0 0 10px ${color}`,
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
style={{
|
||||
fontFamily: 'var(--map-label-font, system-ui, sans-serif)',
|
||||
fontWeight: 'var(--map-label-weight, 600)',
|
||||
fontSize: 'var(--map-label-size, 34px)',
|
||||
letterSpacing: 'var(--map-label-tracking, 0.16em)',
|
||||
textTransform: 'uppercase',
|
||||
color: 'var(--map-label-color, #ffffff)',
|
||||
textShadow: 'var(--map-label-shadow, 0 2px 18px rgba(0,0,0,0.9))',
|
||||
marginTop: 13,
|
||||
paddingLeft: 'var(--map-label-tracking, 0.16em)',
|
||||
whiteSpace: 'nowrap',
|
||||
}}
|
||||
>
|
||||
{name}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
// Surface an existing MapTiler Planet feature without maintaining duplicate GeoJSON.
|
||||
// Paint animation is deterministic; geometry slicing is not. For a source-to-end line draw,
|
||||
// bake the selected feature to ordered GeoJSON and use RiverReveal.tsx instead.
|
||||
|
||||
type VectorLayerType = 'fill' | 'line' | 'circle' | 'symbol';
|
||||
|
||||
export type MapTilerVectorElement = {
|
||||
id: string;
|
||||
sourceLayer: string;
|
||||
type: VectorLayerType;
|
||||
filter?: unknown[];
|
||||
minzoom?: number;
|
||||
maxzoom?: number;
|
||||
layout?: Record<string, unknown>;
|
||||
paint: Record<string, unknown>;
|
||||
};
|
||||
|
||||
const SOURCE_ID = 'maptiler-planet';
|
||||
|
||||
export const addMapTilerVectorElement = (
|
||||
map: any,
|
||||
apiKey: string,
|
||||
element: MapTilerVectorElement,
|
||||
beforeId?: string,
|
||||
) => {
|
||||
if (!map.getSource(SOURCE_ID)) {
|
||||
map.addSource(SOURCE_ID, {
|
||||
type: 'vector',
|
||||
url: `https://api.maptiler.com/tiles/v3/tiles.json?key=${apiKey}`,
|
||||
});
|
||||
}
|
||||
|
||||
if (map.getLayer(element.id)) return;
|
||||
|
||||
map.addLayer(
|
||||
{
|
||||
id: element.id,
|
||||
type: element.type,
|
||||
source: SOURCE_ID,
|
||||
'source-layer': element.sourceLayer,
|
||||
...(element.filter ? {filter: element.filter} : {}),
|
||||
...(element.minzoom === undefined ? {} : {minzoom: element.minzoom}),
|
||||
...(element.maxzoom === undefined ? {} : {maxzoom: element.maxzoom}),
|
||||
...(element.layout ? {layout: element.layout} : {}),
|
||||
paint: element.paint,
|
||||
},
|
||||
beforeId,
|
||||
);
|
||||
};
|
||||
|
||||
export const setVectorElementPaint = (
|
||||
map: any,
|
||||
layerId: string,
|
||||
paint: Record<string, unknown>,
|
||||
) => {
|
||||
for (const [property, value] of Object.entries(paint)) {
|
||||
map.setPaintProperty(layerId, property, value);
|
||||
}
|
||||
};
|
||||
|
||||
// Example:
|
||||
//
|
||||
// addMapTilerVectorElement(map, process.env.REMOTION_MAPTILER_KEY!, {
|
||||
// id: "story-river",
|
||||
// sourceLayer: "waterway",
|
||||
// type: "line",
|
||||
// filter: [
|
||||
// "all",
|
||||
// ["==", ["get", "class"], "river"],
|
||||
// ["==", ["coalesce", ["get", "name_en"], ["get", "name"]], "Yarlung Tsangpo"],
|
||||
// ],
|
||||
// layout: {"line-cap": "round", "line-join": "round"},
|
||||
// paint: {"line-color": "#E8F7FF", "line-width": 3, "line-opacity": 0},
|
||||
// });
|
||||
//
|
||||
// Per Remotion frame:
|
||||
// setVectorElementPaint(map, "story-river", {
|
||||
// "line-opacity": reveal,
|
||||
// "line-width": 2 + reveal * 2,
|
||||
// });
|
||||
+363
@@ -0,0 +1,363 @@
|
||||
import * as maptilersdk from '@maptiler/sdk';
|
||||
import * as turf from '@turf/turf';
|
||||
import React, {useEffect, useRef, useState} from 'react';
|
||||
import '@maptiler/sdk/dist/maptiler-sdk.css';
|
||||
import {
|
||||
AbsoluteFill,
|
||||
Easing,
|
||||
continueRender,
|
||||
delayRender,
|
||||
interpolate,
|
||||
useCurrentFrame,
|
||||
useVideoConfig,
|
||||
} from 'remotion';
|
||||
import {CountryLabel} from './CountryLabel';
|
||||
import countryMeta from './sample-data/country-meta.json';
|
||||
import flowCoords from './sample-data/yarlung-flow.json';
|
||||
import {COLORS, COUNTRY, COUNTRY_DARK, FILL_OPACITY} from './tokens';
|
||||
|
||||
// Sample route reveal. Replace the imported sample geometry, names, timing, and visual tokens in the
|
||||
// consuming production. The renderer stays static; approved centre/zoom motion is a CSS plate transform.
|
||||
|
||||
maptilersdk.config.apiKey = process.env.REMOTION_MAPTILER_KEY as string;
|
||||
|
||||
const line = turf.lineString(flowCoords as [number, number][]);
|
||||
const lineKm = turf.length(line);
|
||||
|
||||
const START = {
|
||||
center: [89.6, 27.7] as [number, number],
|
||||
zoom: 4.75,
|
||||
pitch: 0,
|
||||
bearing: 0,
|
||||
};
|
||||
const END = {
|
||||
center: [90.2, 27.0] as [number, number],
|
||||
zoom: 5.05,
|
||||
pitch: 0,
|
||||
bearing: 0,
|
||||
};
|
||||
const lerp = (a: number, b: number, t: number) => a + (b - a) * t;
|
||||
const clamp01 = (v: number) => Math.max(0, Math.min(1, v));
|
||||
|
||||
const ORDER = ['china', 'india', 'bangladesh'] as const;
|
||||
type Country = (typeof ORDER)[number];
|
||||
const META = countryMeta as Record<
|
||||
Country,
|
||||
{stop: number; anchor: [number, number]; border: [number, number][][]}
|
||||
>;
|
||||
const countryPolygons = {
|
||||
type: 'FeatureCollection' as const,
|
||||
features: ORDER.map((country) => ({
|
||||
type: 'Feature' as const,
|
||||
properties: {country},
|
||||
geometry: {
|
||||
type: 'MultiPolygon' as const,
|
||||
coordinates: META[country].border.map((ring) => [ring]),
|
||||
},
|
||||
})),
|
||||
};
|
||||
|
||||
// Pre-build each country's border as ordered segments with cumulative lengths (for the multi-segment draw).
|
||||
const DRAW = Object.fromEntries(
|
||||
ORDER.map((c) => {
|
||||
const segLines = META[c].border.map((s) => turf.lineString(s));
|
||||
const segLen = segLines.map((l) => turf.length(l));
|
||||
const cum: number[] = [];
|
||||
let acc = 0;
|
||||
for (const L of segLen) {
|
||||
cum.push(acc);
|
||||
acc += L;
|
||||
}
|
||||
return [c, {segLines, segLen, cum, total: acc}];
|
||||
}),
|
||||
) as Record<
|
||||
Country,
|
||||
{segLines: any[]; segLen: number[]; cum: number[]; total: number}
|
||||
>;
|
||||
|
||||
// Reveal the portion of the border between fromKm and toKm as a MultiLineString (no joins across gaps).
|
||||
const sliceBorder = (
|
||||
d: (typeof DRAW)[Country],
|
||||
fromKm: number,
|
||||
toKm: number,
|
||||
) => {
|
||||
const out: number[][][] = [];
|
||||
for (let i = 0; i < d.segLines.length; i++) {
|
||||
const start = d.cum[i],
|
||||
end = start + d.segLen[i];
|
||||
const a = Math.max(fromKm, start),
|
||||
b = Math.min(toKm, end);
|
||||
if (b - a <= 0.0008) continue;
|
||||
out.push(
|
||||
turf.lineSliceAlong(d.segLines[i], a - start, b - start).geometry
|
||||
.coordinates,
|
||||
);
|
||||
}
|
||||
return {
|
||||
type: 'Feature' as const,
|
||||
properties: {},
|
||||
geometry: {type: 'MultiLineString' as const, coordinates: out},
|
||||
};
|
||||
};
|
||||
const EMPTY = {
|
||||
type: 'Feature' as const,
|
||||
properties: {},
|
||||
geometry: {type: 'MultiLineString' as const, coordinates: [] as number[][][]},
|
||||
};
|
||||
|
||||
// --- Timing (seconds). River draws over [RIVER_START, RIVER_END]; each country triggers when the river
|
||||
// reaches it (stop · span), then runs border → fill → label. Beat length is derived from these. ---
|
||||
const RIVER_START = 0.3;
|
||||
const RIVER_END = 8.0;
|
||||
const BORDER_S = 2.5;
|
||||
const FILL_S = 1.0;
|
||||
const LABEL_S = 0.7;
|
||||
const trigger = (c: Country) =>
|
||||
RIVER_START + META[c].stop * (RIVER_END - RIVER_START);
|
||||
|
||||
export const RiverReveal: React.FC = () => {
|
||||
const ref = useRef<HTMLDivElement>(null);
|
||||
const started = useRef(false);
|
||||
const frame = useCurrentFrame();
|
||||
const {fps, durationInFrames, width, height} = useVideoConfig();
|
||||
const [map, setMap] = useState<any>(null);
|
||||
const [labels, setLabels] = useState<
|
||||
Record<string, {x: number; y: number; reveal: number}>
|
||||
>({});
|
||||
const [plate, setPlate] = useState({x: 0, y: 0, scale: 1});
|
||||
const [handle] = useState(() => delayRender('maptiler init A'));
|
||||
|
||||
useEffect(() => {
|
||||
if (!ref.current || started.current) return;
|
||||
started.current = true;
|
||||
const m = new maptilersdk.Map({
|
||||
container: ref.current,
|
||||
style: maptilersdk.MapStyle.BASIC,
|
||||
center: END.center,
|
||||
zoom: Math.max(START.zoom, END.zoom),
|
||||
pitch: END.pitch,
|
||||
bearing: END.bearing,
|
||||
interactive: false,
|
||||
attributionControl: true,
|
||||
navigationControl: false,
|
||||
geolocateControl: false,
|
||||
maptilerLogo: true,
|
||||
fadeDuration: 0,
|
||||
canvasContextAttributes: {preserveDrawingBuffer: true},
|
||||
} as any);
|
||||
|
||||
m.on('load', () => {
|
||||
// Strip basemap labels (symbols) AND the inner admin-1 borders ('Other border[ dash]',
|
||||
// admin_level 3–10) to cut basemap clutter. Keep country + disputed borders.
|
||||
for (const l of m.getStyle().layers as any[])
|
||||
if (l.type === 'symbol' || /other border/i.test(l.id))
|
||||
m.removeLayer(l.id);
|
||||
|
||||
m.addSource('countries', {type: 'geojson', data: countryPolygons});
|
||||
for (const c of ORDER) {
|
||||
m.addLayer({
|
||||
id: `fill-${c}`,
|
||||
type: 'fill',
|
||||
source: 'countries',
|
||||
filter: ['==', ['get', 'country'], c],
|
||||
paint: {'fill-color': COUNTRY[c], 'fill-opacity': 0},
|
||||
});
|
||||
}
|
||||
// Per country: just the border that draws on, settled to a darker shade of the country colour
|
||||
// (the electricity now lives on the river, not the borders).
|
||||
for (const c of ORDER) {
|
||||
m.addSource(`trail-${c}`, {type: 'geojson', data: EMPTY});
|
||||
m.addLayer({
|
||||
id: `trail-${c}`,
|
||||
type: 'line',
|
||||
source: `trail-${c}`,
|
||||
layout: {'line-cap': 'round', 'line-join': 'round'},
|
||||
paint: {
|
||||
'line-color': COUNTRY_DARK[c],
|
||||
'line-width': 2,
|
||||
'line-opacity': 0.95,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const seed = turf.lineSliceAlong(
|
||||
line,
|
||||
0,
|
||||
Math.max(0.001, lineKm * 0.001),
|
||||
);
|
||||
m.addSource('river', {type: 'geojson', data: seed});
|
||||
m.addSource('river-head', {type: 'geojson', data: seed});
|
||||
// Electric water: soft blue glow → icy core → white-hot leading head with its own glow.
|
||||
m.addLayer({
|
||||
id: 'river-glow',
|
||||
type: 'line',
|
||||
source: 'river',
|
||||
layout: {'line-cap': 'round', 'line-join': 'round'},
|
||||
paint: {
|
||||
'line-color': '#49C6FF',
|
||||
'line-width': 11,
|
||||
'line-opacity': 0.32,
|
||||
'line-blur': 6,
|
||||
},
|
||||
});
|
||||
m.addLayer({
|
||||
id: 'river-line',
|
||||
type: 'line',
|
||||
source: 'river',
|
||||
layout: {'line-cap': 'round', 'line-join': 'round'},
|
||||
paint: {'line-color': COLORS.river, 'line-width': 3},
|
||||
});
|
||||
m.addLayer({
|
||||
id: 'river-headglow',
|
||||
type: 'line',
|
||||
source: 'river-head',
|
||||
layout: {'line-cap': 'round', 'line-join': 'round'},
|
||||
paint: {
|
||||
'line-color': COLORS.riverHeadGlow,
|
||||
'line-width': 16,
|
||||
'line-opacity': 0,
|
||||
'line-blur': 9,
|
||||
},
|
||||
});
|
||||
m.addLayer({
|
||||
id: 'river-head',
|
||||
type: 'line',
|
||||
source: 'river-head',
|
||||
layout: {'line-cap': 'round', 'line-join': 'round'},
|
||||
paint: {
|
||||
'line-color': COLORS.riverHead,
|
||||
'line-width': 4.5,
|
||||
'line-opacity': 0,
|
||||
},
|
||||
});
|
||||
|
||||
m.once('idle', () => {
|
||||
setMap(m);
|
||||
continueRender(handle);
|
||||
});
|
||||
});
|
||||
}, [handle]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!map) return;
|
||||
const h = delayRender(`frame A ${frame}`);
|
||||
const t = frame / fps; // seconds
|
||||
const tt = interpolate(frame, [0, durationInFrames - 1], [0, 1], {
|
||||
extrapolateLeft: 'clamp',
|
||||
extrapolateRight: 'clamp',
|
||||
});
|
||||
|
||||
// River draw
|
||||
const reveal = interpolate(t, [RIVER_START, RIVER_END], [0, 1], {
|
||||
extrapolateLeft: 'clamp',
|
||||
extrapolateRight: 'clamp',
|
||||
easing: Easing.bezier(0.645, 0.045, 0.355, 1),
|
||||
});
|
||||
const riverDrawnKm = lineKm * reveal;
|
||||
(map.getSource('river') as any)?.setData(
|
||||
turf.lineSliceAlong(line, 0, Math.max(0.001, riverDrawnKm)),
|
||||
);
|
||||
// Electric draw-head leading the river: white-hot core + glow, fading out once the river completes.
|
||||
const riverHeadKm = lineKm * 0.03;
|
||||
(map.getSource('river-head') as any)?.setData(
|
||||
turf.lineSliceAlong(
|
||||
line,
|
||||
Math.max(0, riverDrawnKm - riverHeadKm),
|
||||
Math.max(0.001, riverDrawnKm),
|
||||
),
|
||||
);
|
||||
let riverHeadFade = 0;
|
||||
if (reveal > 0.002 && reveal < 0.999) riverHeadFade = 1;
|
||||
else if (reveal >= 0.999)
|
||||
riverHeadFade = 1 - clamp01((t - RIVER_END) / 0.5);
|
||||
map.setPaintProperty(
|
||||
'river-headglow',
|
||||
'line-opacity',
|
||||
0.85 * riverHeadFade,
|
||||
);
|
||||
map.setPaintProperty('river-head', 'line-opacity', riverHeadFade);
|
||||
|
||||
const camera = {
|
||||
center: [
|
||||
lerp(START.center[0], END.center[0], tt),
|
||||
lerp(START.center[1], END.center[1], tt),
|
||||
] as [number, number],
|
||||
zoom: lerp(START.zoom, END.zoom, tt),
|
||||
};
|
||||
const cameraPoint = map.project(camera.center);
|
||||
const plateScale = 2 ** (camera.zoom - Math.max(START.zoom, END.zoom));
|
||||
const plateX = width / 2 - cameraPoint.x * plateScale;
|
||||
const plateY = height / 2 - cameraPoint.y * plateScale;
|
||||
|
||||
const pos: Record<string, {x: number; y: number; reveal: number}> = {};
|
||||
for (const c of ORDER) {
|
||||
const d = DRAW[c];
|
||||
const lt = t - trigger(c); // local seconds since this country triggered
|
||||
|
||||
// 1) border draws on (constant duration), settling to a darker shade — no electric head
|
||||
const bp = interpolate(clamp01(lt / BORDER_S), [0, 1], [0, 1], {
|
||||
easing: Easing.bezier(0.645, 0.045, 0.355, 1),
|
||||
});
|
||||
(map.getSource(`trail-${c}`) as any)?.setData(
|
||||
bp <= 0 ? EMPTY : sliceBorder(d, 0, d.total * bp),
|
||||
);
|
||||
|
||||
// 2) fill blooms in (overshoot then settle) after the border completes
|
||||
const fp = clamp01((lt - BORDER_S) / FILL_S);
|
||||
const fo = interpolate(
|
||||
fp,
|
||||
[0, 0.6, 1],
|
||||
[0, FILL_OPACITY * 1.25, FILL_OPACITY],
|
||||
{
|
||||
extrapolateLeft: 'clamp',
|
||||
extrapolateRight: 'clamp',
|
||||
easing: Easing.bezier(0.3333333333333333, 1, 0.6666666666666666, 1),
|
||||
},
|
||||
);
|
||||
map.setPaintProperty(`fill-${c}`, 'fill-opacity', fp <= 0 ? 0 : fo);
|
||||
|
||||
// 3) label rises in after the fill
|
||||
const lp = clamp01((lt - BORDER_S - FILL_S) / LABEL_S);
|
||||
const p = map.project(META[c].anchor);
|
||||
pos[c] = {
|
||||
x: p.x * plateScale + plateX,
|
||||
y: p.y * plateScale + plateY,
|
||||
reveal: lp,
|
||||
};
|
||||
}
|
||||
setLabels(pos);
|
||||
|
||||
setPlate({x: plateX, y: plateY, scale: plateScale});
|
||||
map.once('idle', () => continueRender(h));
|
||||
map.triggerRepaint();
|
||||
}, [map, frame, fps, durationInFrames, width, height]);
|
||||
|
||||
return (
|
||||
<AbsoluteFill style={{backgroundColor: COLORS.bg}}>
|
||||
<div
|
||||
ref={ref}
|
||||
style={{
|
||||
width: width * 2,
|
||||
height: height * 2,
|
||||
position: 'absolute',
|
||||
transform: `translate(${plate.x}px, ${plate.y}px) scale(${plate.scale})`,
|
||||
transformOrigin: '0 0',
|
||||
}}
|
||||
/>
|
||||
<AbsoluteFill style={{pointerEvents: 'none'}}>
|
||||
{ORDER.map((c) =>
|
||||
labels[c] ? (
|
||||
<CountryLabel
|
||||
key={c}
|
||||
name={c.toUpperCase()}
|
||||
color={COUNTRY[c]}
|
||||
reveal={labels[c].reveal}
|
||||
x={labels[c].x}
|
||||
y={labels[c].y}
|
||||
/>
|
||||
) : null,
|
||||
)}
|
||||
</AbsoluteFill>
|
||||
</AbsoluteFill>
|
||||
);
|
||||
};
|
||||
+22
@@ -0,0 +1,22 @@
|
||||
// Minimal Remotion scaffold for the map explainer. In a Remotion project (`bunx create-video@latest`,
|
||||
// blank template), this is `src/Root.tsx`; `src/index.ts` is just `registerRoot(RemotionRoot)`.
|
||||
//
|
||||
// The Composition sets DURATION (the beat) and dimensions. The component reads durationInFrames / width /
|
||||
// height from useVideoConfig(). The beat must be long enough for the last country's full sequence after
|
||||
// the river reaches it. See references/map-explainer-architecture.md §2.
|
||||
// Render with: bunx remotion render src/index.ts MapExplainer out.mp4 --gl=angle --concurrency=1 --timeout=120000
|
||||
|
||||
import React from 'react';
|
||||
import {Composition} from 'remotion';
|
||||
import {RiverReveal} from './RiverReveal'; // → src/components/RiverReveal.tsx in your project
|
||||
|
||||
export const RemotionRoot: React.FC = () => (
|
||||
<Composition
|
||||
id="MapExplainer"
|
||||
component={RiverReveal}
|
||||
durationInFrames={12 * 30} // 12 s @ 30 fps — raise if a later country needs more room after the river arrives
|
||||
fps={30}
|
||||
width={1920}
|
||||
height={1080}
|
||||
/>
|
||||
);
|
||||
+7535
File diff suppressed because it is too large
Load Diff
+605
@@ -0,0 +1,605 @@
|
||||
[
|
||||
[84.144, 29.565],
|
||||
[84.171, 29.577],
|
||||
[84.22500000000001, 29.559],
|
||||
[84.246, 29.541],
|
||||
[84.297, 29.544],
|
||||
[84.321, 29.532],
|
||||
[84.411, 29.544],
|
||||
[84.426, 29.529],
|
||||
[84.45, 29.535],
|
||||
[84.468, 29.523],
|
||||
[84.492, 29.523],
|
||||
[84.531, 29.490000000000002],
|
||||
[84.507, 29.466],
|
||||
[84.501, 29.445],
|
||||
[84.513, 29.385],
|
||||
[84.495, 29.379],
|
||||
[84.483, 29.361],
|
||||
[84.486, 29.34],
|
||||
[84.519, 29.316],
|
||||
[84.51, 29.292],
|
||||
[84.522, 29.286],
|
||||
[84.525, 29.265],
|
||||
[84.57600000000001, 29.253],
|
||||
[84.58800000000001, 29.259],
|
||||
[84.60300000000001, 29.247],
|
||||
[84.615, 29.265],
|
||||
[84.63, 29.268],
|
||||
[84.624, 29.283],
|
||||
[84.651, 29.286],
|
||||
[84.669, 29.259],
|
||||
[84.732, 29.25],
|
||||
[84.741, 29.235],
|
||||
[84.771, 29.25],
|
||||
[84.777, 29.241],
|
||||
[84.843, 29.223],
|
||||
[84.864, 29.232],
|
||||
[84.879, 29.202],
|
||||
[84.9, 29.199],
|
||||
[84.909, 29.181],
|
||||
[84.933, 29.184],
|
||||
[84.96900000000001, 29.163],
|
||||
[85.023, 29.187],
|
||||
[85.071, 29.259],
|
||||
[85.122, 29.274],
|
||||
[85.167, 29.322],
|
||||
[85.185, 29.310000000000002],
|
||||
[85.26, 29.319],
|
||||
[85.284, 29.301000000000002],
|
||||
[85.34400000000001, 29.289],
|
||||
[85.35600000000001, 29.265],
|
||||
[85.413, 29.262],
|
||||
[85.431, 29.271],
|
||||
[85.449, 29.247],
|
||||
[85.464, 29.244],
|
||||
[85.521, 29.25],
|
||||
[85.551, 29.265],
|
||||
[85.611, 29.235],
|
||||
[85.617, 29.193],
|
||||
[85.662, 29.172],
|
||||
[85.686, 29.172],
|
||||
[85.69500000000001, 29.184],
|
||||
[85.71600000000001, 29.172],
|
||||
[85.72800000000001, 29.187],
|
||||
[85.764, 29.181],
|
||||
[85.761, 29.193],
|
||||
[85.776, 29.202],
|
||||
[85.788, 29.196],
|
||||
[85.797, 29.22],
|
||||
[85.809, 29.211000000000002],
|
||||
[85.848, 29.211000000000002],
|
||||
[85.863, 29.193],
|
||||
[85.881, 29.193],
|
||||
[85.917, 29.172],
|
||||
[85.968, 29.175],
|
||||
[86.004, 29.163],
|
||||
[86.09400000000001, 29.175],
|
||||
[86.10000000000001, 29.166],
|
||||
[86.133, 29.172],
|
||||
[86.148, 29.166],
|
||||
[86.193, 29.178],
|
||||
[86.223, 29.202],
|
||||
[86.268, 29.193],
|
||||
[86.295, 29.208000000000002],
|
||||
[86.34, 29.205000000000002],
|
||||
[86.412, 29.217000000000002],
|
||||
[86.427, 29.211000000000002],
|
||||
[86.43, 29.199],
|
||||
[86.47800000000001, 29.196],
|
||||
[86.496, 29.22],
|
||||
[86.514, 29.211000000000002],
|
||||
[86.52, 29.235],
|
||||
[86.529, 29.238],
|
||||
[86.535, 29.217000000000002],
|
||||
[86.559, 29.196],
|
||||
[86.607, 29.202],
|
||||
[86.616, 29.184],
|
||||
[86.658, 29.193],
|
||||
[86.67, 29.205000000000002],
|
||||
[86.682, 29.199],
|
||||
[86.7, 29.208000000000002],
|
||||
[86.736, 29.205000000000002],
|
||||
[86.778, 29.187],
|
||||
[86.805, 29.196],
|
||||
[86.82300000000001, 29.184],
|
||||
[86.84100000000001, 29.19],
|
||||
[86.901, 29.181],
|
||||
[86.949, 29.163],
|
||||
[87.027, 29.172],
|
||||
[87.039, 29.148],
|
||||
[87.063, 29.136],
|
||||
[87.084, 29.142],
|
||||
[87.12, 29.175],
|
||||
[87.162, 29.142],
|
||||
[87.21000000000001, 29.142],
|
||||
[87.237, 29.157],
|
||||
[87.249, 29.136],
|
||||
[87.297, 29.127],
|
||||
[87.303, 29.115000000000002],
|
||||
[87.366, 29.121000000000002],
|
||||
[87.459, 29.097],
|
||||
[87.486, 29.115000000000002],
|
||||
[87.546, 29.109],
|
||||
[87.56700000000001, 29.121000000000002],
|
||||
[87.59400000000001, 29.121000000000002],
|
||||
[87.60600000000001, 29.133],
|
||||
[87.654, 29.139],
|
||||
[87.666, 29.13],
|
||||
[87.684, 29.133],
|
||||
[87.687, 29.157],
|
||||
[87.669, 29.181],
|
||||
[87.675, 29.205000000000002],
|
||||
[87.705, 29.217000000000002],
|
||||
[87.714, 29.235],
|
||||
[87.732, 29.238],
|
||||
[87.732, 29.253],
|
||||
[87.756, 29.283],
|
||||
[87.753, 29.301000000000002],
|
||||
[87.789, 29.304000000000002],
|
||||
[87.795, 29.328],
|
||||
[87.81, 29.337],
|
||||
[87.849, 29.331],
|
||||
[87.87, 29.349],
|
||||
[87.906, 29.343],
|
||||
[87.924, 29.349],
|
||||
[87.97800000000001, 29.388],
|
||||
[87.996, 29.376],
|
||||
[88.014, 29.379],
|
||||
[88.017, 29.37],
|
||||
[88.125, 29.367],
|
||||
[88.137, 29.337],
|
||||
[88.161, 29.331],
|
||||
[88.176, 29.337],
|
||||
[88.188, 29.325],
|
||||
[88.2, 29.328],
|
||||
[88.221, 29.367],
|
||||
[88.245, 29.367],
|
||||
[88.287, 29.349],
|
||||
[88.299, 29.358],
|
||||
[88.365, 29.316],
|
||||
[88.41, 29.322],
|
||||
[88.422, 29.316],
|
||||
[88.443, 29.325],
|
||||
[88.458, 29.349],
|
||||
[88.503, 29.361],
|
||||
[88.536, 29.334],
|
||||
[88.563, 29.328],
|
||||
[88.596, 29.349],
|
||||
[88.629, 29.346],
|
||||
[88.641, 29.334],
|
||||
[88.671, 29.34],
|
||||
[88.71000000000001, 29.331],
|
||||
[88.72800000000001, 29.343],
|
||||
[88.791, 29.337],
|
||||
[88.818, 29.352],
|
||||
[88.86, 29.319],
|
||||
[88.884, 29.334],
|
||||
[88.908, 29.316],
|
||||
[88.923, 29.331],
|
||||
[88.962, 29.328],
|
||||
[88.971, 29.349],
|
||||
[89.007, 29.361],
|
||||
[89.019, 29.352],
|
||||
[89.034, 29.358],
|
||||
[89.08500000000001, 29.322],
|
||||
[89.136, 29.316],
|
||||
[89.16, 29.343],
|
||||
[89.181, 29.337],
|
||||
[89.211, 29.349],
|
||||
[89.235, 29.379],
|
||||
[89.253, 29.373],
|
||||
[89.265, 29.385],
|
||||
[89.289, 29.385],
|
||||
[89.307, 29.376],
|
||||
[89.349, 29.379],
|
||||
[89.397, 29.358],
|
||||
[89.45100000000001, 29.355],
|
||||
[89.46000000000001, 29.334],
|
||||
[89.58, 29.358],
|
||||
[89.595, 29.346],
|
||||
[89.61, 29.355],
|
||||
[89.631, 29.346],
|
||||
[89.661, 29.349],
|
||||
[89.679, 29.364],
|
||||
[89.757, 29.295],
|
||||
[89.787, 29.292],
|
||||
[89.808, 29.310000000000002],
|
||||
[89.85300000000001, 29.322],
|
||||
[89.931, 29.319],
|
||||
[89.985, 29.343],
|
||||
[90.015, 29.337],
|
||||
[90.072, 29.352],
|
||||
[90.156, 29.355],
|
||||
[90.168, 29.346],
|
||||
[90.20100000000001, 29.349],
|
||||
[90.22500000000001, 29.331],
|
||||
[90.273, 29.337],
|
||||
[90.276, 29.328],
|
||||
[90.342, 29.313],
|
||||
[90.378, 29.295],
|
||||
[90.435, 29.241],
|
||||
[90.477, 29.256],
|
||||
[90.492, 29.25],
|
||||
[90.51, 29.256],
|
||||
[90.522, 29.247],
|
||||
[90.54, 29.262],
|
||||
[90.621, 29.277],
|
||||
[90.642, 29.301000000000002],
|
||||
[90.663, 29.298000000000002],
|
||||
[90.681, 29.313],
|
||||
[90.684, 29.328],
|
||||
[90.705, 29.34],
|
||||
[90.729, 29.328],
|
||||
[90.747, 29.337],
|
||||
[90.765, 29.328],
|
||||
[90.768, 29.310000000000002],
|
||||
[90.756, 29.295],
|
||||
[90.771, 29.277],
|
||||
[90.855, 29.283],
|
||||
[90.897, 29.319],
|
||||
[90.933, 29.310000000000002],
|
||||
[90.95100000000001, 29.295],
|
||||
[90.993, 29.322],
|
||||
[91.035, 29.295],
|
||||
[91.065, 29.316],
|
||||
[91.116, 29.325],
|
||||
[91.131, 29.313],
|
||||
[91.173, 29.325],
|
||||
[91.194, 29.286],
|
||||
[91.233, 29.274],
|
||||
[91.296, 29.289],
|
||||
[91.308, 29.277],
|
||||
[91.34700000000001, 29.28],
|
||||
[91.374, 29.292],
|
||||
[91.395, 29.28],
|
||||
[91.449, 29.286],
|
||||
[91.479, 29.268],
|
||||
[91.512, 29.28],
|
||||
[91.533, 29.271],
|
||||
[91.554, 29.289],
|
||||
[91.587, 29.292],
|
||||
[91.602, 29.271],
|
||||
[91.62, 29.265],
|
||||
[91.659, 29.271],
|
||||
[91.674, 29.259],
|
||||
[91.71000000000001, 29.268],
|
||||
[91.74, 29.259],
|
||||
[91.782, 29.274],
|
||||
[91.839, 29.268],
|
||||
[91.869, 29.283],
|
||||
[91.98, 29.262],
|
||||
[92.007, 29.232],
|
||||
[92.07000000000001, 29.289],
|
||||
[92.115, 29.283],
|
||||
[92.154, 29.289],
|
||||
[92.196, 29.244],
|
||||
[92.22, 29.253],
|
||||
[92.235, 29.244],
|
||||
[92.277, 29.244],
|
||||
[92.295, 29.229],
|
||||
[92.319, 29.226],
|
||||
[92.376, 29.226],
|
||||
[92.397, 29.241],
|
||||
[92.406, 29.262],
|
||||
[92.433, 29.25],
|
||||
[92.46600000000001, 29.253],
|
||||
[92.529, 29.172],
|
||||
[92.529, 29.133],
|
||||
[92.553, 29.148],
|
||||
[92.598, 29.145],
|
||||
[92.589, 29.121000000000002],
|
||||
[92.61, 29.097],
|
||||
[92.619, 29.115000000000002],
|
||||
[92.658, 29.121000000000002],
|
||||
[92.685, 29.139],
|
||||
[92.697, 29.127],
|
||||
[92.676, 29.112000000000002],
|
||||
[92.679, 29.103],
|
||||
[92.709, 29.109],
|
||||
[92.739, 29.067],
|
||||
[92.772, 29.091],
|
||||
[92.775, 29.073],
|
||||
[92.796, 29.082],
|
||||
[92.808, 29.061],
|
||||
[92.82000000000001, 29.067],
|
||||
[92.82300000000001, 29.082],
|
||||
[92.82900000000001, 29.076],
|
||||
[92.85300000000001, 29.082],
|
||||
[92.85600000000001, 29.073],
|
||||
[92.88, 29.073],
|
||||
[92.904, 29.058],
|
||||
[92.934, 29.067],
|
||||
[92.952, 29.049],
|
||||
[92.973, 29.061],
|
||||
[92.988, 29.043],
|
||||
[92.997, 29.049],
|
||||
[93.066, 29.043],
|
||||
[93.081, 29.094],
|
||||
[93.117, 29.115000000000002],
|
||||
[93.123, 29.136],
|
||||
[93.138, 29.139],
|
||||
[93.15, 29.127],
|
||||
[93.147, 29.094],
|
||||
[93.165, 29.064],
|
||||
[93.165, 29.043],
|
||||
[93.153, 29.037],
|
||||
[93.153, 29.025000000000002],
|
||||
[93.162, 29.016000000000002],
|
||||
[93.21300000000001, 29.025000000000002],
|
||||
[93.22800000000001, 29.019000000000002],
|
||||
[93.23100000000001, 29.001],
|
||||
[93.261, 28.992],
|
||||
[93.285, 29.001],
|
||||
[93.312, 28.998],
|
||||
[93.315, 29.016000000000002],
|
||||
[93.348, 29.046],
|
||||
[93.375, 29.052],
|
||||
[93.393, 29.094],
|
||||
[93.429, 29.109],
|
||||
[93.447, 29.106],
|
||||
[93.438, 29.13],
|
||||
[93.48, 29.175],
|
||||
[93.492, 29.163],
|
||||
[93.54, 29.178],
|
||||
[93.57000000000001, 29.166],
|
||||
[93.627, 29.172],
|
||||
[93.63, 29.151],
|
||||
[93.645, 29.145],
|
||||
[93.675, 29.151],
|
||||
[93.681, 29.163],
|
||||
[93.699, 29.16],
|
||||
[93.702, 29.142],
|
||||
[93.735, 29.127],
|
||||
[93.75, 29.136],
|
||||
[93.75, 29.148],
|
||||
[93.78, 29.154],
|
||||
[93.789, 29.124000000000002],
|
||||
[93.825, 29.118000000000002],
|
||||
[93.834, 29.124000000000002],
|
||||
[93.831, 29.142],
|
||||
[93.894, 29.13],
|
||||
[93.912, 29.145],
|
||||
[93.903, 29.166],
|
||||
[93.909, 29.178],
|
||||
[93.94500000000001, 29.184],
|
||||
[93.95400000000001, 29.196],
|
||||
[94.017, 29.193],
|
||||
[94.035, 29.205000000000002],
|
||||
[94.176, 29.196],
|
||||
[94.251, 29.262],
|
||||
[94.305, 29.271],
|
||||
[94.302, 29.292],
|
||||
[94.33200000000001, 29.316],
|
||||
[94.35000000000001, 29.316],
|
||||
[94.389, 29.337],
|
||||
[94.401, 29.361],
|
||||
[94.419, 29.37],
|
||||
[94.434, 29.406000000000002],
|
||||
[94.542, 29.448],
|
||||
[94.581, 29.481],
|
||||
[94.656, 29.490000000000002],
|
||||
[94.70700000000001, 29.463],
|
||||
[94.818, 29.493000000000002],
|
||||
[94.875, 29.541],
|
||||
[94.923, 29.61],
|
||||
[94.926, 29.622],
|
||||
[94.887, 29.634],
|
||||
[94.917, 29.67],
|
||||
[94.893, 29.697],
|
||||
[94.905, 29.715],
|
||||
[94.956, 29.757],
|
||||
[95.124, 29.763],
|
||||
[95.124, 29.781000000000002],
|
||||
[95.09100000000001, 29.814],
|
||||
[95.11200000000001, 29.868000000000002],
|
||||
[95.13, 29.88],
|
||||
[95.175, 29.895],
|
||||
[95.196, 29.892],
|
||||
[95.223, 29.868000000000002],
|
||||
[95.286, 29.868000000000002],
|
||||
[95.304, 29.856],
|
||||
[95.307, 29.838],
|
||||
[95.283, 29.82],
|
||||
[95.289, 29.811],
|
||||
[95.385, 29.772000000000002],
|
||||
[95.379, 29.718],
|
||||
[95.397, 29.688000000000002],
|
||||
[95.388, 29.595],
|
||||
[95.403, 29.562],
|
||||
[95.427, 29.538],
|
||||
[95.43900000000001, 29.478],
|
||||
[95.43, 29.451],
|
||||
[95.313, 29.331],
|
||||
[95.256, 29.289],
|
||||
[95.202, 29.28],
|
||||
[95.04, 29.175],
|
||||
[95.001, 29.169],
|
||||
[95.004, 29.139],
|
||||
[94.908, 29.052],
|
||||
[94.902, 29.016000000000002],
|
||||
[94.869, 28.998],
|
||||
[94.839, 28.956],
|
||||
[94.788, 28.935000000000002],
|
||||
[94.773, 28.854],
|
||||
[94.794, 28.824],
|
||||
[94.914, 28.821],
|
||||
[94.923, 28.812],
|
||||
[94.911, 28.794],
|
||||
[94.92, 28.752],
|
||||
[94.977, 28.713],
|
||||
[94.983, 28.674],
|
||||
[94.998, 28.683],
|
||||
[95.031, 28.617],
|
||||
[95.09700000000001, 28.539],
|
||||
[95.10300000000001, 28.506],
|
||||
[95.09100000000001, 28.455000000000002],
|
||||
[95.09700000000001, 28.419],
|
||||
[95.013, 28.326],
|
||||
[94.992, 28.287],
|
||||
[94.992, 28.242],
|
||||
[95.019, 28.215],
|
||||
[95.031, 28.173000000000002],
|
||||
[95.06700000000001, 28.155],
|
||||
[95.145, 28.149],
|
||||
[95.211, 28.179000000000002],
|
||||
[95.277, 28.155],
|
||||
[95.292, 28.116],
|
||||
[95.316, 28.089000000000002],
|
||||
[95.355, 28.071],
|
||||
[95.379, 28.071],
|
||||
[95.385, 28.059],
|
||||
[95.382, 27.951],
|
||||
[95.4, 27.936],
|
||||
[95.412, 27.882],
|
||||
[95.379, 27.843],
|
||||
[95.349, 27.837],
|
||||
[95.325, 27.810000000000002],
|
||||
[95.325, 27.78],
|
||||
[95.298, 27.762],
|
||||
[95.289, 27.717000000000002],
|
||||
[95.253, 27.657],
|
||||
[95.211, 27.666],
|
||||
[95.151, 27.624000000000002],
|
||||
[95.139, 27.63],
|
||||
[95.10300000000001, 27.609],
|
||||
[94.992, 27.6],
|
||||
[94.944, 27.57],
|
||||
[94.917, 27.573],
|
||||
[94.869, 27.504],
|
||||
[94.803, 27.495],
|
||||
[94.803, 27.48],
|
||||
[94.785, 27.471],
|
||||
[94.794, 27.456],
|
||||
[94.767, 27.435000000000002],
|
||||
[94.767, 27.414],
|
||||
[94.73100000000001, 27.402],
|
||||
[94.71900000000001, 27.372],
|
||||
[94.69800000000001, 27.36],
|
||||
[94.69800000000001, 27.333000000000002],
|
||||
[94.683, 27.324],
|
||||
[94.677, 27.294],
|
||||
[94.635, 27.291],
|
||||
[94.629, 27.273],
|
||||
[94.602, 27.255],
|
||||
[94.596, 27.231],
|
||||
[94.584, 27.228],
|
||||
[94.587, 27.177],
|
||||
[94.596, 27.162],
|
||||
[94.584, 27.147000000000002],
|
||||
[94.566, 27.150000000000002],
|
||||
[94.554, 27.102],
|
||||
[94.518, 27.105],
|
||||
[94.449, 27.060000000000002],
|
||||
[94.44, 27.018],
|
||||
[94.407, 26.985],
|
||||
[94.35900000000001, 26.97],
|
||||
[94.287, 26.919],
|
||||
[94.197, 26.925],
|
||||
[94.164, 26.898],
|
||||
[94.146, 26.865000000000002],
|
||||
[94.125, 26.856],
|
||||
[94.113, 26.868000000000002],
|
||||
[94.134, 26.907],
|
||||
[94.113, 26.913],
|
||||
[94.077, 26.886],
|
||||
[93.927, 26.832],
|
||||
[93.864, 26.766000000000002],
|
||||
[93.753, 26.733],
|
||||
[93.657, 26.715],
|
||||
[93.618, 26.724],
|
||||
[93.56700000000001, 26.751],
|
||||
[93.441, 26.769000000000002],
|
||||
[93.411, 26.76],
|
||||
[93.369, 26.718],
|
||||
[93.288, 26.742],
|
||||
[93.249, 26.724],
|
||||
[93.183, 26.673000000000002],
|
||||
[93.12, 26.646],
|
||||
[93.087, 26.655],
|
||||
[93.018, 26.646],
|
||||
[92.898, 26.655],
|
||||
[92.883, 26.652],
|
||||
[92.874, 26.625],
|
||||
[92.83500000000001, 26.607],
|
||||
[92.733, 26.613],
|
||||
[92.676, 26.601],
|
||||
[92.658, 26.592000000000002],
|
||||
[92.616, 26.523],
|
||||
[92.595, 26.517],
|
||||
[92.508, 26.517],
|
||||
[92.43, 26.535],
|
||||
[92.391, 26.532],
|
||||
[92.244, 26.472],
|
||||
[92.229, 26.442],
|
||||
[92.202, 26.454],
|
||||
[92.172, 26.442],
|
||||
[92.124, 26.403000000000002],
|
||||
[92.06700000000001, 26.373],
|
||||
[92.07000000000001, 26.325],
|
||||
[92.058, 26.304000000000002],
|
||||
[91.971, 26.271],
|
||||
[91.932, 26.271],
|
||||
[91.899, 26.241],
|
||||
[91.878, 26.235],
|
||||
[91.854, 26.241],
|
||||
[91.665, 26.166],
|
||||
[91.608, 26.172],
|
||||
[91.542, 26.145],
|
||||
[91.497, 26.139],
|
||||
[91.377, 26.172],
|
||||
[91.287, 26.166],
|
||||
[91.197, 26.202],
|
||||
[91.134, 26.214000000000002],
|
||||
[91.062, 26.187],
|
||||
[90.96300000000001, 26.175],
|
||||
[90.888, 26.136],
|
||||
[90.849, 26.13],
|
||||
[90.807, 26.154],
|
||||
[90.741, 26.163],
|
||||
[90.681, 26.19],
|
||||
[90.58200000000001, 26.205000000000002],
|
||||
[90.507, 26.229],
|
||||
[90.474, 26.226],
|
||||
[90.435, 26.178],
|
||||
[90.402, 26.16],
|
||||
[90.345, 26.148],
|
||||
[90.309, 26.124],
|
||||
[90.22500000000001, 26.106],
|
||||
[90.177, 26.076],
|
||||
[89.955, 26.01],
|
||||
[89.904, 25.941],
|
||||
[89.84700000000001, 25.89],
|
||||
[89.81400000000001, 25.815],
|
||||
[89.736, 25.692],
|
||||
[89.724, 25.632],
|
||||
[89.697, 25.572],
|
||||
[89.709, 25.482],
|
||||
[89.697, 25.398],
|
||||
[89.67, 25.323],
|
||||
[89.7, 25.266000000000002],
|
||||
[89.703, 25.242],
|
||||
[89.685, 25.2],
|
||||
[89.661, 25.173000000000002],
|
||||
[89.625, 25.074],
|
||||
[89.613, 24.96],
|
||||
[89.613, 24.936],
|
||||
[89.658, 24.87],
|
||||
[89.673, 24.792],
|
||||
[89.706, 24.75],
|
||||
[89.769, 24.549],
|
||||
[89.748, 24.372],
|
||||
[89.754, 24.282],
|
||||
[89.733, 24.225],
|
||||
[89.742, 24.201],
|
||||
[89.739, 24.123],
|
||||
[89.697, 24.015],
|
||||
[89.7, 23.958000000000002],
|
||||
[89.727, 23.883],
|
||||
[89.787, 23.796],
|
||||
[89.85600000000001, 23.748],
|
||||
[89.919, 23.664],
|
||||
[89.985, 23.643],
|
||||
[90.144, 23.544],
|
||||
[90.249, 23.463]
|
||||
]
|
||||
+34
@@ -0,0 +1,34 @@
|
||||
// Example tokens only. Replace every visual value with production-local tokens.
|
||||
export const COLORS = {
|
||||
bg: '#101315',
|
||||
// Electric water — a near-white icy core with a blue glow and a white-hot draw-head (the "electricity"
|
||||
// travels along the river as it draws on). No dark casing.
|
||||
river: '#E8F7FF', // bright icy core
|
||||
riverGlow: 'rgba(73,198,255,0.5)', // electric-blue glow
|
||||
riverHead: '#FFFFFF', // white-hot leading head
|
||||
riverHeadGlow: 'rgba(120,225,255,0.95)',
|
||||
border: '#f5f2ed', // neutral cream country borders/labels over the colored fills
|
||||
cream: '#f5f0eb',
|
||||
} as const;
|
||||
|
||||
// Example progressive fill tokens. Rename these keys and replace values for each production.
|
||||
export const COUNTRY = {
|
||||
china: '#D4A853',
|
||||
india: '#5B8A8A',
|
||||
bangladesh: '#C07B57',
|
||||
} as const;
|
||||
// Darker shade of each country colour — the settled border line (the bright COUNTRY colour is the
|
||||
// glowing draw-head that leads the animation).
|
||||
export const COUNTRY_DARK = {
|
||||
china: '#9A7530',
|
||||
india: '#3C5C5C',
|
||||
bangladesh: '#855239',
|
||||
} as const;
|
||||
export const FILL_OPACITY = 0.5;
|
||||
|
||||
export const VIDEO = {width: 1920, height: 1080, fps: 30} as const;
|
||||
|
||||
// Beat durations (seconds → frames at VIDEO.fps)
|
||||
export const DUR = {
|
||||
mapExplainer: 12 * VIDEO.fps,
|
||||
} as const;
|
||||
Reference in New Issue
Block a user