Custom Effects
Create your own effects by extending the Effect base class.
Effect Structure
class CustomEffect extends Effect {
constructor(target, config) {
super(target, config);
// Find the appropriate layer
let effectLayer = target.layer;
if (!effectLayer) {
let current = target;
while (current && !effectLayer) {
if (current.className === 'Layer') effectLayer = current;
current = current.parent;
}
}
if (!effectLayer) effectLayer = paper.project.activeLayer;
// Create visual elements
this.myVisual = new paper.Path.Circle({
center: target.position,
radius: config.size || 10,
fillColor: config.color || 'white',
parent: effectLayer
});
// Track for cleanup
this.visuals.push(this.myVisual);
}
update(dt) {
// Called every frame
// dt = delta time in seconds
if (!this.target || !this.target.parent) {
this.isFinished = true;
return;
}
// Update visual position, animation, etc.
this.myVisual.position = this.target.position;
}
updateConfig(config) {
super.updateConfig(config);
// Apply new config to visuals
if (config.color) {
this.myVisual.fillColor = config.color;
}
}
remove() {
// Base class removes all items in this.visuals
super.remove();
}
}
Registering Your Effect
Add to the switch statement in EffectSystem.addEffect():
case 'custom':
effect = new CustomEffect(item, config);
break;
Effect Lifecycle
- Constructor: Create visual elements
- update(dt): Called every frame, animate visuals
- updateConfig(): Called when config changes
- remove(): Cleanup when effect is removed
Important Properties
| Property | Description |
|---|---|
this.target |
The Paper.js item this effect is attached to |
this.config |
Effect configuration object |
this.visuals |
Array of visual elements (auto-cleaned) |
this.isFinished |
Set true to remove effect |
Example: Orbit Effect
class OrbitEffect extends Effect {
constructor(target, config) {
super(target, config);
const layer = target.layer || paper.project.activeLayer;
this.orbitDot = new paper.Path.Circle({
center: target.position,
radius: config.dotSize || 5,
fillColor: config.color || '#60a5fa',
parent: layer
});
this.visuals.push(this.orbitDot);
this.angle = 0;
this.radius = config.radius || 50;
this.speed = config.speed || 2;
}
update(dt) {
if (!this.target || !this.target.parent) {
this.isFinished = true;
return;
}
this.angle += this.speed * dt;
const x = this.target.position.x + Math.cos(this.angle) * this.radius;
const y = this.target.position.y + Math.sin(this.angle) * this.radius;
this.orbitDot.position = new paper.Point(x, y);
}
}
Tips
- Always check
this.target.parentin update() to handle deleted items - Use
this.visuals.push()for automatic cleanup - Set
this.isFinished = trueto remove the effect - Access frame time via the
dtparameter