New effects, refactor
This commit is contained in:
parent
4ac45367e5
commit
c0d6bee9c3
14 changed files with 1620 additions and 215 deletions
216
static/js/button-generator/effects/background-fire.js
Normal file
216
static/js/button-generator/effects/background-fire.js
Normal file
|
|
@ -0,0 +1,216 @@
|
|||
import { ButtonEffect } from "../effect-base.js";
|
||||
|
||||
/**
|
||||
* Fire background effect
|
||||
* Animated flames rising from bottom using particles
|
||||
*/
|
||||
export class FireEffect extends ButtonEffect {
|
||||
constructor() {
|
||||
super({
|
||||
id: "bg-fire",
|
||||
name: "Fire",
|
||||
type: "general",
|
||||
category: "Background Animations",
|
||||
renderOrder: 55,
|
||||
});
|
||||
|
||||
this.particles = [];
|
||||
this.initialized = false;
|
||||
}
|
||||
|
||||
defineControls() {
|
||||
return [
|
||||
{
|
||||
id: "animate-fire",
|
||||
type: "checkbox",
|
||||
label: "Fire Effect",
|
||||
defaultValue: false,
|
||||
},
|
||||
{
|
||||
id: "fire-intensity",
|
||||
type: "range",
|
||||
label: "Intensity",
|
||||
defaultValue: 50,
|
||||
min: 20,
|
||||
max: 100,
|
||||
step: 5,
|
||||
showWhen: "animate-fire",
|
||||
description: "Number of flame particles",
|
||||
},
|
||||
{
|
||||
id: "fire-height",
|
||||
type: "range",
|
||||
label: "Flame Height",
|
||||
defaultValue: 0.6,
|
||||
min: 0.3,
|
||||
max: 1,
|
||||
step: 0.1,
|
||||
showWhen: "animate-fire",
|
||||
description: "How high flames reach",
|
||||
},
|
||||
{
|
||||
id: "fire-speed",
|
||||
type: "range",
|
||||
label: "Speed",
|
||||
defaultValue: 1,
|
||||
min: 0.3,
|
||||
max: 3,
|
||||
step: 0.1,
|
||||
showWhen: "animate-fire",
|
||||
description: "Speed of rising flames",
|
||||
},
|
||||
{
|
||||
id: "fire-color-scheme",
|
||||
type: "select",
|
||||
label: "Color Scheme",
|
||||
defaultValue: "normal",
|
||||
options: [
|
||||
{ value: "normal", label: "Normal Fire" },
|
||||
{ value: "blue", label: "Blue Flame" },
|
||||
{ value: "green", label: "Green Flame" },
|
||||
{ value: "purple", label: "Purple Flame" },
|
||||
{ value: "white", label: "White Hot" },
|
||||
],
|
||||
showWhen: "animate-fire",
|
||||
description: "Flame color",
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
isEnabled(controlValues) {
|
||||
return controlValues["animate-fire"] === true;
|
||||
}
|
||||
|
||||
getFireColors(scheme) {
|
||||
switch (scheme) {
|
||||
case "normal":
|
||||
return [
|
||||
{ r: 255, g: 60, b: 0 }, // Red-orange
|
||||
{ r: 255, g: 140, b: 0 }, // Orange
|
||||
{ r: 255, g: 200, b: 0 }, // Yellow
|
||||
];
|
||||
case "blue":
|
||||
return [
|
||||
{ r: 0, g: 100, b: 255 }, // Blue
|
||||
{ r: 100, g: 180, b: 255 }, // Light blue
|
||||
{ r: 200, g: 230, b: 255 }, // Very light blue
|
||||
];
|
||||
case "green":
|
||||
return [
|
||||
{ r: 0, g: 200, b: 50 }, // Green
|
||||
{ r: 100, g: 255, b: 100 }, // Light green
|
||||
{ r: 200, g: 255, b: 150 }, // Very light green
|
||||
];
|
||||
case "purple":
|
||||
return [
|
||||
{ r: 150, g: 0, b: 255 }, // Purple
|
||||
{ r: 200, g: 100, b: 255 }, // Light purple
|
||||
{ r: 230, g: 180, b: 255 }, // Very light purple
|
||||
];
|
||||
case "white":
|
||||
return [
|
||||
{ r: 255, g: 200, b: 150 }, // Warm white
|
||||
{ r: 255, g: 240, b: 200 }, // Light white
|
||||
{ r: 255, g: 255, b: 255 }, // Pure white
|
||||
];
|
||||
default:
|
||||
return [
|
||||
{ r: 255, g: 60, b: 0 },
|
||||
{ r: 255, g: 140, b: 0 },
|
||||
{ r: 255, g: 200, b: 0 },
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
apply(context, controlValues, animState, renderData) {
|
||||
if (!animState) return;
|
||||
|
||||
const intensity = controlValues["fire-intensity"] || 50;
|
||||
const height = controlValues["fire-height"] || 0.6;
|
||||
const speed = controlValues["fire-speed"] || 1;
|
||||
const colorScheme = controlValues["fire-color-scheme"] || "normal";
|
||||
|
||||
const colors = this.getFireColors(colorScheme);
|
||||
const maxHeight = renderData.height * height;
|
||||
|
||||
// Spawn new particles at the bottom
|
||||
for (let i = 0; i < intensity / 10; i++) {
|
||||
this.particles.push({
|
||||
x: Math.random() * renderData.width,
|
||||
y: renderData.height,
|
||||
vx: (Math.random() - 0.5) * 1.5,
|
||||
vy: -(2 + Math.random() * 3) * speed,
|
||||
size: 2 + Math.random() * 6,
|
||||
life: 1.0,
|
||||
colorIndex: Math.random(),
|
||||
});
|
||||
}
|
||||
|
||||
// Update and draw particles
|
||||
this.particles = this.particles.filter((particle) => {
|
||||
// Update position
|
||||
particle.x += particle.vx;
|
||||
particle.y += particle.vy;
|
||||
|
||||
// Add some turbulence
|
||||
particle.vx += (Math.random() - 0.5) * 0.2;
|
||||
particle.vy *= 0.98; // Slow down as they rise
|
||||
|
||||
// Fade out based on height and time
|
||||
const heightRatio =
|
||||
(renderData.height - particle.y) / renderData.height;
|
||||
particle.life -= 0.015;
|
||||
|
||||
if (particle.life > 0 && particle.y > renderData.height - maxHeight) {
|
||||
// Choose color based on life (hotter at bottom, cooler at top)
|
||||
const colorProgress = 1 - particle.life;
|
||||
const colorIdx = Math.floor(colorProgress * (colors.length - 1));
|
||||
const colorBlend = (colorProgress * (colors.length - 1)) % 1;
|
||||
|
||||
const c1 = colors[Math.min(colorIdx, colors.length - 1)];
|
||||
const c2 = colors[Math.min(colorIdx + 1, colors.length - 1)];
|
||||
|
||||
const r = Math.floor(c1.r + (c2.r - c1.r) * colorBlend);
|
||||
const g = Math.floor(c1.g + (c2.g - c1.g) * colorBlend);
|
||||
const b = Math.floor(c1.b + (c2.b - c1.b) * colorBlend);
|
||||
|
||||
// Draw particle with gradient
|
||||
const gradient = context.createRadialGradient(
|
||||
particle.x,
|
||||
particle.y,
|
||||
0,
|
||||
particle.x,
|
||||
particle.y,
|
||||
particle.size
|
||||
);
|
||||
|
||||
gradient.addColorStop(
|
||||
0,
|
||||
`rgba(${r}, ${g}, ${b}, ${particle.life * 0.8})`
|
||||
);
|
||||
gradient.addColorStop(
|
||||
0.5,
|
||||
`rgba(${r}, ${g}, ${b}, ${particle.life * 0.5})`
|
||||
);
|
||||
gradient.addColorStop(1, `rgba(${r}, ${g}, ${b}, 0)`);
|
||||
|
||||
context.fillStyle = gradient;
|
||||
context.beginPath();
|
||||
context.arc(particle.x, particle.y, particle.size, 0, Math.PI * 2);
|
||||
context.fill();
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
||||
// Limit particle count
|
||||
if (this.particles.length > intensity * 5) {
|
||||
this.particles = this.particles.slice(-intensity * 5);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function register(generator) {
|
||||
generator.registerEffect(new FireEffect());
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue