feat: add snow atmosphere effect
tsParticles snow — 60–300 flakes depending on intensity, size variation for depth, gentle drift via random speed + curved paths. Appears in atmosphere panel between Rain and Embers. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
97954de110
commit
a923e1b226
4 changed files with 45 additions and 2 deletions
|
|
@ -8,6 +8,7 @@ const EFFECTS: { key: EffectKey; emoji: string; label: string }[] = [
|
||||||
{ key: "fog", emoji: "🌫", label: "Fog" },
|
{ key: "fog", emoji: "🌫", label: "Fog" },
|
||||||
{ key: "fire", emoji: "🔥", label: "Fire" },
|
{ key: "fire", emoji: "🔥", label: "Fire" },
|
||||||
{ key: "rain", emoji: "🌧", label: "Rain" },
|
{ key: "rain", emoji: "🌧", label: "Rain" },
|
||||||
|
{ key: "snow", emoji: "❄️", label: "Snow" },
|
||||||
{ key: "embers", emoji: "✨", label: "Embers" },
|
{ key: "embers", emoji: "✨", label: "Embers" },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
@ -26,6 +27,7 @@ export default function AtmospherePanel({
|
||||||
fog: atmosphere.fog.intensity,
|
fog: atmosphere.fog.intensity,
|
||||||
fire: atmosphere.fire.intensity,
|
fire: atmosphere.fire.intensity,
|
||||||
rain: atmosphere.rain.intensity,
|
rain: atmosphere.rain.intensity,
|
||||||
|
snow: atmosphere.snow.intensity,
|
||||||
embers: atmosphere.embers.intensity,
|
embers: atmosphere.embers.intensity,
|
||||||
});
|
});
|
||||||
const containerRef = useRef<HTMLDivElement>(null);
|
const containerRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
@ -48,6 +50,7 @@ export default function AtmospherePanel({
|
||||||
fog: atmosphere.fog.intensity,
|
fog: atmosphere.fog.intensity,
|
||||||
fire: atmosphere.fire.intensity,
|
fire: atmosphere.fire.intensity,
|
||||||
rain: atmosphere.rain.intensity,
|
rain: atmosphere.rain.intensity,
|
||||||
|
snow: atmosphere.snow.intensity,
|
||||||
embers: atmosphere.embers.intensity,
|
embers: atmosphere.embers.intensity,
|
||||||
});
|
});
|
||||||
}, [atmosphere]);
|
}, [atmosphere]);
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ import { useEffect, useState } from "react";
|
||||||
import Particles, { initParticlesEngine } from "@tsparticles/react";
|
import Particles, { initParticlesEngine } from "@tsparticles/react";
|
||||||
import { loadSlim } from "@tsparticles/slim";
|
import { loadSlim } from "@tsparticles/slim";
|
||||||
import type { AtmosphereState } from "../lib/atmosphereTypes";
|
import type { AtmosphereState } from "../lib/atmosphereTypes";
|
||||||
import { getRainConfig, getEmbersConfig } from "../lib/particleConfigs";
|
import { getRainConfig, getSnowConfig, getEmbersConfig } from "../lib/particleConfigs";
|
||||||
|
|
||||||
// Module-level singleton so the engine is only initialised once
|
// Module-level singleton so the engine is only initialised once
|
||||||
let enginePromise: Promise<void> | null = null;
|
let enginePromise: Promise<void> | null = null;
|
||||||
|
|
@ -28,7 +28,7 @@ export default function ParticleOverlay({ atmosphere }: ParticleOverlayProps) {
|
||||||
|
|
||||||
if (!ready) return null;
|
if (!ready) return null;
|
||||||
|
|
||||||
const { rain, embers } = atmosphere;
|
const { rain, snow, embers } = atmosphere;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
|
@ -39,6 +39,13 @@ export default function ParticleOverlay({ atmosphere }: ParticleOverlayProps) {
|
||||||
options={getRainConfig(rain.intensity)}
|
options={getRainConfig(rain.intensity)}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{snow.active && (
|
||||||
|
<Particles
|
||||||
|
key={`snow-${snow.intensity}`}
|
||||||
|
id="particles-snow"
|
||||||
|
options={getSnowConfig(snow.intensity)}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
{embers.active && (
|
{embers.active && (
|
||||||
<Particles
|
<Particles
|
||||||
key={`embers-${embers.intensity}`}
|
key={`embers-${embers.intensity}`}
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ export interface AtmosphereState {
|
||||||
fog: EffectState;
|
fog: EffectState;
|
||||||
fire: EffectState;
|
fire: EffectState;
|
||||||
rain: EffectState;
|
rain: EffectState;
|
||||||
|
snow: EffectState;
|
||||||
embers: EffectState;
|
embers: EffectState;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -14,5 +15,6 @@ export const defaultAtmosphere: AtmosphereState = {
|
||||||
fog: { active: false, intensity: 50 },
|
fog: { active: false, intensity: 50 },
|
||||||
fire: { active: false, intensity: 50 },
|
fire: { active: false, intensity: 50 },
|
||||||
rain: { active: false, intensity: 50 },
|
rain: { active: false, intensity: 50 },
|
||||||
|
snow: { active: false, intensity: 50 },
|
||||||
embers: { active: false, intensity: 50 },
|
embers: { active: false, intensity: 50 },
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -29,6 +29,37 @@ export function getRainConfig(intensity: number): ISourceOptions {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Snow — intensity 0–100 → count 60–300, speed 1–3.5
|
||||||
|
* Gentle falling flakes with wobble drift. Size varies for depth illusion.
|
||||||
|
*/
|
||||||
|
export function getSnowConfig(intensity: number): ISourceOptions {
|
||||||
|
const count = Math.round(60 + (intensity / 100) * 240);
|
||||||
|
const speed = 1 + (intensity / 100) * 2.5;
|
||||||
|
return {
|
||||||
|
fullScreen: { enable: true, zIndex: 9997 },
|
||||||
|
particles: {
|
||||||
|
number: { value: count, density: { enable: false } },
|
||||||
|
color: { value: ["#ffffff", "#ddeeff", "#eef6ff"] },
|
||||||
|
shape: { type: "circle" },
|
||||||
|
opacity: {
|
||||||
|
value: { min: 0.25, max: 0.75 },
|
||||||
|
animation: { enable: true, speed: 0.3, sync: false, startValue: "random" },
|
||||||
|
},
|
||||||
|
size: { value: { min: 1, max: 5 } },
|
||||||
|
move: {
|
||||||
|
enable: true,
|
||||||
|
speed: speed,
|
||||||
|
direction: "bottom",
|
||||||
|
random: true,
|
||||||
|
straight: false,
|
||||||
|
outModes: { default: "out" },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
interactivity: { events: { onClick: { enable: false }, onHover: { enable: false } } },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Embers — intensity 0–100 → count 50–150, speed 0.5–2
|
* Embers — intensity 0–100 → count 50–150, speed 0.5–2
|
||||||
* Smaller, more numerous slow-drifting sparks. Flickering opacity.
|
* Smaller, more numerous slow-drifting sparks. Flickering opacity.
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue