Start of the rewrite to a modular system

This commit is contained in:
Dan 2026-01-09 09:15:05 +00:00
parent 2bfdc30caa
commit 4ac45367e5
29 changed files with 4414 additions and 588 deletions

View file

@ -0,0 +1,232 @@
import { ButtonEffect } from '../effect-base.js';
/**
* Standard text rendering effect
* Renders static text (when no animations are active)
*/
export class StandardTextEffect extends ButtonEffect {
constructor(textLineNumber = 1) {
const suffix = textLineNumber === 1 ? '' : '2';
super({
id: `text-standard${suffix}`,
name: `Standard Text ${textLineNumber}`,
type: textLineNumber === 1 ? 'text' : 'text2',
category: textLineNumber === 1 ? 'Text Line 1' : 'Text Line 2',
renderOrder: 20, // After animations
textLineNumber: textLineNumber // Pass through config so defineControls can access it
});
this.textLineNumber = textLineNumber;
}
defineControls() {
// Access from config since this is called before constructor completes
const textLineNumber = this.textLineNumber || this.config?.textLineNumber || 1;
const suffix = textLineNumber === 1 ? '' : '2';
return [
{
id: `button-text${suffix}`,
type: 'text',
label: `Text Line ${textLineNumber}`,
defaultValue: textLineNumber === 1 ? 'RITUAL.SH' : ''
},
{
id: `text${suffix}-enabled`,
type: 'checkbox',
label: `Enable Text Line ${textLineNumber}`,
defaultValue: textLineNumber === 1
},
{
id: `font-size${suffix}`,
type: 'range',
label: 'Font Size',
min: 6,
max: 24,
defaultValue: textLineNumber === 1 ? 14 : 12
},
{
id: `text${suffix}-x`,
type: 'range',
label: 'Horizontal Position',
min: 0,
max: 100,
defaultValue: 50,
description: 'Percentage from left'
},
{
id: `text${suffix}-y`,
type: 'range',
label: 'Vertical Position',
min: 0,
max: 100,
defaultValue: textLineNumber === 1 ? 35 : 65,
description: 'Percentage from top'
},
{
id: `text${suffix}-color-type`,
type: 'select',
label: 'Color Type',
defaultValue: 'solid',
options: [
{ value: 'solid', label: 'Solid Color' },
{ value: 'gradient', label: 'Gradient' }
]
},
{
id: `text${suffix}-color`,
type: 'color',
label: 'Text Color',
defaultValue: '#ffffff',
showWhen: `text${suffix}-color-type`
},
{
id: `text${suffix}-gradient-color1`,
type: 'color',
label: 'Gradient Color 1',
defaultValue: '#ffffff',
showWhen: `text${suffix}-color-type`
},
{
id: `text${suffix}-gradient-color2`,
type: 'color',
label: 'Gradient Color 2',
defaultValue: '#00ffff',
showWhen: `text${suffix}-color-type`
},
{
id: `text${suffix}-gradient-angle`,
type: 'range',
label: 'Gradient Angle',
min: 0,
max: 360,
defaultValue: 0,
showWhen: `text${suffix}-color-type`
},
{
id: `text${suffix}-outline`,
type: 'checkbox',
label: 'Outline',
defaultValue: false
},
{
id: `outline${suffix}-color`,
type: 'color',
label: 'Outline Color',
defaultValue: '#000000',
showWhen: `text${suffix}-outline`
},
{
id: `font-family${suffix}`,
type: 'select',
label: 'Font',
defaultValue: 'Lato',
options: [
{ value: 'Lato', label: 'Lato' },
{ value: 'Roboto', label: 'Roboto' },
{ value: 'Open Sans', label: 'Open Sans' },
{ value: 'Montserrat', label: 'Montserrat' },
{ value: 'Oswald', label: 'Oswald' },
{ value: 'Bebas Neue', label: 'Bebas Neue' },
{ value: 'Roboto Mono', label: 'Roboto Mono' },
{ value: 'VT323', label: 'VT323' },
{ value: 'Press Start 2P', label: 'Press Start 2P' },
{ value: 'DSEG7-Classic', label: 'DSEG7' }
]
},
{
id: `font-bold${suffix}`,
type: 'checkbox',
label: 'Bold',
defaultValue: false
},
{
id: `font-italic${suffix}`,
type: 'checkbox',
label: 'Italic',
defaultValue: false
}
];
}
isEnabled(controlValues) {
const suffix = this.textLineNumber === 1 ? '' : '2';
const text = controlValues[`button-text${suffix}`];
const enabled = controlValues[`text${suffix}-enabled`];
// Only render if text exists, is enabled, and no animations are active on this text
const waveActive = controlValues[`animate-text-wave${suffix}`];
const rainbowActive = controlValues[`animate-text-rainbow${suffix}`];
return text && enabled && !waveActive && !rainbowActive;
}
apply(context, controlValues, animState, renderData) {
const suffix = this.textLineNumber === 1 ? '' : '2';
const text = controlValues[`button-text${suffix}`];
if (!text) return;
const fontSize = controlValues[`font-size${suffix}`] || 12;
const fontWeight = controlValues[`font-bold${suffix}`] ? 'bold' : 'normal';
const fontStyle = controlValues[`font-italic${suffix}`] ? 'italic' : 'normal';
const fontFamily = controlValues[`font-family${suffix}`] || 'Arial';
const x = (controlValues[`text${suffix}-x`] / 100) * renderData.width;
const y = (controlValues[`text${suffix}-y`] / 100) * renderData.height;
// Set font
context.font = `${fontStyle} ${fontWeight} ${fontSize}px "${fontFamily}"`;
context.textAlign = 'center';
context.textBaseline = 'middle';
// Get colors
const colors = this.getTextColors(context, controlValues, text, x, y, fontSize);
// Draw outline if enabled
if (controlValues[`text${suffix}-outline`]) {
context.strokeStyle = colors.strokeStyle;
context.lineWidth = 2;
context.strokeText(text, x, y);
}
// Draw text
context.fillStyle = colors.fillStyle;
context.fillText(text, x, y);
}
/**
* Get text colors (solid or gradient)
*/
getTextColors(context, controlValues, text, x, y, fontSize) {
const suffix = this.textLineNumber === 1 ? '' : '2';
const colorType = controlValues[`text${suffix}-color-type`] || 'solid';
let fillStyle, strokeStyle;
if (colorType === 'solid') {
fillStyle = controlValues[`text${suffix}-color`] || '#ffffff';
strokeStyle = controlValues[`outline${suffix}-color`] || '#000000';
} else {
// Gradient
const angle = (controlValues[`text${suffix}-gradient-angle`] || 0) * (Math.PI / 180);
const textWidth = context.measureText(text).width;
const x1 = x - textWidth / 2 + (Math.cos(angle) * textWidth) / 2;
const y1 = y - fontSize / 2 + (Math.sin(angle) * fontSize) / 2;
const x2 = x + textWidth / 2 - (Math.cos(angle) * textWidth) / 2;
const y2 = y + fontSize / 2 - (Math.sin(angle) * fontSize) / 2;
const gradient = context.createLinearGradient(x1, y1, x2, y2);
gradient.addColorStop(0, controlValues[`text${suffix}-gradient-color1`] || '#ffffff');
gradient.addColorStop(1, controlValues[`text${suffix}-gradient-color2`] || '#00ffff');
fillStyle = gradient;
strokeStyle = controlValues[`outline${suffix}-color`] || '#000000';
}
return { fillStyle, strokeStyle };
}
}
// Auto-register effect
export function register(generator) {
generator.registerEffect(new StandardTextEffect(1));
generator.registerEffect(new StandardTextEffect(2));
}