day swap animation
This commit is contained in:
@@ -208,6 +208,17 @@ export interface WeekDailyData {
|
||||
windspeed_10m_max: number[];
|
||||
windgusts_10m_max: number[];
|
||||
winddirection_10m_dominant: number[];
|
||||
// Only the live forecast carries these; the archive-backed views reuse this
|
||||
// shape without them, so they stay optional.
|
||||
/** Seconds between sunrise and sunset. */
|
||||
daylight_duration?: number[];
|
||||
uv_index_max?: number[];
|
||||
precipitation_probability_max?: number[];
|
||||
/** Unix seconds; 0 on the days the moon doesn't rise / set at all. */
|
||||
moonrise?: number[];
|
||||
moonset?: number[];
|
||||
/** 0 and 1 are new moon, 0.5 is full moon. */
|
||||
moon_phase?: number[];
|
||||
}
|
||||
|
||||
export interface WeekForecastResult {
|
||||
@@ -361,7 +372,13 @@ const WEEK_DAILY_VARS = [
|
||||
'precipitation_sum',
|
||||
'wind_speed_10m_max',
|
||||
'wind_gusts_10m_max',
|
||||
'wind_direction_10m_dominant'
|
||||
'wind_direction_10m_dominant',
|
||||
'daylight_duration',
|
||||
'uv_index_max',
|
||||
'precipitation_probability_max',
|
||||
'moonrise',
|
||||
'moonset',
|
||||
'moon_phase'
|
||||
] as const;
|
||||
|
||||
/**
|
||||
@@ -463,6 +480,17 @@ export async function fetchWeekForecast(params: WeekForecastParams): Promise<Wee
|
||||
const sunriseVar = dailyBlock.variables(3)!;
|
||||
const sunsetVar = dailyBlock.variables(4)!;
|
||||
|
||||
// Optional tail variables: a model that doesn't carry them yields fewer
|
||||
// entries, so read them defensively instead of asserting.
|
||||
const dailyAt = (i: number): number[] => {
|
||||
const v = dailyBlock.variables(i);
|
||||
return v ? getValues(v) : [];
|
||||
};
|
||||
const dailyInt64At = (i: number): number[] => {
|
||||
const v = dailyBlock.variables(i);
|
||||
return v ? getInt64Values(v) : [];
|
||||
};
|
||||
|
||||
const daily: WeekDailyData = {
|
||||
weather_code: getValues(dailyBlock.variables(0)!),
|
||||
temperature_2m_max: getValues(dailyBlock.variables(1)!),
|
||||
@@ -473,7 +501,13 @@ export async function fetchWeekForecast(params: WeekForecastParams): Promise<Wee
|
||||
precipitation_sum: getValues(dailyBlock.variables(6)!),
|
||||
windspeed_10m_max: getValues(dailyBlock.variables(7)!),
|
||||
windgusts_10m_max: getValues(dailyBlock.variables(8)!),
|
||||
winddirection_10m_dominant: getValues(dailyBlock.variables(9)!)
|
||||
winddirection_10m_dominant: getValues(dailyBlock.variables(9)!),
|
||||
daylight_duration: dailyAt(10),
|
||||
uv_index_max: dailyAt(11),
|
||||
precipitation_probability_max: dailyAt(12),
|
||||
moonrise: dailyInt64At(13),
|
||||
moonset: dailyInt64At(14),
|
||||
moon_phase: dailyAt(15)
|
||||
};
|
||||
|
||||
const daylightBands = buildDaylightBands(daily.sunrise, daily.sunset);
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* Fades a block back in whenever the value passed to it changes - used to make
|
||||
* a day switch visible in the hourly table, the written forecast and the
|
||||
* meteograms.
|
||||
*
|
||||
* It animates the existing node instead of remounting it: the meteograms hold
|
||||
* canvas charts with their own zoom state, and rebuilding those on every day
|
||||
* change would be both expensive and lossy.
|
||||
*/
|
||||
export function daySwap(node: HTMLElement, key: unknown) {
|
||||
let current = key;
|
||||
|
||||
const play = () => {
|
||||
if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
|
||||
node.animate(
|
||||
[
|
||||
{ opacity: 0.15, transform: 'translateY(4px)' },
|
||||
{ opacity: 1, transform: 'translateY(0)' }
|
||||
],
|
||||
{ duration: 280, easing: 'cubic-bezier(0.22, 0.61, 0.36, 1)' }
|
||||
);
|
||||
};
|
||||
|
||||
return {
|
||||
update(next: unknown) {
|
||||
if (next === current) return;
|
||||
current = next;
|
||||
play();
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user