199 lines
5.6 KiB
JavaScript
199 lines
5.6 KiB
JavaScript
/**
|
|
* EXAMPLE EFFECT
|
|
*
|
|
* This is a template for creating new effects.
|
|
* Copy this file and modify it to create your own custom effects.
|
|
*
|
|
* This example creates a "spotlight" effect that highlights a circular area
|
|
* and darkens the rest of the button.
|
|
*/
|
|
|
|
import { ButtonEffect } from "../effect-base.js";
|
|
|
|
/**
|
|
* Spotlight Effect
|
|
* Creates a moving circular spotlight that highlights different areas
|
|
*/
|
|
export class SpotlightEffect extends ButtonEffect {
|
|
constructor() {
|
|
super({
|
|
// Unique ID for this effect (used in control IDs)
|
|
id: "spotlight",
|
|
|
|
// Display name shown in UI
|
|
name: "Spotlight",
|
|
|
|
// Effect type determines render order category
|
|
// Options: 'background', 'border', 'text', 'text2', 'general'
|
|
type: "general",
|
|
|
|
// Category for organizing effects in UI
|
|
category: "Visual Effects",
|
|
|
|
// Render order within type (lower = earlier)
|
|
// 1-9: backgrounds, 10-19: borders, 20-29: transforms,
|
|
// 30-49: text, 50-79: overlays, 80-99: post-processing
|
|
renderOrder: 60,
|
|
});
|
|
}
|
|
|
|
/**
|
|
* Define UI controls for this effect
|
|
* These controls will be automatically bound to the generator
|
|
*/
|
|
defineControls() {
|
|
return [
|
|
// Main enable/disable checkbox
|
|
{
|
|
id: "animate-spotlight",
|
|
type: "checkbox",
|
|
label: "Spotlight Effect",
|
|
defaultValue: false,
|
|
description: "Moving circular spotlight",
|
|
},
|
|
|
|
// Spotlight size control
|
|
{
|
|
id: "spotlight-size",
|
|
type: "range",
|
|
label: "Spotlight Size",
|
|
defaultValue: 20,
|
|
min: 10,
|
|
max: 50,
|
|
step: 1,
|
|
showWhen: "animate-spotlight", // Only show when checkbox is enabled
|
|
description: "Radius of the spotlight",
|
|
},
|
|
|
|
// Darkness of the vignette
|
|
{
|
|
id: "spotlight-darkness",
|
|
type: "range",
|
|
label: "Darkness",
|
|
defaultValue: 1,
|
|
min: 0,
|
|
max: 1,
|
|
step: 0.1,
|
|
showWhen: "animate-spotlight",
|
|
description: "How dark the non-spotlight area should be",
|
|
},
|
|
|
|
// Speed of movement
|
|
{
|
|
id: "spotlight-speed",
|
|
type: "range",
|
|
label: "Movement Speed",
|
|
defaultValue: 1,
|
|
min: 1,
|
|
max: 3,
|
|
step: 1,
|
|
showWhen: "animate-spotlight",
|
|
description: "Speed of spotlight movement",
|
|
},
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Determine if this effect should be applied
|
|
* @param {Object} controlValues - Current values of all controls
|
|
* @returns {boolean}
|
|
*/
|
|
isEnabled(controlValues) {
|
|
return controlValues["animate-spotlight"] === true;
|
|
}
|
|
|
|
/**
|
|
* Apply the effect to the canvas
|
|
*
|
|
* @param {CanvasRenderingContext2D} context - Canvas 2D rendering context
|
|
* @param {Object} controlValues - Current values of all controls
|
|
* @param {AnimationState|null} animState - Animation state (null for static render)
|
|
* @param {Object} renderData - Render information: { width, height, centerX, centerY }
|
|
*/
|
|
apply(context, controlValues, animState, renderData) {
|
|
// Skip if no animation (spotlight needs movement)
|
|
if (!animState) return;
|
|
|
|
// Get control values
|
|
const size = controlValues["spotlight-size"] || 20;
|
|
const darkness = controlValues["spotlight-darkness"] || 0.5;
|
|
const speed = controlValues["spotlight-speed"] || 1;
|
|
|
|
// Calculate spotlight position
|
|
// Move in a circular pattern using animation phase
|
|
const phase = animState.getPhase(speed);
|
|
const spotX = renderData.centerX + Math.cos(phase) * 20;
|
|
const spotY = renderData.centerY + Math.sin(phase) * 10;
|
|
|
|
// Create radial gradient for spotlight effect
|
|
const gradient = context.createRadialGradient(
|
|
spotX,
|
|
spotY,
|
|
0, // Inner circle (center of spotlight)
|
|
spotX,
|
|
spotY,
|
|
size, // Outer circle (edge of spotlight)
|
|
);
|
|
|
|
// Center is transparent (spotlight is bright)
|
|
gradient.addColorStop(0, `rgba(0, 0, 0, 0)`);
|
|
// Edge fades to dark
|
|
gradient.addColorStop(0.5, `rgba(0, 0, 0, ${darkness * 0.3})`);
|
|
gradient.addColorStop(1, `rgba(0, 0, 0, ${darkness})`);
|
|
|
|
// Apply the gradient as an overlay
|
|
context.fillStyle = gradient;
|
|
context.fillRect(0, 0, renderData.width, renderData.height);
|
|
|
|
// Optional: Add a bright center dot
|
|
context.fillStyle = "rgba(255, 255, 255, 0.3)";
|
|
context.beginPath();
|
|
context.arc(spotX, spotY, 2, 0, Math.PI * 2);
|
|
context.fill();
|
|
}
|
|
|
|
/**
|
|
* Optional: Add helper methods for your effect
|
|
*/
|
|
calculateSpotlightPath(progress, width, height) {
|
|
// Example helper method
|
|
return {
|
|
x: width * progress,
|
|
y: height / 2,
|
|
};
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Registration function
|
|
* This is called to add the effect to the generator
|
|
*
|
|
* @param {ButtonGenerator} generator - The button generator instance
|
|
*/
|
|
export function register(generator) {
|
|
generator.registerEffect(new SpotlightEffect());
|
|
}
|
|
|
|
/**
|
|
* USAGE:
|
|
*
|
|
* 1. Copy this file to a new name (e.g., my-effect.js)
|
|
* 2. Modify the class name, id, and effect logic
|
|
* 3. Import in main.js:
|
|
* import * as myEffect from './effects/my-effect.js';
|
|
* 4. Register in setupApp():
|
|
* myEffect.register(generator);
|
|
* 5. Add HTML controls with matching IDs
|
|
*/
|
|
|
|
/**
|
|
* TIPS:
|
|
*
|
|
* - Use animState.progress for linear animations (0 to 1)
|
|
* - Use animState.getPhase(speed) for periodic animations (0 to 2π)
|
|
* - Use Math.sin/cos for smooth periodic motion
|
|
* - Check if (!animState) at the start if your effect requires animation
|
|
* - The context is automatically saved/restored, so feel free to transform
|
|
* - Use renderData for canvas dimensions and center point
|
|
* - Look at existing effects for more examples
|
|
*/
|