New effects, refactor

This commit is contained in:
Dan 2026-01-09 13:20:06 +00:00
parent 4ac45367e5
commit c0d6bee9c3
14 changed files with 1620 additions and 215 deletions

View file

@ -0,0 +1,191 @@
import { ButtonEffect } from "../effect-base.js";
/**
* Aurora/Plasma background effect
* Flowing organic color patterns using layered gradients
*/
export class AuroraEffect extends ButtonEffect {
constructor() {
super({
id: "bg-aurora",
name: "Aurora",
type: "general",
category: "Background Animations",
renderOrder: 55,
});
}
defineControls() {
return [
{
id: "animate-aurora",
type: "checkbox",
label: "Aurora Effect",
defaultValue: false,
},
{
id: "aurora-speed",
type: "range",
label: "Flow Speed",
defaultValue: 1,
min: 1,
max: 3,
step: 1,
showWhen: "animate-aurora",
description: "Speed of flowing colors",
},
{
id: "aurora-intensity",
type: "range",
label: "Intensity",
defaultValue: 0.6,
min: 0.2,
max: 1,
step: 0.1,
showWhen: "animate-aurora",
description: "Brightness and opacity",
},
{
id: "aurora-complexity",
type: "range",
label: "Complexity",
defaultValue: 3,
min: 2,
max: 6,
step: 1,
showWhen: "animate-aurora",
description: "Number of wave layers",
},
{
id: "aurora-color-scheme",
type: "select",
label: "Color Scheme",
defaultValue: "northern",
options: [
{ value: "northern", label: "Northern Lights" },
{ value: "purple", label: "Purple Dream" },
{ value: "fire", label: "Fire" },
{ value: "ocean", label: "Ocean" },
{ value: "rainbow", label: "Rainbow" },
],
showWhen: "animate-aurora",
description: "Color palette",
},
];
}
isEnabled(controlValues) {
return controlValues["animate-aurora"] === true;
}
getColorScheme(scheme, hue) {
switch (scheme) {
case "northern":
return [
{ h: 120, s: 70, l: 50 }, // Green
{ h: 160, s: 70, l: 50 }, // Teal
{ h: 200, s: 70, l: 50 }, // Blue
];
case "purple":
return [
{ h: 270, s: 70, l: 50 }, // Purple
{ h: 300, s: 70, l: 50 }, // Magenta
{ h: 330, s: 70, l: 50 }, // Pink
];
case "fire":
return [
{ h: 0, s: 80, l: 50 }, // Red
{ h: 30, s: 80, l: 50 }, // Orange
{ h: 50, s: 80, l: 50 }, // Yellow-Orange
];
case "ocean":
return [
{ h: 180, s: 70, l: 50 }, // Cyan
{ h: 200, s: 70, l: 50 }, // Light Blue
{ h: 220, s: 70, l: 50 }, // Blue
];
case "rainbow":
return [
{ h: (hue + 0) % 360, s: 70, l: 50 },
{ h: (hue + 120) % 360, s: 70, l: 50 },
{ h: (hue + 240) % 360, s: 70, l: 50 },
];
default:
return [
{ h: 120, s: 70, l: 50 },
{ h: 180, s: 70, l: 50 },
{ h: 240, s: 70, l: 50 },
];
}
}
apply(context, controlValues, animState, renderData) {
if (!animState) return;
const speed = controlValues["aurora-speed"] || 1;
const intensity = controlValues["aurora-intensity"] || 0.6;
const complexity = controlValues["aurora-complexity"] || 3;
const colorScheme = controlValues["aurora-color-scheme"] || "northern";
const time = animState.getPhase(speed);
// Create flowing hue shift that loops properly (only used for rainbow scheme)
// Convert phase (0 to 2π) to hue degrees (0 to 360)
const hueShift = (time / (Math.PI * 2)) * 360;
const colors = this.getColorScheme(colorScheme, hueShift);
// Draw multiple overlapping gradients to create aurora effect
context.globalCompositeOperation = "screen"; // Blend mode for aurora effect
for (let i = 0; i < complexity; i++) {
const phase = time + i * ((Math.PI * 2) / complexity);
// Calculate wave positions
const wave1X =
renderData.centerX + Math.sin(phase) * renderData.width * 0.5;
const wave1Y =
renderData.centerY + Math.cos(phase * 1.3) * renderData.height * 0.5;
// Create radial gradient
const gradient = context.createRadialGradient(
wave1X,
wave1Y,
0,
wave1X,
wave1Y,
renderData.width * 0.8,
);
// Pick color based on wave index
const colorIdx = i % colors.length;
const color = colors[colorIdx];
const baseOpacity = intensity * 0.3;
// Rainbow scheme already has hueShift applied in getColorScheme
// Other schemes use their fixed colors
gradient.addColorStop(
0,
`hsla(${color.h}, ${color.s}%, ${color.l}%, ${baseOpacity})`,
);
gradient.addColorStop(
0.5,
`hsla(${color.h}, ${color.s}%, ${color.l}%, ${baseOpacity * 0.5})`,
);
gradient.addColorStop(
1,
`hsla(${color.h}, ${color.s}%, ${color.l}%, 0)`,
);
context.fillStyle = gradient;
context.fillRect(0, 0, renderData.width, renderData.height);
}
// Reset composite operation
context.globalCompositeOperation = "source-over";
}
}
export function register(generator) {
generator.registerEffect(new AuroraEffect());
}