/** * 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: 0.5, min: 0, max: 1, step: 0.05, 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: 0.1, max: 3, step: 0.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: Override canApply for more complex logic * By default, it just checks isEnabled() */ canApply(controlValues) { // Example: Only apply if text is also enabled const textEnabled = controlValues['textEnabled']; return this.isEnabled(controlValues) && textEnabled; } /** * 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 */