@guinetik/gcanvas 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/release.yaml +70 -0
- package/.jshintrc +4 -0
- package/.vscode/settings.json +22 -0
- package/CLAUDE.md +310 -0
- package/blackhole.jpg +0 -0
- package/demo.png +0 -0
- package/demos/CNAME +1 -0
- package/demos/animations.html +31 -0
- package/demos/basic.html +38 -0
- package/demos/baskara.html +31 -0
- package/demos/bezier.html +35 -0
- package/demos/beziersignature.html +29 -0
- package/demos/blackhole.html +28 -0
- package/demos/blob.html +35 -0
- package/demos/demos.css +289 -0
- package/demos/easing.html +28 -0
- package/demos/events.html +195 -0
- package/demos/fluent.html +647 -0
- package/demos/fractals.html +36 -0
- package/demos/genart.html +26 -0
- package/demos/gendream.html +26 -0
- package/demos/group.html +36 -0
- package/demos/home.html +587 -0
- package/demos/index.html +364 -0
- package/demos/isometric.html +34 -0
- package/demos/js/animations.js +452 -0
- package/demos/js/basic.js +204 -0
- package/demos/js/baskara.js +751 -0
- package/demos/js/bezier.js +692 -0
- package/demos/js/beziersignature.js +241 -0
- package/demos/js/blackhole/accretiondisk.obj.js +379 -0
- package/demos/js/blackhole/blackhole.obj.js +318 -0
- package/demos/js/blackhole/index.js +409 -0
- package/demos/js/blackhole/particle.js +56 -0
- package/demos/js/blackhole/starfield.obj.js +218 -0
- package/demos/js/blob.js +2263 -0
- package/demos/js/easing.js +477 -0
- package/demos/js/fluent.js +183 -0
- package/demos/js/fractals.js +931 -0
- package/demos/js/fractalworker.js +93 -0
- package/demos/js/genart.js +268 -0
- package/demos/js/gendream.js +209 -0
- package/demos/js/group.js +140 -0
- package/demos/js/info-toggle.js +25 -0
- package/demos/js/isometric.js +863 -0
- package/demos/js/kerr.js +1556 -0
- package/demos/js/lavalamp.js +590 -0
- package/demos/js/layout.js +354 -0
- package/demos/js/mondrian.js +285 -0
- package/demos/js/opacity.js +275 -0
- package/demos/js/painter.js +484 -0
- package/demos/js/particles-showcase.js +514 -0
- package/demos/js/particles.js +299 -0
- package/demos/js/patterns.js +397 -0
- package/demos/js/penrose/artifact.js +69 -0
- package/demos/js/penrose/blackhole.js +121 -0
- package/demos/js/penrose/constants.js +73 -0
- package/demos/js/penrose/game.js +943 -0
- package/demos/js/penrose/lore.js +278 -0
- package/demos/js/penrose/penrosescene.js +892 -0
- package/demos/js/penrose/ship.js +216 -0
- package/demos/js/penrose/sounds.js +211 -0
- package/demos/js/penrose/voidparticle.js +55 -0
- package/demos/js/penrose/voidscene.js +258 -0
- package/demos/js/penrose/voidship.js +144 -0
- package/demos/js/penrose/wormhole.js +46 -0
- package/demos/js/pipeline.js +555 -0
- package/demos/js/scene.js +304 -0
- package/demos/js/scenes.js +320 -0
- package/demos/js/schrodinger.js +410 -0
- package/demos/js/schwarzschild.js +1023 -0
- package/demos/js/shapes.js +628 -0
- package/demos/js/space/alien.js +171 -0
- package/demos/js/space/boom.js +98 -0
- package/demos/js/space/boss.js +353 -0
- package/demos/js/space/buff.js +73 -0
- package/demos/js/space/bullet.js +102 -0
- package/demos/js/space/constants.js +85 -0
- package/demos/js/space/game.js +1884 -0
- package/demos/js/space/hud.js +112 -0
- package/demos/js/space/laserbeam.js +179 -0
- package/demos/js/space/lightning.js +277 -0
- package/demos/js/space/minion.js +192 -0
- package/demos/js/space/missile.js +212 -0
- package/demos/js/space/player.js +430 -0
- package/demos/js/space/powerup.js +90 -0
- package/demos/js/space/starfield.js +58 -0
- package/demos/js/space/starpower.js +90 -0
- package/demos/js/spacetime.js +559 -0
- package/demos/js/svgtween.js +204 -0
- package/demos/js/tde/accretiondisk.js +418 -0
- package/demos/js/tde/blackhole.js +219 -0
- package/demos/js/tde/blackholescene.js +209 -0
- package/demos/js/tde/config.js +59 -0
- package/demos/js/tde/index.js +695 -0
- package/demos/js/tde/jets.js +290 -0
- package/demos/js/tde/lensedstarfield.js +147 -0
- package/demos/js/tde/tdestar.js +317 -0
- package/demos/js/tde/tidalstream.js +356 -0
- package/demos/js/tde_old/blackhole.obj.js +354 -0
- package/demos/js/tde_old/debris.obj.js +791 -0
- package/demos/js/tde_old/flare.obj.js +239 -0
- package/demos/js/tde_old/index.js +448 -0
- package/demos/js/tde_old/star.obj.js +812 -0
- package/demos/js/tiles.js +312 -0
- package/demos/js/tweendemo.js +79 -0
- package/demos/js/visibility.js +102 -0
- package/demos/kerr.html +28 -0
- package/demos/lavalamp.html +27 -0
- package/demos/layouts.html +37 -0
- package/demos/logo.svg +4 -0
- package/demos/loop.html +84 -0
- package/demos/mondrian.html +32 -0
- package/demos/og_image.png +0 -0
- package/demos/opacity.html +36 -0
- package/demos/painter.html +39 -0
- package/demos/particles-showcase.html +28 -0
- package/demos/particles.html +24 -0
- package/demos/patterns.html +33 -0
- package/demos/penrose-game.html +31 -0
- package/demos/pipeline.html +737 -0
- package/demos/scene.html +33 -0
- package/demos/scenes.html +96 -0
- package/demos/schrodinger.html +27 -0
- package/demos/schwarzschild.html +27 -0
- package/demos/shapes.html +16 -0
- package/demos/space.html +85 -0
- package/demos/spacetime.html +27 -0
- package/demos/svgtween.html +29 -0
- package/demos/tde.html +28 -0
- package/demos/tiles.html +28 -0
- package/demos/transforms.html +400 -0
- package/demos/tween.html +45 -0
- package/demos/visibility.html +33 -0
- package/disk_example.png +0 -0
- package/docs/README.md +222 -0
- package/docs/concepts/architecture-overview.md +204 -0
- package/docs/concepts/lifecycle.md +255 -0
- package/docs/concepts/rendering-pipeline.md +279 -0
- package/docs/concepts/tde-zorder.md +106 -0
- package/docs/concepts/two-layer-architecture.md +229 -0
- package/docs/getting-started/first-game.md +354 -0
- package/docs/getting-started/hello-world.md +269 -0
- package/docs/getting-started/installation.md +157 -0
- package/docs/modules/collision/README.md +453 -0
- package/docs/modules/fluent/README.md +1075 -0
- package/docs/modules/game/README.md +303 -0
- package/docs/modules/isometric-camera.md +210 -0
- package/docs/modules/isometric.md +275 -0
- package/docs/modules/painter/README.md +328 -0
- package/docs/modules/particle/README.md +559 -0
- package/docs/modules/shapes/README.md +221 -0
- package/docs/modules/shapes/base/euclidian.md +123 -0
- package/docs/modules/shapes/base/geometry2d.md +204 -0
- package/docs/modules/shapes/base/renderable.md +215 -0
- package/docs/modules/shapes/base/shape.md +262 -0
- package/docs/modules/shapes/base/transformable.md +243 -0
- package/docs/modules/shapes/hierarchy.md +218 -0
- package/docs/modules/state/README.md +577 -0
- package/docs/modules/util/README.md +99 -0
- package/docs/modules/util/camera3d.md +412 -0
- package/docs/modules/util/scene3d.md +395 -0
- package/index.html +17 -0
- package/jsdoc.json +50 -0
- package/package.json +55 -0
- package/readme.md +599 -0
- package/scripts/build-demo.js +69 -0
- package/scripts/bundle4llm.js +276 -0
- package/scripts/clearconsole.js +48 -0
- package/src/collision/collision-system.js +332 -0
- package/src/collision/collision.js +303 -0
- package/src/collision/index.js +10 -0
- package/src/fluent/fluent-game.js +430 -0
- package/src/fluent/fluent-go.js +1060 -0
- package/src/fluent/fluent-layer.js +152 -0
- package/src/fluent/fluent-scene.js +291 -0
- package/src/fluent/index.js +98 -0
- package/src/fluent/sketch.js +380 -0
- package/src/game/game.js +467 -0
- package/src/game/index.js +49 -0
- package/src/game/objects/go.js +220 -0
- package/src/game/objects/imagego.js +30 -0
- package/src/game/objects/index.js +54 -0
- package/src/game/objects/isometric-scene.js +260 -0
- package/src/game/objects/layoutscene.js +549 -0
- package/src/game/objects/scene.js +175 -0
- package/src/game/objects/scene3d.js +118 -0
- package/src/game/objects/text.js +221 -0
- package/src/game/objects/wrapper.js +232 -0
- package/src/game/pipeline.js +243 -0
- package/src/game/ui/button.js +396 -0
- package/src/game/ui/cursor.js +93 -0
- package/src/game/ui/fps.js +91 -0
- package/src/game/ui/index.js +5 -0
- package/src/game/ui/togglebutton.js +93 -0
- package/src/game/ui/tooltip.js +249 -0
- package/src/index.js +25 -0
- package/src/io/events.js +20 -0
- package/src/io/index.js +86 -0
- package/src/io/input.js +70 -0
- package/src/io/keys.js +152 -0
- package/src/io/mouse.js +61 -0
- package/src/io/touch.js +39 -0
- package/src/logger/debugtab.js +138 -0
- package/src/logger/index.js +3 -0
- package/src/logger/loggable.js +47 -0
- package/src/logger/logger.js +113 -0
- package/src/math/complex.js +37 -0
- package/src/math/constants.js +1 -0
- package/src/math/fractal.js +1271 -0
- package/src/math/gr.js +201 -0
- package/src/math/heat.js +202 -0
- package/src/math/index.js +12 -0
- package/src/math/noise.js +433 -0
- package/src/math/orbital.js +191 -0
- package/src/math/patterns.js +1339 -0
- package/src/math/penrose.js +259 -0
- package/src/math/quantum.js +115 -0
- package/src/math/random.js +195 -0
- package/src/math/tensor.js +1009 -0
- package/src/mixins/anchor.js +131 -0
- package/src/mixins/draggable.js +72 -0
- package/src/mixins/index.js +2 -0
- package/src/motion/bezier.js +132 -0
- package/src/motion/bounce.js +58 -0
- package/src/motion/easing.js +349 -0
- package/src/motion/float.js +130 -0
- package/src/motion/follow.js +125 -0
- package/src/motion/hop.js +52 -0
- package/src/motion/index.js +82 -0
- package/src/motion/motion.js +1124 -0
- package/src/motion/orbit.js +49 -0
- package/src/motion/oscillate.js +39 -0
- package/src/motion/parabolic.js +141 -0
- package/src/motion/patrol.js +147 -0
- package/src/motion/pendulum.js +48 -0
- package/src/motion/pulse.js +88 -0
- package/src/motion/shake.js +83 -0
- package/src/motion/spiral.js +144 -0
- package/src/motion/spring.js +150 -0
- package/src/motion/swing.js +47 -0
- package/src/motion/tween.js +92 -0
- package/src/motion/tweenetik.js +139 -0
- package/src/motion/waypoint.js +210 -0
- package/src/painter/index.js +8 -0
- package/src/painter/painter.colors.js +331 -0
- package/src/painter/painter.effects.js +230 -0
- package/src/painter/painter.img.js +229 -0
- package/src/painter/painter.js +295 -0
- package/src/painter/painter.lines.js +189 -0
- package/src/painter/painter.opacity.js +41 -0
- package/src/painter/painter.shapes.js +277 -0
- package/src/painter/painter.text.js +273 -0
- package/src/particle/emitter.js +124 -0
- package/src/particle/index.js +11 -0
- package/src/particle/particle-system.js +322 -0
- package/src/particle/particle.js +71 -0
- package/src/particle/updaters.js +170 -0
- package/src/shapes/arc.js +43 -0
- package/src/shapes/arrow.js +33 -0
- package/src/shapes/bezier.js +42 -0
- package/src/shapes/circle.js +62 -0
- package/src/shapes/clouds.js +56 -0
- package/src/shapes/cone.js +219 -0
- package/src/shapes/cross.js +70 -0
- package/src/shapes/cube.js +244 -0
- package/src/shapes/cylinder.js +254 -0
- package/src/shapes/diamond.js +48 -0
- package/src/shapes/euclidian.js +111 -0
- package/src/shapes/figure.js +115 -0
- package/src/shapes/geometry.js +220 -0
- package/src/shapes/group.js +375 -0
- package/src/shapes/heart.js +42 -0
- package/src/shapes/hexagon.js +26 -0
- package/src/shapes/image.js +192 -0
- package/src/shapes/index.js +111 -0
- package/src/shapes/line.js +29 -0
- package/src/shapes/pattern.js +90 -0
- package/src/shapes/pin.js +44 -0
- package/src/shapes/poly.js +31 -0
- package/src/shapes/prism.js +226 -0
- package/src/shapes/rect.js +35 -0
- package/src/shapes/renderable.js +333 -0
- package/src/shapes/ring.js +26 -0
- package/src/shapes/roundrect.js +95 -0
- package/src/shapes/shape.js +117 -0
- package/src/shapes/slice.js +26 -0
- package/src/shapes/sphere.js +314 -0
- package/src/shapes/sphere3d.js +537 -0
- package/src/shapes/square.js +15 -0
- package/src/shapes/star.js +99 -0
- package/src/shapes/svg.js +408 -0
- package/src/shapes/text.js +553 -0
- package/src/shapes/traceable.js +83 -0
- package/src/shapes/transform.js +357 -0
- package/src/shapes/transformable.js +172 -0
- package/src/shapes/triangle.js +26 -0
- package/src/sound/index.js +17 -0
- package/src/sound/sound.js +473 -0
- package/src/sound/synth.analyzer.js +149 -0
- package/src/sound/synth.effects.js +207 -0
- package/src/sound/synth.envelope.js +59 -0
- package/src/sound/synth.js +229 -0
- package/src/sound/synth.musical.js +160 -0
- package/src/sound/synth.noise.js +85 -0
- package/src/sound/synth.oscillators.js +293 -0
- package/src/state/index.js +10 -0
- package/src/state/state-machine.js +371 -0
- package/src/util/camera3d.js +438 -0
- package/src/util/index.js +6 -0
- package/src/util/isometric-camera.js +235 -0
- package/src/util/layout.js +317 -0
- package/src/util/position.js +147 -0
- package/src/util/tasks.js +47 -0
- package/src/util/zindex.js +287 -0
- package/src/webgl/index.js +9 -0
- package/src/webgl/shaders/sphere-shaders.js +994 -0
- package/src/webgl/webgl-renderer.js +388 -0
- package/tde.png +0 -0
- package/test/math/orbital.test.js +61 -0
- package/test/math/tensor.test.js +114 -0
- package/test/particle/emitter.test.js +204 -0
- package/test/particle/particle-system.test.js +310 -0
- package/test/particle/particle.test.js +116 -0
- package/test/particle/updaters.test.js +386 -0
- package/test/setup.js +120 -0
- package/test/shapes/euclidian.test.js +44 -0
- package/test/shapes/geometry.test.js +86 -0
- package/test/shapes/group.test.js +86 -0
- package/test/shapes/rectangle.test.js +64 -0
- package/test/shapes/transform.test.js +379 -0
- package/test/util/camera3d.test.js +428 -0
- package/test/util/scene3d.test.js +352 -0
- package/types/collision.d.ts +249 -0
- package/types/common.d.ts +155 -0
- package/types/game.d.ts +497 -0
- package/types/index.d.ts +309 -0
- package/types/io.d.ts +188 -0
- package/types/logger.d.ts +127 -0
- package/types/math.d.ts +268 -0
- package/types/mixins.d.ts +92 -0
- package/types/motion.d.ts +678 -0
- package/types/painter.d.ts +378 -0
- package/types/shapes.d.ts +864 -0
- package/types/sound.d.ts +672 -0
- package/types/state.d.ts +251 -0
- package/types/util.d.ts +253 -0
- package/vite.config.js +50 -0
- package/vitest.config.js +13 -0
|
@@ -0,0 +1,1124 @@
|
|
|
1
|
+
import { bezierV1 } from "./bezier.js";
|
|
2
|
+
import { bounceV1 } from "./bounce.js";
|
|
3
|
+
import { floatV1 } from "./float.js";
|
|
4
|
+
import { followPath } from "./follow.js";
|
|
5
|
+
import { orbitV1 } from "./orbit.js";
|
|
6
|
+
import { oscillateV1 } from "./oscillate.js";
|
|
7
|
+
import { parabolicV1 } from "./parabolic.js";
|
|
8
|
+
import { patrolV1 } from "./patrol.js";
|
|
9
|
+
import { pendulumV1 } from "./pendulum.js";
|
|
10
|
+
import { pulseV1 } from "./pulse.js";
|
|
11
|
+
import { hopV1 } from "./hop.js";
|
|
12
|
+
import { shakeV1 } from "./shake.js";
|
|
13
|
+
import { spiralV1 } from "./spiral.js";
|
|
14
|
+
import { springV1 } from "./spring.js";
|
|
15
|
+
import { swingV1 } from "./swing.js";
|
|
16
|
+
import { waypointV1 } from "./waypoint.js";
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* @class
|
|
20
|
+
* A utility class that provides a collection of stateless animation primitive functions meant to have a simple but consistent API.
|
|
21
|
+
*
|
|
22
|
+
* Each animation method accepts time parameters and returns interpolated values without storing
|
|
23
|
+
* state between calls, making it suitable for any rendering loop or game engine.
|
|
24
|
+
*
|
|
25
|
+
* This class does not mutate values on target objects; instead it returns a result with the value for the current frame.
|
|
26
|
+
*
|
|
27
|
+
* The caller is responsible for updating their entities with the result.
|
|
28
|
+
*
|
|
29
|
+
* The goal is to provide out-of-the box, configurable animation loops that can be adapted to diferent contexts.
|
|
30
|
+
*
|
|
31
|
+
* If you want simple object property interpolation checkout {@link Tweenetik}.<br/><br/>
|
|
32
|
+
*
|
|
33
|
+
* Unlike {@link Tweenetik}, which mutates target objects directly and manages time internally for UI transitions,
|
|
34
|
+
* Motion is designed for real-time game loops or simulations where you control the update timing (`dt`) externally.
|
|
35
|
+
* <br/>
|
|
36
|
+
* Use Motion when you want precise, stateless, loop-driven animations tied to elapsed time.
|
|
37
|
+
* <br/>
|
|
38
|
+
* Use Tweenetik for one-off UI animations like button presses, transitions, or effects decoupled from game time.
|
|
39
|
+
* <br/><br/>
|
|
40
|
+
* Each animation method follows the same pattern:<br/>
|
|
41
|
+
* - Takes elapsed time and duration parameters<br/>
|
|
42
|
+
* - Returns a standardized result object with animation values and metadata<br/>
|
|
43
|
+
* - Maintains a stateless design (input same time parameters, get same result)<br/>
|
|
44
|
+
* - Supports looping, callbacks, and customizable parameters<br/>
|
|
45
|
+
*<br/><br/>
|
|
46
|
+
* Core animation concepts:<br/>
|
|
47
|
+
* - Animations are driven by elapsed time, not frame counts<br/>
|
|
48
|
+
* - Values are interpolated using normalized time (0-1)<br/>
|
|
49
|
+
* - Optional easing functions can be applied to create custom motion curves<br/>
|
|
50
|
+
* - All animations support loop and yoyo options for cyclic behavior<br/>
|
|
51
|
+
*
|
|
52
|
+
* @example
|
|
53
|
+
* // Basic usage with a game loop
|
|
54
|
+
* function update(dt) {
|
|
55
|
+
* // Update animation time
|
|
56
|
+
* character.animTime += dt;
|
|
57
|
+
*
|
|
58
|
+
* // Get interpolated position using a spring animation
|
|
59
|
+
* const result = Motion.spring(
|
|
60
|
+
* 0, // Initial position
|
|
61
|
+
* 100, // Target position
|
|
62
|
+
* character.animTime, // Current time
|
|
63
|
+
* 1.5, // Duration
|
|
64
|
+
* true, // Loop
|
|
65
|
+
* true, // Yoyo
|
|
66
|
+
* { stiffness: 0.7, damping: 0.5 }
|
|
67
|
+
* );
|
|
68
|
+
*
|
|
69
|
+
* // Apply the result
|
|
70
|
+
* character.x = result.value;
|
|
71
|
+
* }
|
|
72
|
+
*
|
|
73
|
+
* @example
|
|
74
|
+
* // Animating along a path with waypoints
|
|
75
|
+
* function updateGuard(dt) {
|
|
76
|
+
* guard.animTime += dt;
|
|
77
|
+
*
|
|
78
|
+
* const result = Motion.waypoint(
|
|
79
|
+
* guard, // Target object
|
|
80
|
+
* guard.animTime, // Current time
|
|
81
|
+
* [ // Array of waypoints
|
|
82
|
+
* [0, 0],
|
|
83
|
+
* [100, 0],
|
|
84
|
+
* [100, 100],
|
|
85
|
+
* [0, 100]
|
|
86
|
+
* ],
|
|
87
|
+
* 50, // Speed (units per second)
|
|
88
|
+
* 2, // Wait time at each point
|
|
89
|
+
* true // Loop
|
|
90
|
+
* );
|
|
91
|
+
*
|
|
92
|
+
* // Apply position
|
|
93
|
+
* guard.x = result.x;
|
|
94
|
+
* guard.y = result.y;
|
|
95
|
+
*
|
|
96
|
+
* // Update animation state
|
|
97
|
+
* if (result.moving) {
|
|
98
|
+
* guard.playAnimation('walk_' + result.direction);
|
|
99
|
+
* } else {
|
|
100
|
+
* guard.playAnimation('idle');
|
|
101
|
+
* }
|
|
102
|
+
* }
|
|
103
|
+
*
|
|
104
|
+
* @example
|
|
105
|
+
* // Creating a floating effect
|
|
106
|
+
* function updateBoat(dt) {
|
|
107
|
+
* boat.animTime += dt;
|
|
108
|
+
*
|
|
109
|
+
* const result = Motion.float(
|
|
110
|
+
* boat.originalX,
|
|
111
|
+
* boat.originalY,
|
|
112
|
+
* boat.animTime,
|
|
113
|
+
* 5, // Duration
|
|
114
|
+
* 0.5, // Speed
|
|
115
|
+
* 0.7, // Energy/randomness
|
|
116
|
+
* 15, // Float radius
|
|
117
|
+
* true // Loop
|
|
118
|
+
* );
|
|
119
|
+
*
|
|
120
|
+
* // Apply position
|
|
121
|
+
* boat.x = result.x;
|
|
122
|
+
* boat.y = result.y;
|
|
123
|
+
* }
|
|
124
|
+
*/
|
|
125
|
+
export class Motion {
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Base animation result constructor
|
|
129
|
+
* Creates a standardized result object for all animations
|
|
130
|
+
*
|
|
131
|
+
* @param {Object} values - The calculated animation values
|
|
132
|
+
* @param {number} t - Current normalized time (0-1)
|
|
133
|
+
* @param {boolean} loop - Whether the animation is looping
|
|
134
|
+
* @param {boolean} completed - Whether a non-looping animation has completed
|
|
135
|
+
* @param {Object} state - Internal state object for continuity between calls
|
|
136
|
+
* @returns {Object} Standardized animation result
|
|
137
|
+
*/
|
|
138
|
+
static animationResult(values, t, loop, completed = false, state = null) {
|
|
139
|
+
return {
|
|
140
|
+
...values, // Animation-specific values (x, y, value, etc.)
|
|
141
|
+
t, // Normalized time (0-1)
|
|
142
|
+
progress: t, // Alias for normalized time
|
|
143
|
+
loop, // Whether animation is looping
|
|
144
|
+
completed, // Whether animation has completed (non-looping only)
|
|
145
|
+
state, // Internal state for the next call
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Processes time and calculates normalized t value
|
|
151
|
+
* Handles looping, duration, and triggers callbacks
|
|
152
|
+
*
|
|
153
|
+
* @param {number} elapsedTime - Total elapsed time in seconds
|
|
154
|
+
* @param {number} duration - Duration of one animation cycle in seconds
|
|
155
|
+
* @param {boolean} loop - Whether animation should loop
|
|
156
|
+
* @param {Object} [callbacks] - Optional callback functions
|
|
157
|
+
* @param {Function} [callbacks.onStart] - Called when animation starts
|
|
158
|
+
* @param {Function} [callbacks.onComplete] - Called when animation completes
|
|
159
|
+
* @param {Function} [callbacks.onLoop] - Called when animation loops
|
|
160
|
+
* @param {Object} [state] - Internal state tracking for callbacks
|
|
161
|
+
* @returns {Object} { t, completed, state }
|
|
162
|
+
*/
|
|
163
|
+
static _step(
|
|
164
|
+
elapsedTime,
|
|
165
|
+
duration,
|
|
166
|
+
loop,
|
|
167
|
+
callbacks = {},
|
|
168
|
+
state = { started: false, loopCount: 0 }
|
|
169
|
+
) {
|
|
170
|
+
// Calculate normalized time (0 to 1)
|
|
171
|
+
let t = duration > 0 ? elapsedTime / duration : 1;
|
|
172
|
+
let completed = false;
|
|
173
|
+
// Handle callback state if not provided
|
|
174
|
+
state = state || { started: false, loopCount: 0 };
|
|
175
|
+
// Call onStart callback once
|
|
176
|
+
if (!state.started && callbacks.onStart) {
|
|
177
|
+
callbacks.onStart();
|
|
178
|
+
state.started = true;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// Handle looping
|
|
182
|
+
if (loop) {
|
|
183
|
+
// Get only the fractional part for looping animations
|
|
184
|
+
t = t % 1;
|
|
185
|
+
// Track loop count for onLoop callback
|
|
186
|
+
const newLoopCount = Math.floor(elapsedTime / duration);
|
|
187
|
+
if (newLoopCount > state.loopCount && callbacks.onLoop) {
|
|
188
|
+
callbacks.onLoop(newLoopCount);
|
|
189
|
+
state.loopCount = newLoopCount;
|
|
190
|
+
}
|
|
191
|
+
} else {
|
|
192
|
+
// Clamp to 1 for non-looping animations
|
|
193
|
+
if (t >= 1) {
|
|
194
|
+
t = 1;
|
|
195
|
+
completed = true;
|
|
196
|
+
// Call onComplete callback once
|
|
197
|
+
if (!state.completed && callbacks.onComplete) {
|
|
198
|
+
callbacks.onComplete();
|
|
199
|
+
state.completed = true;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return { t, completed, state };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/**
|
|
208
|
+
* Updates animation time and applies easing
|
|
209
|
+
*
|
|
210
|
+
* @param {number} elapsedTime - Total elapsed time in seconds
|
|
211
|
+
* @param {number} duration - Duration of animation in seconds
|
|
212
|
+
* @param {boolean} loop - Whether animation should loop
|
|
213
|
+
* @param {Function} [easingFn=null] - Easing function to apply
|
|
214
|
+
* @param {Object} [callbacks] - Optional callback functions
|
|
215
|
+
* @param {Object} [state] - Internal state tracking for callbacks
|
|
216
|
+
* @returns {Object} { t, easedT, completed, state }
|
|
217
|
+
*/
|
|
218
|
+
static _frame(
|
|
219
|
+
elapsedTime,
|
|
220
|
+
duration,
|
|
221
|
+
loop,
|
|
222
|
+
easingFn = null,
|
|
223
|
+
callbacks = {},
|
|
224
|
+
state = null
|
|
225
|
+
) {
|
|
226
|
+
// Process time and handle callbacks
|
|
227
|
+
const {
|
|
228
|
+
t,
|
|
229
|
+
completed,
|
|
230
|
+
state: newState,
|
|
231
|
+
} = this._step(elapsedTime, duration, loop, callbacks, state);
|
|
232
|
+
// Apply easing if provided
|
|
233
|
+
const easedT = easingFn ? easingFn(t) : t;
|
|
234
|
+
return { t, easedT, completed, state: newState };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Oscillate between min and max value using sine
|
|
239
|
+
*
|
|
240
|
+
* @param {number} min - Minimum value
|
|
241
|
+
* @param {number} max - Maximum value
|
|
242
|
+
* @param {number} elapsedTime - Total elapsed time in seconds
|
|
243
|
+
* @param {number} duration - Duration of one full oscillation in seconds
|
|
244
|
+
* @param {boolean} [loop=true] - Whether animation should loop
|
|
245
|
+
* @param {Function} [easingFn=null] - Optional easing function to apply
|
|
246
|
+
* @param {Object} [callbacks] - Optional callback functions
|
|
247
|
+
* @param {Object} [state] - Internal state tracking for callbacks
|
|
248
|
+
* @returns {Object} Animation result with value and metadata
|
|
249
|
+
*/
|
|
250
|
+
static oscillate(
|
|
251
|
+
min,
|
|
252
|
+
max,
|
|
253
|
+
elapsedTime,
|
|
254
|
+
duration,
|
|
255
|
+
loop = true,
|
|
256
|
+
easingFn = null,
|
|
257
|
+
callbacks = {},
|
|
258
|
+
state = null
|
|
259
|
+
) {
|
|
260
|
+
return oscillateV1(
|
|
261
|
+
min,
|
|
262
|
+
max,
|
|
263
|
+
elapsedTime,
|
|
264
|
+
duration,
|
|
265
|
+
loop,
|
|
266
|
+
easingFn,
|
|
267
|
+
callbacks,
|
|
268
|
+
state
|
|
269
|
+
);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Parabolic arc interpolation
|
|
274
|
+
*
|
|
275
|
+
* @param {number} start - Start value
|
|
276
|
+
* @param {number} peak - Peak value
|
|
277
|
+
* @param {number} end - End value
|
|
278
|
+
* @param {number} elapsedTime - Total elapsed time in seconds
|
|
279
|
+
* @param {number} duration - Duration of animation in seconds
|
|
280
|
+
* @param {boolean} [loop=false] - Whether animation should loop (restart from beginning)
|
|
281
|
+
* @param {boolean} [yoyo=false] - Whether animation should reverse direction at the end
|
|
282
|
+
* @param {Function} [easingFn=null] - Optional easing function to apply
|
|
283
|
+
* @param {Object} [callbacks] - Optional callback functions
|
|
284
|
+
* @param {Object} [state] - Internal state tracking for callbacks
|
|
285
|
+
* @returns {Object} Animation result with value and metadata
|
|
286
|
+
*/
|
|
287
|
+
static parabolic(
|
|
288
|
+
start,
|
|
289
|
+
peak,
|
|
290
|
+
end,
|
|
291
|
+
elapsedTime,
|
|
292
|
+
duration,
|
|
293
|
+
loop = false,
|
|
294
|
+
yoyo = false,
|
|
295
|
+
easingFn = null,
|
|
296
|
+
callbacks = {},
|
|
297
|
+
state = null
|
|
298
|
+
) {
|
|
299
|
+
return parabolicV1(
|
|
300
|
+
start,
|
|
301
|
+
peak,
|
|
302
|
+
end,
|
|
303
|
+
elapsedTime,
|
|
304
|
+
duration,
|
|
305
|
+
loop,
|
|
306
|
+
yoyo,
|
|
307
|
+
easingFn,
|
|
308
|
+
callbacks,
|
|
309
|
+
state
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Patrol animation - creates movement around an area with natural pausing
|
|
315
|
+
* Perfect for characters patrolling or objects drifting within bounds
|
|
316
|
+
*
|
|
317
|
+
* @param {Object} target - Object with x,y properties defining the center point
|
|
318
|
+
* @param {number} elapsedTime - Total elapsed time in seconds
|
|
319
|
+
* @param {number} duration - Duration of one full float cycle in seconds
|
|
320
|
+
* @param {number} speed - Movement speed multiplier (0.1-2.0 recommended)
|
|
321
|
+
* @param {number} randomness - How random/unpredictable the float path is (0-1)
|
|
322
|
+
* @param {number} radius - Radius of float area (object will move within -radius to +radius)
|
|
323
|
+
* @param {boolean} [loop=true] - Whether animation should loop
|
|
324
|
+
* @param {Function} [easingFn=null] - Optional easing function to apply
|
|
325
|
+
* @param {Object} [callbacks] - Optional callback functions
|
|
326
|
+
* @param {Object} [state] - Internal state tracking for callbacks and initial position
|
|
327
|
+
* @returns {Object} Animation result with x, y coordinates and metadata
|
|
328
|
+
*/
|
|
329
|
+
static float(
|
|
330
|
+
target,
|
|
331
|
+
elapsedTime,
|
|
332
|
+
duration,
|
|
333
|
+
speed,
|
|
334
|
+
randomness,
|
|
335
|
+
radius,
|
|
336
|
+
loop = true,
|
|
337
|
+
easingFn = null,
|
|
338
|
+
callbacks = {},
|
|
339
|
+
state = null
|
|
340
|
+
) {
|
|
341
|
+
return floatV1(
|
|
342
|
+
target,
|
|
343
|
+
elapsedTime,
|
|
344
|
+
duration,
|
|
345
|
+
speed,
|
|
346
|
+
randomness,
|
|
347
|
+
radius,
|
|
348
|
+
loop,
|
|
349
|
+
easingFn,
|
|
350
|
+
callbacks,
|
|
351
|
+
state
|
|
352
|
+
);
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* Spring animation that uses elastic easing functions for a bouncy spring effect
|
|
357
|
+
* Stateless implementation that doesn't require previous frame state
|
|
358
|
+
*
|
|
359
|
+
* @param {number} initial - Initial value (starting position)
|
|
360
|
+
* @param {number} target - Target value (ending position)
|
|
361
|
+
* @param {number} elapsedTime - Total elapsed time in seconds
|
|
362
|
+
* @param {number} duration - Duration of one complete cycle in seconds
|
|
363
|
+
* @param {boolean} [loop=false] - Whether animation should loop
|
|
364
|
+
* @param {boolean} [yoyo=false] - Whether animation should return to initial value
|
|
365
|
+
* @param {Object} [springParams] - Spring parameters
|
|
366
|
+
* @param {number} [springParams.stiffness=0.3] - Spring stiffness (0-1)
|
|
367
|
+
* @param {number} [springParams.damping=0.6] - Damping factor (0-1)
|
|
368
|
+
* @param {Object} [callbacks] - Optional callback functions
|
|
369
|
+
* @param {Function} [callbacks.onStart] - Called when animation starts
|
|
370
|
+
* @param {Function} [callbacks.onComplete] - Called when animation completes
|
|
371
|
+
* @param {Function} [callbacks.onLoop] - Called when animation loops
|
|
372
|
+
* @param {Function} [callbacks.onYoyoTurn] - Called when yoyo animation changes direction
|
|
373
|
+
* @returns {Object} Animation result with value, velocity and metadata
|
|
374
|
+
*/
|
|
375
|
+
static spring(
|
|
376
|
+
initial,
|
|
377
|
+
target,
|
|
378
|
+
elapsedTime,
|
|
379
|
+
duration,
|
|
380
|
+
loop = false,
|
|
381
|
+
yoyo = false,
|
|
382
|
+
springParams = {},
|
|
383
|
+
callbacks = {}
|
|
384
|
+
) {
|
|
385
|
+
return springV1(
|
|
386
|
+
initial,
|
|
387
|
+
target,
|
|
388
|
+
elapsedTime,
|
|
389
|
+
duration,
|
|
390
|
+
loop,
|
|
391
|
+
yoyo,
|
|
392
|
+
springParams,
|
|
393
|
+
callbacks
|
|
394
|
+
);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
static swing(
|
|
398
|
+
centerX,
|
|
399
|
+
centerY,
|
|
400
|
+
maxAngle,
|
|
401
|
+
elapsedTime,
|
|
402
|
+
duration,
|
|
403
|
+
loop = true,
|
|
404
|
+
yoyo = true,
|
|
405
|
+
easingFn = null,
|
|
406
|
+
callbacks = {},
|
|
407
|
+
state = null
|
|
408
|
+
) {
|
|
409
|
+
return swingV1(
|
|
410
|
+
centerX,
|
|
411
|
+
centerY,
|
|
412
|
+
maxAngle,
|
|
413
|
+
elapsedTime,
|
|
414
|
+
duration,
|
|
415
|
+
loop,
|
|
416
|
+
yoyo,
|
|
417
|
+
easingFn,
|
|
418
|
+
callbacks,
|
|
419
|
+
state
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
static pendulum(
|
|
424
|
+
originAngle,
|
|
425
|
+
amplitude,
|
|
426
|
+
elapsedTime,
|
|
427
|
+
duration,
|
|
428
|
+
loop = true,
|
|
429
|
+
damped = false,
|
|
430
|
+
easingFn = null,
|
|
431
|
+
callbacks = {},
|
|
432
|
+
state = null
|
|
433
|
+
) {
|
|
434
|
+
return pendulumV1(
|
|
435
|
+
originAngle,
|
|
436
|
+
amplitude,
|
|
437
|
+
elapsedTime,
|
|
438
|
+
duration,
|
|
439
|
+
loop,
|
|
440
|
+
damped,
|
|
441
|
+
easingFn,
|
|
442
|
+
callbacks,
|
|
443
|
+
state
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* Pulse between min and max value and back
|
|
449
|
+
*
|
|
450
|
+
* @param {number} min - Minimum value
|
|
451
|
+
* @param {number} max - Maximum value
|
|
452
|
+
* @param {number} elapsedTime - Total elapsed time in seconds
|
|
453
|
+
* @param {number} duration - Duration of one full pulse in seconds
|
|
454
|
+
* @param {boolean} [loop=true] - Whether animation should loop
|
|
455
|
+
* @param {boolean} [yoyo=false] - Whether to use separate easing for return journey
|
|
456
|
+
* @param {Function} [easingFn=null] - Optional easing function to apply
|
|
457
|
+
* @param {Object} [callbacks] - Optional callback functions
|
|
458
|
+
* @returns {Object} Animation result with value and metadata
|
|
459
|
+
*/
|
|
460
|
+
static pulse(
|
|
461
|
+
min,
|
|
462
|
+
max,
|
|
463
|
+
elapsedTime,
|
|
464
|
+
duration,
|
|
465
|
+
loop = true,
|
|
466
|
+
yoyo = false,
|
|
467
|
+
easingFn = null,
|
|
468
|
+
callbacks = {}
|
|
469
|
+
) {
|
|
470
|
+
return pulseV1(
|
|
471
|
+
min,
|
|
472
|
+
max,
|
|
473
|
+
elapsedTime,
|
|
474
|
+
duration,
|
|
475
|
+
loop,
|
|
476
|
+
yoyo,
|
|
477
|
+
easingFn,
|
|
478
|
+
(callbacks = {})
|
|
479
|
+
);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Spiral motion animation
|
|
484
|
+
*
|
|
485
|
+
* @param {number} centerX - X coordinate of spiral center
|
|
486
|
+
* @param {number} centerY - Y coordinate of spiral center
|
|
487
|
+
* @param {number} startRadius - Starting radius of the spiral
|
|
488
|
+
* @param {number} endRadius - Ending radius of the spiral
|
|
489
|
+
* @param {number} startAngle - Starting angle in radians
|
|
490
|
+
* @param {number} revolutions - Number of complete revolutions
|
|
491
|
+
* @param {number} elapsedTime - Total elapsed time in seconds
|
|
492
|
+
* @param {number} duration - Duration of animation in seconds
|
|
493
|
+
* @param {boolean} [loop=false] - Whether animation should loop (restart from beginning)
|
|
494
|
+
* @param {boolean} [yoyo=false] - Whether animation should reverse direction at the end
|
|
495
|
+
* @param {Function} [easingFn=null] - Optional easing function to apply
|
|
496
|
+
* @param {Object} [callbacks] - Optional callback functions
|
|
497
|
+
* @param {Object} [state] - Internal state tracking for callbacks
|
|
498
|
+
* @returns {Object} Animation result with x, y coordinates and metadata
|
|
499
|
+
*/
|
|
500
|
+
static spiral(
|
|
501
|
+
centerX,
|
|
502
|
+
centerY,
|
|
503
|
+
startRadius,
|
|
504
|
+
endRadius,
|
|
505
|
+
startAngle,
|
|
506
|
+
revolutions,
|
|
507
|
+
elapsedTime,
|
|
508
|
+
duration,
|
|
509
|
+
loop = false,
|
|
510
|
+
yoyo = false,
|
|
511
|
+
easingFn = null,
|
|
512
|
+
callbacks = {},
|
|
513
|
+
state = null
|
|
514
|
+
) {
|
|
515
|
+
return spiralV1(
|
|
516
|
+
centerX,
|
|
517
|
+
centerY,
|
|
518
|
+
startRadius,
|
|
519
|
+
endRadius,
|
|
520
|
+
startAngle,
|
|
521
|
+
revolutions,
|
|
522
|
+
elapsedTime,
|
|
523
|
+
duration,
|
|
524
|
+
loop,
|
|
525
|
+
yoyo,
|
|
526
|
+
easingFn,
|
|
527
|
+
callbacks,
|
|
528
|
+
state
|
|
529
|
+
);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
/**
|
|
533
|
+
* Orbit motion animation (circular or elliptical)
|
|
534
|
+
*
|
|
535
|
+
* @param {number} centerX - X coordinate of orbit center
|
|
536
|
+
* @param {number} centerY - Y coordinate of orbit center
|
|
537
|
+
* @param {number} radiusX - X radius of the orbit (horizontal)
|
|
538
|
+
* @param {number} radiusY - Y radius of the orbit (vertical)
|
|
539
|
+
* @param {number} startAngle - Starting angle in radians
|
|
540
|
+
* @param {number} elapsedTime - Total elapsed time in seconds
|
|
541
|
+
* @param {number} duration - Duration of one full orbit in seconds
|
|
542
|
+
* @param {boolean} [loop=true] - Whether animation should loop
|
|
543
|
+
* @param {boolean} [clockwise=true] - Direction of orbit
|
|
544
|
+
* @param {Function} [easingFn=null] - Optional easing function to apply
|
|
545
|
+
* @param {Object} [callbacks] - Optional callback functions
|
|
546
|
+
* @param {Object} [state] - Internal state tracking for callbacks
|
|
547
|
+
* @returns {Object} Animation result with x, y coordinates and metadata
|
|
548
|
+
*/
|
|
549
|
+
static orbit(
|
|
550
|
+
centerX,
|
|
551
|
+
centerY,
|
|
552
|
+
radiusX,
|
|
553
|
+
radiusY,
|
|
554
|
+
startAngle,
|
|
555
|
+
elapsedTime,
|
|
556
|
+
duration,
|
|
557
|
+
loop = true,
|
|
558
|
+
clockwise = true,
|
|
559
|
+
easingFn = null,
|
|
560
|
+
callbacks = {},
|
|
561
|
+
state = null
|
|
562
|
+
) {
|
|
563
|
+
return orbitV1(
|
|
564
|
+
centerX,
|
|
565
|
+
centerY,
|
|
566
|
+
radiusX,
|
|
567
|
+
radiusY,
|
|
568
|
+
startAngle,
|
|
569
|
+
elapsedTime,
|
|
570
|
+
duration,
|
|
571
|
+
loop,
|
|
572
|
+
clockwise,
|
|
573
|
+
easingFn,
|
|
574
|
+
callbacks,
|
|
575
|
+
state
|
|
576
|
+
);
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
/**
|
|
580
|
+
* Bezier curve motion animation with yoyo support
|
|
581
|
+
*
|
|
582
|
+
* @param {Array<number>} p0 - Start point [x, y]
|
|
583
|
+
* @param {Array<number>} p1 - Control point 1 [x, y]
|
|
584
|
+
* @param {Array<number>} p2 - Control point 2 [x, y]
|
|
585
|
+
* @param {Array<number>} p3 - End point [x, y]
|
|
586
|
+
* @param {number} elapsedTime - Total elapsed time in seconds
|
|
587
|
+
* @param {number} duration - Duration of animation in seconds
|
|
588
|
+
* @param {boolean} [loop=false] - Whether animation should loop
|
|
589
|
+
* @param {boolean} [yoyo=false] - Whether animation should return to start
|
|
590
|
+
* @param {Function} [easingFn=null] - Optional easing function to apply
|
|
591
|
+
* @param {Object} [callbacks] - Optional callback functions
|
|
592
|
+
* @param {Object} [state] - Internal state tracking for callbacks
|
|
593
|
+
* @returns {Object} Animation result with x, y coordinates and metadata
|
|
594
|
+
*/
|
|
595
|
+
static bezier(
|
|
596
|
+
p0,
|
|
597
|
+
p1,
|
|
598
|
+
p2,
|
|
599
|
+
p3,
|
|
600
|
+
elapsedTime,
|
|
601
|
+
duration,
|
|
602
|
+
loop = false,
|
|
603
|
+
yoyo = false,
|
|
604
|
+
easingFn = null,
|
|
605
|
+
callbacks = {},
|
|
606
|
+
state = null
|
|
607
|
+
) {
|
|
608
|
+
return bezierV1(
|
|
609
|
+
p0,
|
|
610
|
+
p1,
|
|
611
|
+
p2,
|
|
612
|
+
p3,
|
|
613
|
+
elapsedTime,
|
|
614
|
+
duration,
|
|
615
|
+
loop,
|
|
616
|
+
yoyo,
|
|
617
|
+
easingFn,
|
|
618
|
+
callbacks,
|
|
619
|
+
state
|
|
620
|
+
);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
/**
|
|
624
|
+
* Bounce animation - object drops and bounces with diminishing height
|
|
625
|
+
*
|
|
626
|
+
* @param {number} maxHeight - Maximum height (negative y value)
|
|
627
|
+
* @param {number} groundY - Ground position (positive y value)
|
|
628
|
+
* @param {number} bounceCount - Number of bounces to perform
|
|
629
|
+
* @param {number} elapsedTime - Total elapsed time in seconds
|
|
630
|
+
* @param {number} duration - Duration of animation in seconds
|
|
631
|
+
* @param {boolean} [loop=false] - Whether animation should loop
|
|
632
|
+
* @param {Function} [easingFn=null] - Optional easing function to apply
|
|
633
|
+
* @param {Object} [callbacks] - Optional callback functions
|
|
634
|
+
* @param {Object} [state] - Internal state tracking for callbacks
|
|
635
|
+
* @returns {Object} Animation result with y position and metadata
|
|
636
|
+
*/
|
|
637
|
+
static bounce(
|
|
638
|
+
maxHeight,
|
|
639
|
+
groundY,
|
|
640
|
+
bounceCount,
|
|
641
|
+
elapsedTime,
|
|
642
|
+
duration,
|
|
643
|
+
loop = false,
|
|
644
|
+
easingFn = null,
|
|
645
|
+
callbacks = {},
|
|
646
|
+
state = null
|
|
647
|
+
) {
|
|
648
|
+
return bounceV1(
|
|
649
|
+
maxHeight,
|
|
650
|
+
groundY,
|
|
651
|
+
bounceCount,
|
|
652
|
+
elapsedTime,
|
|
653
|
+
duration,
|
|
654
|
+
loop,
|
|
655
|
+
easingFn,
|
|
656
|
+
callbacks,
|
|
657
|
+
state
|
|
658
|
+
);
|
|
659
|
+
}
|
|
660
|
+
|
|
661
|
+
/**
|
|
662
|
+
* Shake animation with decreasing intensity
|
|
663
|
+
*
|
|
664
|
+
* @param {number} centerX - Center X position
|
|
665
|
+
* @param {number} centerY - Center Y position
|
|
666
|
+
* @param {number} maxOffsetX - Maximum X offset
|
|
667
|
+
* @param {number} maxOffsetY - Maximum Y offset
|
|
668
|
+
* @param {number} frequency - Frequency of shakes
|
|
669
|
+
* @param {number} decay - How quickly the shake decreases (0-1)
|
|
670
|
+
* @param {number} elapsedTime - Total elapsed time in seconds
|
|
671
|
+
* @param {number} duration - Duration of animation in seconds
|
|
672
|
+
* @param {boolean} [loop=false] - Whether animation should loop
|
|
673
|
+
* @param {Function} [easingFn=null] - Optional easing function to apply
|
|
674
|
+
* @param {Object} [callbacks] - Optional callback functions
|
|
675
|
+
* @param {Object} [state] - Internal state tracking for callbacks
|
|
676
|
+
* @returns {Object} Animation result with x, y coordinates and metadata
|
|
677
|
+
*/
|
|
678
|
+
static shake(
|
|
679
|
+
centerX,
|
|
680
|
+
centerY,
|
|
681
|
+
maxOffsetX,
|
|
682
|
+
maxOffsetY,
|
|
683
|
+
frequency,
|
|
684
|
+
decay,
|
|
685
|
+
elapsedTime,
|
|
686
|
+
duration,
|
|
687
|
+
loop = false,
|
|
688
|
+
easingFn = null,
|
|
689
|
+
callbacks = {},
|
|
690
|
+
state = null
|
|
691
|
+
) {
|
|
692
|
+
return shakeV1(
|
|
693
|
+
centerX,
|
|
694
|
+
centerY,
|
|
695
|
+
maxOffsetX,
|
|
696
|
+
maxOffsetY,
|
|
697
|
+
frequency,
|
|
698
|
+
decay,
|
|
699
|
+
elapsedTime,
|
|
700
|
+
duration,
|
|
701
|
+
loop,
|
|
702
|
+
easingFn,
|
|
703
|
+
callbacks,
|
|
704
|
+
state
|
|
705
|
+
);
|
|
706
|
+
}
|
|
707
|
+
|
|
708
|
+
static follow(
|
|
709
|
+
points,
|
|
710
|
+
closed = false,
|
|
711
|
+
elapsedTime,
|
|
712
|
+
duration,
|
|
713
|
+
loop = false,
|
|
714
|
+
easingFn = null,
|
|
715
|
+
callbacks = {},
|
|
716
|
+
state = null
|
|
717
|
+
) {
|
|
718
|
+
return followPath(
|
|
719
|
+
points,
|
|
720
|
+
closed,
|
|
721
|
+
elapsedTime,
|
|
722
|
+
duration,
|
|
723
|
+
loop,
|
|
724
|
+
easingFn,
|
|
725
|
+
callbacks,
|
|
726
|
+
state
|
|
727
|
+
);
|
|
728
|
+
}
|
|
729
|
+
|
|
730
|
+
/**
|
|
731
|
+
* Waypoint is a patrol animation that follows a path of waypoints with proper waiting periods
|
|
732
|
+
* Moves characters along cardinal directions (horizontal and vertical movement)
|
|
733
|
+
*
|
|
734
|
+
* @param {Object} target - Object with x,y properties (not used for position calculation)
|
|
735
|
+
* @param {number} elapsedTime - Total elapsed time in seconds
|
|
736
|
+
* @param {Array<Array<number>>} waypoints - Array of waypoints [[x1,y1], [x2,y2], ...]
|
|
737
|
+
* @param {number} speed - Movement speed in units per second
|
|
738
|
+
* @param {number} waitTime - Time to wait at each waypoint in seconds
|
|
739
|
+
* @param {boolean} [loop=true] - Whether patrol should loop back to start
|
|
740
|
+
* @param {Object} [callbacks] - Optional callback functions
|
|
741
|
+
* @param {Function} [callbacks.onWaypointReached] - Called when reaching a waypoint
|
|
742
|
+
* @param {Function} [callbacks.onWaitStart] - Called when starting to wait at a waypoint
|
|
743
|
+
* @param {Function} [callbacks.onWaitEnd] - Called when done waiting at a waypoint
|
|
744
|
+
* @param {Function} [callbacks.onPatrolComplete] - Called when patrol is complete (non-looping only)
|
|
745
|
+
* @param {Object} [state] - Internal state tracking
|
|
746
|
+
* @returns {Object} Animation result with position and patrol metadata
|
|
747
|
+
*/
|
|
748
|
+
static waypoint(
|
|
749
|
+
target,
|
|
750
|
+
elapsedTime,
|
|
751
|
+
waypoints,
|
|
752
|
+
speed,
|
|
753
|
+
waitTime,
|
|
754
|
+
loop = true,
|
|
755
|
+
callbacks = {},
|
|
756
|
+
state = null
|
|
757
|
+
) {
|
|
758
|
+
return waypointV1(
|
|
759
|
+
target,
|
|
760
|
+
elapsedTime,
|
|
761
|
+
waypoints,
|
|
762
|
+
speed,
|
|
763
|
+
waitTime,
|
|
764
|
+
loop,
|
|
765
|
+
callbacks,
|
|
766
|
+
state
|
|
767
|
+
);
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
/**
|
|
771
|
+
* Simple patrol animation that moves randomly within a radius
|
|
772
|
+
* Character moves along cardinal directions with waiting periods
|
|
773
|
+
*
|
|
774
|
+
* @param {number} initialX - Initial X position (center point)
|
|
775
|
+
* @param {number} initialY - Initial Y position (center point)
|
|
776
|
+
* @param {number} elapsedTime - Total elapsed time in seconds
|
|
777
|
+
* @param {number} moveTime - Time to spend moving between points
|
|
778
|
+
* @param {number} waitTime - Time to wait at each point
|
|
779
|
+
* @param {number} radius - Maximum distance from center point
|
|
780
|
+
* @param {boolean} [loop=true] - Whether animation should loop
|
|
781
|
+
* @param {Object} [state] - Internal state tracking
|
|
782
|
+
* @returns {Object} Animation result with position and direction
|
|
783
|
+
*/
|
|
784
|
+
static patrol(
|
|
785
|
+
initialX,
|
|
786
|
+
initialY,
|
|
787
|
+
elapsedTime,
|
|
788
|
+
moveTime,
|
|
789
|
+
waitTime,
|
|
790
|
+
radius,
|
|
791
|
+
loop = true,
|
|
792
|
+
state = null
|
|
793
|
+
) {
|
|
794
|
+
return patrolV1(
|
|
795
|
+
initialX,
|
|
796
|
+
initialY,
|
|
797
|
+
elapsedTime,
|
|
798
|
+
moveTime,
|
|
799
|
+
waitTime,
|
|
800
|
+
radius,
|
|
801
|
+
loop,
|
|
802
|
+
state
|
|
803
|
+
);
|
|
804
|
+
}
|
|
805
|
+
|
|
806
|
+
/**
|
|
807
|
+
* Hop animation - makes the object jump up and down rhythmically
|
|
808
|
+
*
|
|
809
|
+
* @param {number} baseY - The ground/base Y position
|
|
810
|
+
* @param {number} hopHeight - Maximum height (negative Y offset)
|
|
811
|
+
* @param {number} elapsedTime - Elapsed time in seconds
|
|
812
|
+
* @param {number} duration - Duration of one hop (up and down)
|
|
813
|
+
* @param {boolean} [loop=true] - Whether the hop repeats
|
|
814
|
+
* @param {Function} [easingFn=null] - Optional easing for jump arc
|
|
815
|
+
* @param {Object} [callbacks={}] - Optional callback functions
|
|
816
|
+
* @param {Object} [state=null] - Internal state
|
|
817
|
+
* @returns {Object} Animation result with y position
|
|
818
|
+
*/
|
|
819
|
+
static hop(
|
|
820
|
+
baseY,
|
|
821
|
+
hopHeight,
|
|
822
|
+
elapsedTime,
|
|
823
|
+
duration,
|
|
824
|
+
loop = true,
|
|
825
|
+
yoyo = true,
|
|
826
|
+
easingFn = null,
|
|
827
|
+
callbacks = {},
|
|
828
|
+
state = null
|
|
829
|
+
) {
|
|
830
|
+
return hopV1(
|
|
831
|
+
baseY,
|
|
832
|
+
hopHeight,
|
|
833
|
+
elapsedTime,
|
|
834
|
+
duration,
|
|
835
|
+
loop,
|
|
836
|
+
yoyo,
|
|
837
|
+
easingFn,
|
|
838
|
+
callbacks,
|
|
839
|
+
state
|
|
840
|
+
);
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
/**
|
|
844
|
+
* !!!!
|
|
845
|
+
* ANIMATION GROUPING
|
|
846
|
+
* VERY MUCH EXPERIMENTAL AT THIS POINT
|
|
847
|
+
* !!!!
|
|
848
|
+
*/
|
|
849
|
+
|
|
850
|
+
/**
|
|
851
|
+
* Group multiple animations together
|
|
852
|
+
*
|
|
853
|
+
* @param {Array<Function>} animations - Array of animation function references
|
|
854
|
+
* @param {Array<Array>} animationArgs - Array of argument arrays for each animation
|
|
855
|
+
* @param {number} elapsedTime - Total elapsed time in seconds
|
|
856
|
+
* @param {number} duration - Duration of animation in seconds
|
|
857
|
+
* @param {boolean} [loop=false] - Whether animation should loop
|
|
858
|
+
* @param {Function} [easingFn=null] - Optional easing function to apply to all animations
|
|
859
|
+
* @param {Object} [callbacks] - Optional callback functions
|
|
860
|
+
* @param {Object} [state] - Internal state tracking for callbacks and animations
|
|
861
|
+
* @returns {Object} Combined animation result with results from all animations
|
|
862
|
+
*/
|
|
863
|
+
static group(
|
|
864
|
+
animations,
|
|
865
|
+
animationArgs,
|
|
866
|
+
elapsedTime,
|
|
867
|
+
duration,
|
|
868
|
+
loop = false,
|
|
869
|
+
easingFn = null,
|
|
870
|
+
callbacks = {},
|
|
871
|
+
state = null
|
|
872
|
+
) {
|
|
873
|
+
// Initialize state with animation states array if not provided
|
|
874
|
+
if (!state) {
|
|
875
|
+
state = {
|
|
876
|
+
started: false,
|
|
877
|
+
loopCount: 0,
|
|
878
|
+
animationStates: Array(animations.length).fill(null),
|
|
879
|
+
};
|
|
880
|
+
}
|
|
881
|
+
|
|
882
|
+
// Update animation time and apply easing
|
|
883
|
+
const {
|
|
884
|
+
t,
|
|
885
|
+
easedT,
|
|
886
|
+
completed,
|
|
887
|
+
state: newState,
|
|
888
|
+
} = this._frame(elapsedTime, duration, loop, easingFn, callbacks, state);
|
|
889
|
+
|
|
890
|
+
// Run all animations with same timing
|
|
891
|
+
const results = {};
|
|
892
|
+
|
|
893
|
+
for (let i = 0; i < animations.length; i++) {
|
|
894
|
+
const animFn = animations[i];
|
|
895
|
+
const args = [...animationArgs[i]];
|
|
896
|
+
|
|
897
|
+
// Replace timing arguments with our timing
|
|
898
|
+
// Find time/duration/loop arguments based on the function signature
|
|
899
|
+
if (
|
|
900
|
+
animFn === this.parabolic ||
|
|
901
|
+
animFn === this.oscillate ||
|
|
902
|
+
animFn === this.pulse
|
|
903
|
+
) {
|
|
904
|
+
// Format: (params..., elapsedTime, duration, loop, ...)
|
|
905
|
+
args[3] = elapsedTime;
|
|
906
|
+
args[4] = duration;
|
|
907
|
+
args[5] = loop;
|
|
908
|
+
if (args[6] === undefined) args[6] = easingFn;
|
|
909
|
+
} else if (animFn === this.spring) {
|
|
910
|
+
// Format: (current, target, elapsedTime, duration, loop, ...)
|
|
911
|
+
args[2] = elapsedTime;
|
|
912
|
+
args[3] = duration;
|
|
913
|
+
args[4] = loop;
|
|
914
|
+
} else if (animFn === this.spiral || animFn === this.bezier) {
|
|
915
|
+
// Format: (params..., elapsedTime, duration, loop, ...)
|
|
916
|
+
args[6] = elapsedTime;
|
|
917
|
+
args[7] = duration;
|
|
918
|
+
args[8] = loop;
|
|
919
|
+
if (args[9] === undefined) args[9] = easingFn;
|
|
920
|
+
} else if (animFn === this.orbit) {
|
|
921
|
+
// Format: (params..., elapsedTime, duration, loop, clockwise, ...)
|
|
922
|
+
args[5] = elapsedTime;
|
|
923
|
+
args[6] = duration;
|
|
924
|
+
args[7] = loop;
|
|
925
|
+
// Skip args[8] as it's clockwise
|
|
926
|
+
if (args[9] === undefined) args[9] = easingFn;
|
|
927
|
+
} else if (animFn === this.bounce || animFn === this.shake) {
|
|
928
|
+
// Format: (params..., elapsedTime, duration, loop, ...)
|
|
929
|
+
args[6] = elapsedTime;
|
|
930
|
+
args[7] = duration;
|
|
931
|
+
args[8] = loop;
|
|
932
|
+
if (args[9] === undefined) args[9] = easingFn;
|
|
933
|
+
} else if (animFn === this.followPath) {
|
|
934
|
+
// Format: (points, closed, elapsedTime, duration, loop, ...)
|
|
935
|
+
args[2] = elapsedTime;
|
|
936
|
+
args[3] = duration;
|
|
937
|
+
args[4] = loop;
|
|
938
|
+
if (args[5] === undefined) args[5] = easingFn;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
// Add state to arguments
|
|
942
|
+
args.push(callbacks);
|
|
943
|
+
args.push(newState.animationStates[i]);
|
|
944
|
+
|
|
945
|
+
// Run animation
|
|
946
|
+
const result = animFn.apply(this, args);
|
|
947
|
+
|
|
948
|
+
// Store updated state
|
|
949
|
+
newState.animationStates[i] = result.state;
|
|
950
|
+
|
|
951
|
+
// Add result to results object with key based on index
|
|
952
|
+
const key = `anim${i}`;
|
|
953
|
+
results[key] = result;
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
// Return combined result
|
|
957
|
+
return this.animationResult(results, t, loop, completed, newState);
|
|
958
|
+
}
|
|
959
|
+
|
|
960
|
+
/**
|
|
961
|
+
* Sequence multiple animations one after another
|
|
962
|
+
*
|
|
963
|
+
* @param {Array<Function>} animations - Array of animation function references
|
|
964
|
+
* @param {Array<Array>} animationArgs - Array of argument arrays for each animation
|
|
965
|
+
* @param {Array<number>} durations - Array of durations for each animation
|
|
966
|
+
* @param {number} elapsedTime - Total elapsed time in seconds
|
|
967
|
+
* @param {boolean} [loop=false] - Whether the entire sequence should loop
|
|
968
|
+
* @param {Array<Function>} [easingFns=null] - Optional array of easing functions for each animation
|
|
969
|
+
* @param {Object} [callbacks] - Optional callback functions for the entire sequence
|
|
970
|
+
* @param {Object} [animCallbacks] - Optional array of callback objects for individual animations
|
|
971
|
+
* @param {Object} [state] - Internal state tracking
|
|
972
|
+
* @returns {Object} Animation result from current active animation with sequence metadata
|
|
973
|
+
*/
|
|
974
|
+
static sequence(
|
|
975
|
+
animations,
|
|
976
|
+
animationArgs,
|
|
977
|
+
durations,
|
|
978
|
+
elapsedTime,
|
|
979
|
+
loop = false,
|
|
980
|
+
easingFns = null,
|
|
981
|
+
callbacks = {},
|
|
982
|
+
animCallbacks = null,
|
|
983
|
+
state = null
|
|
984
|
+
) {
|
|
985
|
+
// Initialize state with animation states array if not provided
|
|
986
|
+
if (!state) {
|
|
987
|
+
state = {
|
|
988
|
+
started: false,
|
|
989
|
+
loopCount: 0,
|
|
990
|
+
animationStates: Array(animations.length).fill(null),
|
|
991
|
+
currentAnim: 0,
|
|
992
|
+
animStartTimes: [0],
|
|
993
|
+
totalDuration: 0,
|
|
994
|
+
};
|
|
995
|
+
|
|
996
|
+
// Calculate start times and total duration
|
|
997
|
+
let timeAccumulator = 0;
|
|
998
|
+
for (let i = 0; i < durations.length; i++) {
|
|
999
|
+
timeAccumulator += durations[i];
|
|
1000
|
+
if (i < durations.length - 1) {
|
|
1001
|
+
state.animStartTimes.push(timeAccumulator);
|
|
1002
|
+
}
|
|
1003
|
+
}
|
|
1004
|
+
state.totalDuration = timeAccumulator;
|
|
1005
|
+
}
|
|
1006
|
+
|
|
1007
|
+
// Handle loop logic for the entire sequence
|
|
1008
|
+
let localElapsedTime = elapsedTime;
|
|
1009
|
+
if (loop && state.totalDuration > 0) {
|
|
1010
|
+
localElapsedTime = elapsedTime % state.totalDuration;
|
|
1011
|
+
|
|
1012
|
+
// Track loop count for callback
|
|
1013
|
+
const newLoopCount = Math.floor(elapsedTime / state.totalDuration);
|
|
1014
|
+
if (newLoopCount > state.loopCount && callbacks.onLoop) {
|
|
1015
|
+
callbacks.onLoop(newLoopCount);
|
|
1016
|
+
state.loopCount = newLoopCount;
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
|
|
1020
|
+
// Call onStart callback once
|
|
1021
|
+
if (!state.started && callbacks.onStart) {
|
|
1022
|
+
callbacks.onStart();
|
|
1023
|
+
state.started = true;
|
|
1024
|
+
}
|
|
1025
|
+
|
|
1026
|
+
// Find current animation based on elapsed time
|
|
1027
|
+
let currentAnim = 0;
|
|
1028
|
+
for (let i = animations.length - 1; i >= 0; i--) {
|
|
1029
|
+
if (localElapsedTime >= state.animStartTimes[i]) {
|
|
1030
|
+
currentAnim = i;
|
|
1031
|
+
break;
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
|
|
1035
|
+
// Update current animation property
|
|
1036
|
+
state.currentAnim = currentAnim;
|
|
1037
|
+
|
|
1038
|
+
// Calculate time within current animation
|
|
1039
|
+
const animStartTime = state.animStartTimes[currentAnim];
|
|
1040
|
+
const animElapsedTime = localElapsedTime - animStartTime;
|
|
1041
|
+
const animDuration = durations[currentAnim];
|
|
1042
|
+
|
|
1043
|
+
// Get animation function and arguments
|
|
1044
|
+
const animFn = animations[currentAnim];
|
|
1045
|
+
const args = [...animationArgs[currentAnim]];
|
|
1046
|
+
|
|
1047
|
+
// Apply proper timing to animation args based on function type
|
|
1048
|
+
// This follows the same pattern as in group()
|
|
1049
|
+
if (
|
|
1050
|
+
animFn === this.parabolic ||
|
|
1051
|
+
animFn === this.oscillate ||
|
|
1052
|
+
animFn === this.pulse
|
|
1053
|
+
) {
|
|
1054
|
+
args[3] = animElapsedTime;
|
|
1055
|
+
args[4] = animDuration;
|
|
1056
|
+
args[5] = false; // Never loop individual animations
|
|
1057
|
+
if (easingFns && easingFns[currentAnim]) args[6] = easingFns[currentAnim];
|
|
1058
|
+
} else if (animFn === this.spring) {
|
|
1059
|
+
args[2] = animElapsedTime;
|
|
1060
|
+
args[3] = animDuration;
|
|
1061
|
+
args[4] = false;
|
|
1062
|
+
} else if (animFn === this.spiral || animFn === this.bezier) {
|
|
1063
|
+
args[6] = animElapsedTime;
|
|
1064
|
+
args[7] = animDuration;
|
|
1065
|
+
args[8] = false;
|
|
1066
|
+
if (easingFns && easingFns[currentAnim]) args[9] = easingFns[currentAnim];
|
|
1067
|
+
} else if (animFn === this.orbit) {
|
|
1068
|
+
args[5] = animElapsedTime;
|
|
1069
|
+
args[6] = animDuration;
|
|
1070
|
+
args[7] = false;
|
|
1071
|
+
// Skip args[8] as it's clockwise
|
|
1072
|
+
if (easingFns && easingFns[currentAnim]) args[9] = easingFns[currentAnim];
|
|
1073
|
+
} else if (animFn === this.bounce || animFn === this.shake) {
|
|
1074
|
+
args[6] = animElapsedTime;
|
|
1075
|
+
args[7] = animDuration;
|
|
1076
|
+
args[8] = false;
|
|
1077
|
+
if (easingFns && easingFns[currentAnim]) args[9] = easingFns[currentAnim];
|
|
1078
|
+
} else if (animFn === this.followPath) {
|
|
1079
|
+
args[2] = animElapsedTime;
|
|
1080
|
+
args[3] = animDuration;
|
|
1081
|
+
args[4] = false;
|
|
1082
|
+
if (easingFns && easingFns[currentAnim]) args[5] = easingFns[currentAnim];
|
|
1083
|
+
}
|
|
1084
|
+
|
|
1085
|
+
// Add individual animation callbacks if provided
|
|
1086
|
+
const currentAnimCallbacks =
|
|
1087
|
+
animCallbacks && animCallbacks[currentAnim]
|
|
1088
|
+
? animCallbacks[currentAnim]
|
|
1089
|
+
: {};
|
|
1090
|
+
|
|
1091
|
+
// Run animation with its state
|
|
1092
|
+
const result = animFn.apply(this, [
|
|
1093
|
+
...args,
|
|
1094
|
+
currentAnimCallbacks,
|
|
1095
|
+
state.animationStates[currentAnim],
|
|
1096
|
+
]);
|
|
1097
|
+
|
|
1098
|
+
// Store updated state
|
|
1099
|
+
state.animationStates[currentAnim] = result.state;
|
|
1100
|
+
|
|
1101
|
+
// Check if the entire sequence is completed
|
|
1102
|
+
const completed = !loop && localElapsedTime >= state.totalDuration;
|
|
1103
|
+
|
|
1104
|
+
// Call onComplete callback once when the entire sequence completes
|
|
1105
|
+
if (completed && !state.completed && callbacks.onComplete) {
|
|
1106
|
+
callbacks.onComplete();
|
|
1107
|
+
state.completed = true;
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
// Return enhanced result with sequence metadata
|
|
1111
|
+
return this.animationResult(
|
|
1112
|
+
{
|
|
1113
|
+
...result,
|
|
1114
|
+
currentAnim,
|
|
1115
|
+
totalAnimations: animations.length,
|
|
1116
|
+
sequenceProgress: Math.min(localElapsedTime / state.totalDuration, 1),
|
|
1117
|
+
},
|
|
1118
|
+
localElapsedTime / state.totalDuration, // t normalized to entire sequence
|
|
1119
|
+
loop,
|
|
1120
|
+
completed,
|
|
1121
|
+
state
|
|
1122
|
+
);
|
|
1123
|
+
}
|
|
1124
|
+
}
|