ritual.sh/static/js/button-generator/effects/EXAMPLE.js

205 lines
5.9 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: 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
*/