Files
animate/docs/js/core/BaseShape.js
Sam 14ec23237f V1.1
Giant refactor. added layers. ui overhaul. added save/load and we now got presets
2025-12-28 03:21:25 +13:00

125 lines
3.4 KiB
JavaScript

/**
* BaseShape - Base class for all animated shapes
*
* To create a new shape:
* 1. Create a new file in /js/shapes/
* 2. Extend BaseShape
* 3. Define static `config` array with control definitions
* 4. Implement constructor with matching parameters
* 5. Implement `draw(elapsed, deltaTime)` method
* 6. Register with shapeRegistry at the end of the file
*
* Example:
* ```
* class MyShape extends BaseShape {
* static config = [
* { type: 'range', property: 'size', min: 10, max: 500, defaultValue: 100 },
* { type: 'color', property: 'color', defaultValue: '#ff0000' }
* ];
*
* constructor(size, color) {
* super();
* this.size = size;
* this.color = color;
* }
*
* draw(elapsed, deltaTime) {
* this.updateFilters(elapsed);
* // Drawing code using this.size and this.color
* }
* }
* shapeRegistry.register('MyShape', MyShape);
* ```
*
* Control types:
* - range: { type, property, min, max, defaultValue, callback? }
* - color: { type, property, defaultValue }
* - checkbox: { type, property, defaultValue }
* - dropdown: { type, property, defaultValue, options: [{value, label}] }
* - button: { type, label, method }
* - header: { type, text }
*/
class BaseShape {
constructor() {
this.controls = [];
this.controlManager = null;
this.speedMultiplier = 500;
}
/**
* Initialize controls using the ControlManager
* Called automatically when a shape is created
* @param {ControlManager} controlManager - The control manager instance
*/
initializeControls(controlManager) {
this.controlManager = controlManager;
// Get config from static property
const config = this.constructor.config || [];
for (const item of config) {
const controlData = controlManager.addControl(item, this);
if (controlData) {
this.controls.push(controlData);
}
}
// Add speed multiplier control (common to all shapes)
const speedControl = controlManager.addControl({
type: 'range',
min: 1,
max: 1000,
defaultValue: this.speedMultiplier,
property: 'speedMultiplier'
}, this);
if (speedControl) {
this.controls.push(speedControl);
}
}
/**
* Clean up controls when shape is removed
*/
remove() {
if (this.controlManager) {
for (const control of this.controls) {
this.controlManager.removeControl(control);
}
}
this.controls = [];
}
/**
* Update any active filters on controls
* Call this at the start of your draw() method
* @param {number} elapsed - Total elapsed time in seconds
*/
updateFilters(elapsed) {
if (!this.controlManager) return;
for (const control of this.controls) {
if (control.filters && control.filters.length > 0) {
const newValue = this.controlManager.filterManager.computeFilteredValue(
control.filters,
elapsed
);
if (newValue !== null && control.element) {
control.element.value = newValue;
const event = new Event('input', { bubbles: true });
control.element.dispatchEvent(event);
}
}
}
}
/**
* Main draw method - override in subclasses
* @param {number} elapsed - Total elapsed time in seconds
* @param {number} deltaTime - Time since last frame
*/
draw(elapsed, deltaTime) {
throw new Error('draw() method not implemented');
}
}