@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,349 @@
|
|
|
1
|
+
export class Easing {
|
|
2
|
+
// =========================================================================
|
|
3
|
+
// EASING FUNCTIONS
|
|
4
|
+
// =========================================================================
|
|
5
|
+
|
|
6
|
+
static lerp(a, b, t) {
|
|
7
|
+
return a + (b - a) * t;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Linear - no easing, straight line
|
|
12
|
+
* @param {number} t - Input (0-1)
|
|
13
|
+
* @returns {number} Same value as input
|
|
14
|
+
*/
|
|
15
|
+
static linear(t) {
|
|
16
|
+
return t;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Smoothstep - classic shader S-curve interpolation
|
|
21
|
+
* Smooth start and end with zero first derivative at endpoints
|
|
22
|
+
* @param {number} t - Input (0-1)
|
|
23
|
+
* @returns {number} Eased value
|
|
24
|
+
*/
|
|
25
|
+
static smoothstep(t) {
|
|
26
|
+
return t * t * (3 - 2 * t);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Smootherstep - Ken Perlin's improved smoothstep
|
|
31
|
+
* Even smoother with zero first AND second derivatives at endpoints
|
|
32
|
+
* @param {number} t - Input (0-1)
|
|
33
|
+
* @returns {number} Eased value
|
|
34
|
+
*/
|
|
35
|
+
static smootherstep(t) {
|
|
36
|
+
return t * t * t * (t * (t * 6 - 15) + 10);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Quadratic ease-in
|
|
41
|
+
* @param {number} t - Input (0-1)
|
|
42
|
+
* @returns {number} Eased value
|
|
43
|
+
*/
|
|
44
|
+
static easeInQuad(t) {
|
|
45
|
+
return t * t;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Quadratic ease-out
|
|
50
|
+
* @param {number} t - Input (0-1)
|
|
51
|
+
* @returns {number} Eased value
|
|
52
|
+
*/
|
|
53
|
+
static easeOutQuad(t) {
|
|
54
|
+
return t * (2 - t);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Quadratic ease-in-out
|
|
59
|
+
* @param {number} t - Input (0-1)
|
|
60
|
+
* @returns {number} Eased value
|
|
61
|
+
*/
|
|
62
|
+
static easeInOutQuad(t) {
|
|
63
|
+
return t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Cubic ease-in
|
|
68
|
+
* @param {number} t - Input (0-1)
|
|
69
|
+
* @returns {number} Eased value
|
|
70
|
+
*/
|
|
71
|
+
static easeInCubic(t) {
|
|
72
|
+
return t * t * t;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Cubic ease-out
|
|
77
|
+
* @param {number} t - Input (0-1)
|
|
78
|
+
* @returns {number} Eased value
|
|
79
|
+
*/
|
|
80
|
+
static easeOutCubic(t) {
|
|
81
|
+
return --t * t * t + 1;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Cubic ease-in-out
|
|
86
|
+
* @param {number} t - Input (0-1)
|
|
87
|
+
* @returns {number} Eased value
|
|
88
|
+
*/
|
|
89
|
+
static easeInOutCubic(t) {
|
|
90
|
+
return t < 0.5 ? 4 * t * t * t : (t - 1) * (2 * t - 2) * (2 * t - 2) + 1;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* Quartic ease-in
|
|
95
|
+
* @param {number} t - Input (0-1)
|
|
96
|
+
* @returns {number} Eased value
|
|
97
|
+
*/
|
|
98
|
+
static easeInQuart(t) {
|
|
99
|
+
return t * t * t * t;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Quartic ease-out
|
|
104
|
+
* @param {number} t - Input (0-1)
|
|
105
|
+
* @returns {number} Eased value
|
|
106
|
+
*/
|
|
107
|
+
static easeOutQuart(t) {
|
|
108
|
+
return 1 - --t * t * t * t;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Quartic ease-in-out
|
|
113
|
+
* @param {number} t - Input (0-1)
|
|
114
|
+
* @returns {number} Eased value
|
|
115
|
+
*/
|
|
116
|
+
static easeInOutQuart(t) {
|
|
117
|
+
return t < 0.5 ? 8 * t * t * t * t : 1 - 8 * --t * t * t * t;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Sine ease-in
|
|
122
|
+
* @param {number} t - Input (0-1)
|
|
123
|
+
* @returns {number} Eased value
|
|
124
|
+
*/
|
|
125
|
+
static easeInSine(t) {
|
|
126
|
+
return 1 - Math.cos((t * Math.PI) / 2);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Sine ease-out
|
|
131
|
+
* @param {number} t - Input (0-1)
|
|
132
|
+
* @returns {number} Eased value
|
|
133
|
+
*/
|
|
134
|
+
static easeOutSine(t) {
|
|
135
|
+
return Math.sin((t * Math.PI) / 2);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Sine ease-in-out
|
|
140
|
+
* @param {number} t - Input (0-1)
|
|
141
|
+
* @returns {number} Eased value
|
|
142
|
+
*/
|
|
143
|
+
static easeInOutSine(t) {
|
|
144
|
+
return -(Math.cos(Math.PI * t) - 1) / 2;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Exponential ease-in
|
|
149
|
+
* @param {number} t - Input (0-1)
|
|
150
|
+
* @returns {number} Eased value
|
|
151
|
+
*/
|
|
152
|
+
static easeInExpo(t) {
|
|
153
|
+
return t === 0 ? 0 : Math.pow(2, 10 * (t - 1));
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/**
|
|
157
|
+
* Exponential ease-out
|
|
158
|
+
* @param {number} t - Input (0-1)
|
|
159
|
+
* @returns {number} Eased value
|
|
160
|
+
*/
|
|
161
|
+
static easeOutExpo(t) {
|
|
162
|
+
return t === 1 ? 1 : 1 - Math.pow(2, -10 * t);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Exponential ease-in-out
|
|
167
|
+
* @param {number} t - Input (0-1)
|
|
168
|
+
* @returns {number} Eased value
|
|
169
|
+
*/
|
|
170
|
+
static easeInOutExpo(t) {
|
|
171
|
+
if (t === 0 || t === 1) return t;
|
|
172
|
+
if (t < 0.5) {
|
|
173
|
+
return 0.5 * Math.pow(2, 20 * t - 10);
|
|
174
|
+
} else {
|
|
175
|
+
return 0.5 * (2 - Math.pow(2, -20 * t + 10));
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Circular ease-in
|
|
181
|
+
* @param {number} t - Input (0-1)
|
|
182
|
+
* @returns {number} Eased value
|
|
183
|
+
*/
|
|
184
|
+
static easeInCirc(t) {
|
|
185
|
+
return 1 - Math.sqrt(1 - t * t);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Circular ease-out
|
|
190
|
+
* @param {number} t - Input (0-1)
|
|
191
|
+
* @returns {number} Eased value
|
|
192
|
+
*/
|
|
193
|
+
static easeOutCirc(t) {
|
|
194
|
+
return Math.sqrt(1 - --t * t);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
/**
|
|
198
|
+
* Circular ease-in-out
|
|
199
|
+
* @param {number} t - Input (0-1)
|
|
200
|
+
* @returns {number} Eased value
|
|
201
|
+
*/
|
|
202
|
+
static easeInOutCirc(t) {
|
|
203
|
+
return t < 0.5
|
|
204
|
+
? 0.5 * (1 - Math.sqrt(1 - 4 * t * t))
|
|
205
|
+
: 0.5 * (Math.sqrt(-(2 * t - 3) * (2 * t - 1)) + 1);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Elastic ease-in
|
|
210
|
+
* @param {number} t - Input (0-1)
|
|
211
|
+
* @param {number} [amplitude=1] - Amplitude
|
|
212
|
+
* @param {number} [period=0.3] - Period
|
|
213
|
+
* @returns {number} Eased value
|
|
214
|
+
*/
|
|
215
|
+
static easeInElastic(t, amplitude = 1, period = 0.3) {
|
|
216
|
+
if (t === 0 || t === 1) return t;
|
|
217
|
+
|
|
218
|
+
const s = (period / (2 * Math.PI)) * Math.asin(1 / amplitude);
|
|
219
|
+
return -(
|
|
220
|
+
amplitude *
|
|
221
|
+
Math.pow(2, 10 * (t - 1)) *
|
|
222
|
+
Math.sin(((t - 1 - s) * (2 * Math.PI)) / period)
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Elastic ease-out
|
|
228
|
+
* @param {number} t - Input (0-1)
|
|
229
|
+
* @param {number} [amplitude=1] - Amplitude
|
|
230
|
+
* @param {number} [period=0.3] - Period
|
|
231
|
+
* @returns {number} Eased value
|
|
232
|
+
*/
|
|
233
|
+
static easeOutElastic(t, amplitude = 1, period = 0.3) {
|
|
234
|
+
if (t === 0 || t === 1) return t;
|
|
235
|
+
|
|
236
|
+
const s = (period / (2 * Math.PI)) * Math.asin(1 / amplitude);
|
|
237
|
+
return (
|
|
238
|
+
amplitude *
|
|
239
|
+
Math.pow(2, -10 * t) *
|
|
240
|
+
Math.sin(((t - s) * (2 * Math.PI)) / period) +
|
|
241
|
+
1
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
/**
|
|
246
|
+
* Elastic ease-in-out
|
|
247
|
+
* @param {number} t - Input (0-1)
|
|
248
|
+
* @param {number} [amplitude=1] - Amplitude
|
|
249
|
+
* @param {number} [period=0.3] - Period
|
|
250
|
+
* @returns {number} Eased value
|
|
251
|
+
*/
|
|
252
|
+
static easeInOutElastic(t, amplitude = 1, period = 0.3) {
|
|
253
|
+
if (t === 0 || t === 1) return t;
|
|
254
|
+
|
|
255
|
+
const s = (period / (2 * Math.PI)) * Math.asin(1 / amplitude);
|
|
256
|
+
|
|
257
|
+
if (t < 0.5) {
|
|
258
|
+
return (
|
|
259
|
+
-0.5 *
|
|
260
|
+
(amplitude *
|
|
261
|
+
Math.pow(2, 10 * (2 * t - 1)) *
|
|
262
|
+
Math.sin(((2 * t - 1 - s) * (2 * Math.PI)) / period))
|
|
263
|
+
);
|
|
264
|
+
} else {
|
|
265
|
+
return (
|
|
266
|
+
amplitude *
|
|
267
|
+
Math.pow(2, -10 * (2 * t - 1)) *
|
|
268
|
+
Math.sin(((2 * t - 1 - s) * (2 * Math.PI)) / period) *
|
|
269
|
+
0.5 +
|
|
270
|
+
1
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* Back ease-in
|
|
277
|
+
* @param {number} t - Input (0-1)
|
|
278
|
+
* @param {number} [overshoot=1.70158] - Overshoot amount
|
|
279
|
+
* @returns {number} Eased value
|
|
280
|
+
*/
|
|
281
|
+
static easeInBack(t, overshoot = 1.70158) {
|
|
282
|
+
return t * t * ((overshoot + 1) * t - overshoot);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Back ease-out
|
|
287
|
+
* @param {number} t - Input (0-1)
|
|
288
|
+
* @param {number} [overshoot=1.70158] - Overshoot amount
|
|
289
|
+
* @returns {number} Eased value
|
|
290
|
+
*/
|
|
291
|
+
static easeOutBack(t, overshoot = 1.70158) {
|
|
292
|
+
return --t * t * ((overshoot + 1) * t + overshoot) + 1;
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Back ease-in-out
|
|
297
|
+
* @param {number} t - Input (0-1)
|
|
298
|
+
* @param {number} [overshoot=1.70158] - Overshoot amount
|
|
299
|
+
* @returns {number} Eased value
|
|
300
|
+
*/
|
|
301
|
+
static easeInOutBack(t, overshoot = 1.70158) {
|
|
302
|
+
const s = overshoot * 1.525;
|
|
303
|
+
|
|
304
|
+
if (t < 0.5) {
|
|
305
|
+
return 0.5 * (2 * t) * (2 * t) * ((s + 1) * 2 * t - s);
|
|
306
|
+
} else {
|
|
307
|
+
return (
|
|
308
|
+
0.5 * ((2 * t - 2) * (2 * t - 2) * ((s + 1) * (2 * t - 2) + s) + 2)
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
/**
|
|
314
|
+
* Bounce ease-out
|
|
315
|
+
* @param {number} t - Input (0-1)
|
|
316
|
+
* @returns {number} Eased value
|
|
317
|
+
*/
|
|
318
|
+
static easeOutBounce(t) {
|
|
319
|
+
if (t < 1 / 2.75) {
|
|
320
|
+
return 7.5625 * t * t;
|
|
321
|
+
} else if (t < 2 / 2.75) {
|
|
322
|
+
return 7.5625 * (t -= 1.5 / 2.75) * t + 0.75;
|
|
323
|
+
} else if (t < 2.5 / 2.75) {
|
|
324
|
+
return 7.5625 * (t -= 2.25 / 2.75) * t + 0.9375;
|
|
325
|
+
} else {
|
|
326
|
+
return 7.5625 * (t -= 2.625 / 2.75) * t + 0.984375;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Bounce ease-in
|
|
332
|
+
* @param {number} t - Input (0-1)
|
|
333
|
+
* @returns {number} Eased value
|
|
334
|
+
*/
|
|
335
|
+
static easeInBounce(t) {
|
|
336
|
+
return 1 - Easing.easeOutBounce(1 - t);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Bounce ease-in-out
|
|
341
|
+
* @param {number} t - Input (0-1)
|
|
342
|
+
* @returns {number} Eased value
|
|
343
|
+
*/
|
|
344
|
+
static easeInOutBounce(t) {
|
|
345
|
+
return t < 0.5
|
|
346
|
+
? Easing.easeInBounce(t * 2) * 0.5
|
|
347
|
+
: Easing.easeOutBounce(t * 2 - 1) * 0.5 + 0.5;
|
|
348
|
+
}
|
|
349
|
+
}
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { Motion } from "./motion";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Patrol animation - creates movement around an area with natural pausing
|
|
5
|
+
* Perfect for characters patrolling or objects drifting within bounds
|
|
6
|
+
*
|
|
7
|
+
* @param {Object} target - Object with x,y properties defining the center point
|
|
8
|
+
* @param {number} elapsedTime - Total elapsed time in seconds
|
|
9
|
+
* @param {number} duration - Duration of one full float cycle in seconds
|
|
10
|
+
* @param {number} speed - Movement speed multiplier (0.1-2.0 recommended)
|
|
11
|
+
* @param {number} randomness - How random/unpredictable the float path is (0-1)
|
|
12
|
+
* @param {number} radius - Radius of float area (object will move within -radius to +radius)
|
|
13
|
+
* @param {boolean} [loop=true] - Whether animation should loop
|
|
14
|
+
* @param {Function} [easingFn=null] - Optional easing function to apply
|
|
15
|
+
* @param {Object} [callbacks] - Optional callback functions
|
|
16
|
+
* @param {Object} [state] - Internal state tracking for callbacks and initial position
|
|
17
|
+
* @returns {Object} Animation result with x, y coordinates and metadata
|
|
18
|
+
*/
|
|
19
|
+
export function floatV1(
|
|
20
|
+
target,
|
|
21
|
+
elapsedTime,
|
|
22
|
+
duration,
|
|
23
|
+
speed,
|
|
24
|
+
randomness,
|
|
25
|
+
radius,
|
|
26
|
+
loop = true,
|
|
27
|
+
easingFn = null,
|
|
28
|
+
callbacks = {},
|
|
29
|
+
state = null
|
|
30
|
+
) {
|
|
31
|
+
// Early return for zero duration
|
|
32
|
+
if (duration <= 0) {
|
|
33
|
+
return Motion.animationResult(
|
|
34
|
+
{ x: target.x, y: target.y, moving: false },
|
|
35
|
+
1,
|
|
36
|
+
false,
|
|
37
|
+
true
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
// Initialize state if not provided, capturing initial position
|
|
41
|
+
if (!state) {
|
|
42
|
+
state = {
|
|
43
|
+
initialX: target.x,
|
|
44
|
+
initialY: target.y,
|
|
45
|
+
started: false,
|
|
46
|
+
completed: false,
|
|
47
|
+
loopCount: 0,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
// Use initialX and initialY from state as the center point
|
|
51
|
+
const centerX = state.initialX;
|
|
52
|
+
const centerY = state.initialY;
|
|
53
|
+
// Update animation time and apply easing if needed
|
|
54
|
+
const {
|
|
55
|
+
t,
|
|
56
|
+
easedT,
|
|
57
|
+
completed,
|
|
58
|
+
state: timeState,
|
|
59
|
+
} = Motion._frame(elapsedTime, duration, loop, easingFn, callbacks, state);
|
|
60
|
+
// Update state with time tracking info
|
|
61
|
+
state = {
|
|
62
|
+
...state,
|
|
63
|
+
...timeState,
|
|
64
|
+
};
|
|
65
|
+
// Scale time by speed (higher speed = faster oscillations)
|
|
66
|
+
const scaledTime = elapsedTime * speed;
|
|
67
|
+
// Clamp randomness to valid range (0-1)
|
|
68
|
+
const clampedRandomness = Math.max(0, Math.min(1, randomness));
|
|
69
|
+
// Multi-layered sinusoidal movements with different frequencies
|
|
70
|
+
// to create patrol-like motion with natural pauses
|
|
71
|
+
|
|
72
|
+
// Primary motion (large, slow)
|
|
73
|
+
const baseFreqX = 0.7;
|
|
74
|
+
const baseFreqY = 0.9; // Slightly different to avoid perfect circles
|
|
75
|
+
// Secondary motion (direction changes)
|
|
76
|
+
const secondFreqX = 2.3;
|
|
77
|
+
const secondFreqY = 1.9;
|
|
78
|
+
// Calculate combined motion patterns
|
|
79
|
+
const dx =
|
|
80
|
+
Math.sin(scaledTime * baseFreqX) +
|
|
81
|
+
clampedRandomness * 0.4 * Math.sin(scaledTime * secondFreqX + 0.5);
|
|
82
|
+
const dy =
|
|
83
|
+
Math.cos(scaledTime * baseFreqY) +
|
|
84
|
+
clampedRandomness * 0.4 * Math.cos(scaledTime * secondFreqY + 0.7);
|
|
85
|
+
// Scale by radius - the multiplier is 0.5 because we want values to range from -radius to +radius
|
|
86
|
+
// This properly centers the patrol area around the initial position
|
|
87
|
+
const x = centerX + dx * radius;
|
|
88
|
+
const y = centerY + dy * radius;
|
|
89
|
+
//this.logger.log(x, y, centerX, centerY);
|
|
90
|
+
// Calculate if currently moving or paused
|
|
91
|
+
// Object "pauses" when velocity is low (near turning points of sine waves)
|
|
92
|
+
const dxdt =
|
|
93
|
+
baseFreqX * Math.cos(scaledTime * baseFreqX) +
|
|
94
|
+
clampedRandomness *
|
|
95
|
+
0.4 *
|
|
96
|
+
secondFreqX *
|
|
97
|
+
Math.cos(scaledTime * secondFreqX + 0.5);
|
|
98
|
+
const dydt =
|
|
99
|
+
-baseFreqY * Math.sin(scaledTime * baseFreqY) +
|
|
100
|
+
clampedRandomness *
|
|
101
|
+
0.4 *
|
|
102
|
+
-secondFreqY *
|
|
103
|
+
Math.sin(scaledTime * secondFreqY + 0.7);
|
|
104
|
+
// Calculate velocity magnitude
|
|
105
|
+
const velocity = Math.sqrt(dxdt * dxdt + dydt * dydt);
|
|
106
|
+
// Determine if moving or paused (using a threshold)
|
|
107
|
+
const isMoving = velocity > 0.8;
|
|
108
|
+
// Calculate distance from center
|
|
109
|
+
const distanceFromCenter = Math.sqrt(
|
|
110
|
+
(x - centerX) * (x - centerX) + (y - centerY) * (y - centerY)
|
|
111
|
+
);
|
|
112
|
+
// Return standardized result with patrol-specific metadata
|
|
113
|
+
return Motion.animationResult(
|
|
114
|
+
{
|
|
115
|
+
x,
|
|
116
|
+
y,
|
|
117
|
+
centerX,
|
|
118
|
+
centerY,
|
|
119
|
+
offsetX: x - centerX,
|
|
120
|
+
offsetY: y - centerY,
|
|
121
|
+
distance: distanceFromCenter,
|
|
122
|
+
moving: isMoving,
|
|
123
|
+
velocity: velocity,
|
|
124
|
+
},
|
|
125
|
+
t,
|
|
126
|
+
loop,
|
|
127
|
+
completed,
|
|
128
|
+
state
|
|
129
|
+
);
|
|
130
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { Motion } from "./motion";
|
|
2
|
+
import { Tween } from "./tween";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Follow path animation along a series of points
|
|
6
|
+
*
|
|
7
|
+
* @param {Array<Array<number>>} points - Array of points as [x, y] coordinates
|
|
8
|
+
* @param {boolean} [closed=false] - Whether path is closed (connects back to start)
|
|
9
|
+
* @param {number} elapsedTime - Total elapsed time in seconds
|
|
10
|
+
* @param {number} duration - Duration of animation in seconds
|
|
11
|
+
* @param {boolean} [loop=false] - Whether animation should loop
|
|
12
|
+
* @param {Function} [easingFn=null] - Optional easing function to apply
|
|
13
|
+
* @param {Object} [callbacks] - Optional callback functions
|
|
14
|
+
* @param {Object} [state] - Internal state tracking for callbacks
|
|
15
|
+
* @returns {Object} Animation result with x, y coordinates and metadata
|
|
16
|
+
*/
|
|
17
|
+
export function followPath(
|
|
18
|
+
points,
|
|
19
|
+
closed = false,
|
|
20
|
+
elapsedTime,
|
|
21
|
+
duration,
|
|
22
|
+
loop = false,
|
|
23
|
+
easingFn = null,
|
|
24
|
+
callbacks = {},
|
|
25
|
+
state = null
|
|
26
|
+
) {
|
|
27
|
+
// Need at least 2 points
|
|
28
|
+
if (!points || points.length < 2) {
|
|
29
|
+
return this._createResult({ x: 0, y: 0 }, 0, loop, false);
|
|
30
|
+
}
|
|
31
|
+
// Update animation time and apply easing
|
|
32
|
+
const {
|
|
33
|
+
t,
|
|
34
|
+
easedT,
|
|
35
|
+
completed,
|
|
36
|
+
state: newState,
|
|
37
|
+
} = Motion._frame(elapsedTime, duration, loop, easingFn, callbacks, state);
|
|
38
|
+
|
|
39
|
+
// Calculate total path length and segment lengths
|
|
40
|
+
if (!state || !state.pathData) {
|
|
41
|
+
const pathData = {
|
|
42
|
+
segmentLengths: [],
|
|
43
|
+
totalLength: 0,
|
|
44
|
+
points: [...points],
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// Calculate length of each segment
|
|
48
|
+
for (let i = 0; i < points.length - 1; i++) {
|
|
49
|
+
const p1 = points[i];
|
|
50
|
+
const p2 = points[i + 1];
|
|
51
|
+
const dx = p2[0] - p1[0];
|
|
52
|
+
const dy = p2[1] - p1[1];
|
|
53
|
+
const length = Math.sqrt(dx * dx + dy * dy);
|
|
54
|
+
|
|
55
|
+
pathData.segmentLengths.push(length);
|
|
56
|
+
pathData.totalLength += length;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// If closed, add final segment back to start
|
|
60
|
+
if (closed) {
|
|
61
|
+
const p1 = points[points.length - 1];
|
|
62
|
+
const p2 = points[0];
|
|
63
|
+
const dx = p2[0] - p1[0];
|
|
64
|
+
const dy = p2[1] - p1[1];
|
|
65
|
+
const length = Math.sqrt(dx * dx + dy * dy);
|
|
66
|
+
|
|
67
|
+
pathData.segmentLengths.push(length);
|
|
68
|
+
pathData.totalLength += length;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
newState.pathData = pathData;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Get path data
|
|
75
|
+
const { segmentLengths, totalLength, points: pathPoints } = newState.pathData;
|
|
76
|
+
|
|
77
|
+
// Calculate distance along path
|
|
78
|
+
const targetDistance = easedT * totalLength;
|
|
79
|
+
|
|
80
|
+
// Find which segment we're on and the progress through it
|
|
81
|
+
let distanceTraveled = 0;
|
|
82
|
+
let segmentIndex = 0;
|
|
83
|
+
|
|
84
|
+
for (let i = 0; i < segmentLengths.length; i++) {
|
|
85
|
+
if (distanceTraveled + segmentLengths[i] >= targetDistance) {
|
|
86
|
+
segmentIndex = i;
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
distanceTraveled += segmentLengths[i];
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Calculate progress through current segment
|
|
93
|
+
const segmentProgress =
|
|
94
|
+
(targetDistance - distanceTraveled) / segmentLengths[segmentIndex];
|
|
95
|
+
|
|
96
|
+
// Get points for current segment
|
|
97
|
+
const p1 = pathPoints[segmentIndex];
|
|
98
|
+
const p2 =
|
|
99
|
+
segmentIndex < pathPoints.length - 1
|
|
100
|
+
? pathPoints[segmentIndex + 1]
|
|
101
|
+
: pathPoints[0]; // Wrap to start if closed
|
|
102
|
+
|
|
103
|
+
// Interpolate between points
|
|
104
|
+
const x = Tween.lerp(p1[0], p2[0], segmentProgress);
|
|
105
|
+
const y = Tween.lerp(p1[1], p2[1], segmentProgress);
|
|
106
|
+
|
|
107
|
+
// Calculate angle of current segment (for rotation if needed)
|
|
108
|
+
const angle = Math.atan2(p2[1] - p1[1], p2[0] - p1[0]);
|
|
109
|
+
|
|
110
|
+
// Return standardized result
|
|
111
|
+
return Motion.animationResult(
|
|
112
|
+
{
|
|
113
|
+
x,
|
|
114
|
+
y,
|
|
115
|
+
angle,
|
|
116
|
+
segmentIndex,
|
|
117
|
+
segmentProgress,
|
|
118
|
+
pathProgress: easedT,
|
|
119
|
+
},
|
|
120
|
+
t,
|
|
121
|
+
loop,
|
|
122
|
+
completed,
|
|
123
|
+
newState
|
|
124
|
+
);
|
|
125
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Motion } from "./motion";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Hop animation - makes the object jump up and down rhythmically
|
|
5
|
+
*
|
|
6
|
+
* @param {number} baseY - The ground/base Y position
|
|
7
|
+
* @param {number} hopHeight - Maximum height (negative Y offset)
|
|
8
|
+
* @param {number} elapsedTime - Elapsed time in seconds
|
|
9
|
+
* @param {number} duration - Duration of one hop (up and down)
|
|
10
|
+
* @param {boolean} [loop=true] - Whether the hop repeats
|
|
11
|
+
* @param {boolean} [yoyo=true] - Whether the hop repeats
|
|
12
|
+
* @param {Function} [easingFn=null] - Optional easing for jump arc
|
|
13
|
+
* @param {Object} [callbacks={}] - Optional callback functions
|
|
14
|
+
* @param {Object} [state=null] - Internal state
|
|
15
|
+
* @returns {Object} Animation result with y position
|
|
16
|
+
*/
|
|
17
|
+
export function hopV1(
|
|
18
|
+
baseY,
|
|
19
|
+
hopHeight,
|
|
20
|
+
elapsedTime,
|
|
21
|
+
duration,
|
|
22
|
+
loop = true,
|
|
23
|
+
yoyo = true,
|
|
24
|
+
easingFn = null,
|
|
25
|
+
callbacks = {},
|
|
26
|
+
state = null
|
|
27
|
+
) {
|
|
28
|
+
const {
|
|
29
|
+
t,
|
|
30
|
+
easedT,
|
|
31
|
+
completed,
|
|
32
|
+
state: newState,
|
|
33
|
+
} = Motion._frame(elapsedTime, duration, loop, easingFn, callbacks, state, yoyo);
|
|
34
|
+
|
|
35
|
+
let arc = 0;
|
|
36
|
+
|
|
37
|
+
if (!loop && !yoyo) {
|
|
38
|
+
// One-shot up only: freeze at peak when done
|
|
39
|
+
arc = completed ? 1 : Math.sin(Math.min(t, 1) * Math.PI * 0.5); // sin(π/2 * t) = 0 → 1
|
|
40
|
+
} else if (yoyo) {
|
|
41
|
+
// Full up/down cycle using easedT (symmetric)
|
|
42
|
+
arc = Math.sin(easedT * Math.PI); // 0 → 1 → 0
|
|
43
|
+
} else {
|
|
44
|
+
// Looping up-only arc (snap to origin after)
|
|
45
|
+
arc = Math.sin(Math.min(t, 1) * Math.PI * 0.5); // sin(π/2 * t)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const y = baseY - hopHeight * arc;
|
|
49
|
+
|
|
50
|
+
return Motion.animationResult({ y }, t, loop, completed, newState);
|
|
51
|
+
}
|
|
52
|
+
|