From 7389cc9330d298f3161a3e99e9ddc8197963feaa Mon Sep 17 00:00:00 2001 From: kturkal <166247156+kturkal@users.noreply.github.com> Date: Fri, 21 Mar 2025 16:08:29 -0400 Subject: [PATCH] Created Stagger Emphasize Animation --- src/animation/StaggerEmphasize.ts | 159 ++++++++++++++++++++++++++++++ src/animation/index.ts | 3 +- 2 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 src/animation/StaggerEmphasize.ts diff --git a/src/animation/StaggerEmphasize.ts b/src/animation/StaggerEmphasize.ts new file mode 100644 index 0000000..80240d3 --- /dev/null +++ b/src/animation/StaggerEmphasize.ts @@ -0,0 +1,159 @@ +import { Animation } from "./Animation.js"; +import * as THREE from "three"; + +export default class StaggerEmphasize extends Animation { + private objectGroups: THREE.Object3D[][] = []; + private initialScales: Map = new Map(); + private largeScale: number; + private stayEmphasized: boolean; + private keyframe = 0.9; + + /** + * Creates a staggered sequence of emphasis animations. + * Objects inside arrays will be emphasized simultaneously. + * + * @param args Objects or groups of objects (in arrays) to emphasize in sequence + * @param config Configuration options including largeScale and stayEmphasized + * @example + * // Emphasize obj1, then obj2 and obj3 together, then obj4 + * new StaggerEmphasize(obj1, [obj2, obj3], obj4, { largeScale: 1.2, stayEmphasized: true }) + */ + constructor( + ...args: Array + ) { + // Must call super first before accessing 'this' + super((elapsedTime) => { + const totalGroups = this.objectGroups.length; + if (totalGroups === 0) return; + + const totalDuration = 1.0; // Total animation takes 1 second + const groupDuration = totalDuration / totalGroups; + + // First, reset all objects to initial scale if they're not yet active + this.objectGroups.forEach((group, groupIndex) => { + const startTime = groupIndex * groupDuration; + if (elapsedTime < startTime) { + // Not yet active, ensure initial scale + group.forEach(obj => { + const initialScale = this.initialScales.get(obj) || 1; + obj.scale.setScalar(initialScale); + }); + } + }); + + // Then animate active groups + this.objectGroups.forEach((group, groupIndex) => { + const startTime = groupIndex * groupDuration; + const endTime = startTime + groupDuration; + + // Skip if we haven't reached this group yet + if (elapsedTime < startTime) return; + + // For each object in this group + group.forEach(obj => { + const initialScale = this.initialScales.get(obj) || 1; + let scale; + + // Calculate normalized local time for this group + const localTime = (elapsedTime - startTime) / groupDuration; + + if (this.stayEmphasized) { + // If we're past the end time for this group and need to stay emphasized + if (elapsedTime > endTime) { + // Stay at keyframe (peak emphasis) + scale = initialScale * this.largeScale; + } + // Otherwise animate normally up to keyframe + else if (localTime <= this.keyframe) { + // Interpolate from initial to large scale + const t = localTime / this.keyframe; + scale = initialScale * (1 + (this.largeScale - 1) * t); + } else { + // At or past keyframe, stay at large scale + scale = initialScale * this.largeScale; + } + } + // Regular animation (not staying emphasized) + else { + // Only animate if within our time window + if (elapsedTime <= endTime) { + if (localTime <= this.keyframe) { + // Growing phase - to peak + const t = localTime / this.keyframe; + scale = initialScale * (1 + (this.largeScale - 1) * t); + } else { + // Shrinking phase - back to normal + const t = (localTime - this.keyframe) / (1 - this.keyframe); + scale = initialScale * (this.largeScale - (this.largeScale - 1) * t); + } + } else { + // Past our time window, return to initial scale + scale = initialScale; + } + } + + // Apply the calculated scale + obj.scale.setScalar(scale); + }); + }); + }, { reveal: true }); + + // Parse arguments + let config = {}; + let objects = [...args]; + + // Extract config if the last argument is an object but not a THREE.Object3D + if (args.length > 0 && !Array.isArray(args[args.length-1]) && + !(args[args.length-1] instanceof THREE.Object3D)) { + config = objects.pop() as any; + } + + const { largeScale = 1.1, stayEmphasized = false } = config as any; + this.largeScale = largeScale; + this.stayEmphasized = stayEmphasized; + + // Process each argument - organize into groups + objects.forEach((item) => { + if (Array.isArray(item)) { + // This is already a group, filter to only include THREE.Object3D instances + const validObjects = item.filter(obj => obj instanceof THREE.Object3D) as THREE.Object3D[]; + if (validObjects.length > 0) { + this.objectGroups.push(validObjects); + } + } else if (item instanceof THREE.Object3D) { + // Single object becomes its own group + this.objectGroups.push([item]); + } + }); + + // Debug output + console.log(`StaggerEmphasize: Created with ${this.objectGroups.length} groups`); + this.objectGroups.forEach((group, i) => { + console.log(`Group ${i}: ${group.length} objects`); + }); + } + + setUp() { + super.setUp(); + + // Store initial scales of all objects + this.objectGroups.forEach(group => { + group.forEach(obj => { + this.initialScales.set(obj, obj.scale.x); + console.log(`Initial scale for object: ${obj.scale.x}`); + }); + }); + } + + tearDown() { + // Restore all objects to their initial scales + this.objectGroups.forEach(group => { + group.forEach(obj => { + const initialScale = this.initialScales.get(obj) || 1; + obj.scale.setScalar(initialScale); + }); + }); + + super.tearDown(); + } +} \ No newline at end of file diff --git a/src/animation/index.ts b/src/animation/index.ts index 8cb45a8..162613e 100644 --- a/src/animation/index.ts +++ b/src/animation/index.ts @@ -12,4 +12,5 @@ export { default as Wait } from "./Wait.js"; export { default as Emphasize } from "./Emphasize.js"; export { default as Shake } from "./Shake.js"; export { default as Grow } from "./Grow.js"; -export { default as Stagger } from "./Stagger.js"; \ No newline at end of file +export { default as Stagger } from "./Stagger.js"; +export { default as StaggerEmphasize } from "./StaggerEmphasize.js"; \ No newline at end of file