FloatAnimationLib — Float Animation Library
Animates any numeric (float) property of a visual control — position, size, opacity, rotation, scale, and more. This is the most versatile animation type in Plan9Basic, capable of creating smooth transitions, looping effects, and complex motion with 11 interpolation curves and 3 easing modes. 46 functions.
| Category | Count | Description |
|---|---|---|
| Error Handling | 4 | floatani_error, errormsg$, strerror$, clearerror |
| Creation & Destruction | 3 | floatani# (2 overloads), floatani_free |
| Playback Control | 3 | start, stop, stopatcurrent |
| Core Properties | 10 | propertyname, startvalue, stopvalue, duration, delay (get/set) |
| Behavior — Easing | 4 | animationtype, interpolation (get/set) |
| Behavior — Flags | 10 | autoreverse, inverse, loop, enabled, startfromcurrent (get/set) |
| State Queries | 3 | running, normalizedtime, name$ |
| Triggers | 4 | trigger, triggerinverse (get/set) |
| Events | 5 | onfinish, onprocess (get/set), clearcallbacks# |
Cross-Platform Support
| Platform | Status | Notes |
|---|---|---|
| Windows | ✅ Full Support | Win32/Win64 |
| Linux | ✅ Full Support | GTK-based |
| Android | ✅ Full Support | Hardware-accelerated |
Error Handling
| Function | Signature | Description |
|---|---|---|
floatani_error() | floatani_error@ | Last error code (0 = no error) |
floatani_errormsg$() | floatani_errormsg$@ | Last error message as string |
floatani_strerror$(code) | floatani_strerror$@n | Description for a given error code |
floatani_clearerror() | floatani_clearerror@ | Clear the error state |
Error Codes
| Code | Constant | Description |
|---|---|---|
| 0 | ERR_NONE | No error |
| 1 | ERR_NIL_ANIMATION | Animation pointer is nil |
| 2 | ERR_INVALID_PROPERTY | Invalid property name or object |
| 3 | ERR_INVALID_VALUE | Invalid parameter value |
| 4 | ERR_ANIMATION_RUNNING | Cannot modify while animation is running |
How It Works
A float animation smoothly transitions a single numeric property of its parent control from a start value to a stop value over a given duration. The animation is attached to a parent control (rectangle, circle, image, label, etc.) and targets one of its properties by name.
' 1. Create the animation on a parent control let ani# = floatani#(myControl#) ' 2. Tell it WHAT to animate floatani_propertyname#(ani#, "Opacity") ' 3. Tell it FROM where TO where floatani_startvalue#(ani#, 1.0) floatani_stopvalue#(ani#, 0.0) ' 4. Tell it HOW LONG floatani_duration#(ani#, 2.0) ' 5. Optionally set easing curve floatani_interpolation#(ani#, "Quadratic") floatani_animationtype#(ani#, "InOut") ' 6. Start! floatani_start(ani#)
floatani#(rect#), the animation becomes owned by that rectangle and will animate one of its properties.Animatable Properties
You can animate any numeric property accessible by name on a FireMonkey control. The most common ones:
| Property Name | Type | Description | Typical Range |
|---|---|---|---|
Position.X | Float | Horizontal position | 0 – parent width |
Position.Y | Float | Vertical position | 0 – parent height |
Width | Float | Control width | 0+ |
Height | Float | Control height | 0+ |
Opacity | Float | Transparency | 0.0 (invisible) – 1.0 (opaque) |
RotationAngle | Float | Rotation in degrees | 0 – 360 |
Scale.X | Float | Horizontal scale factor | 0.0+ (1.0 = normal) |
Scale.Y | Float | Vertical scale factor | 0.0+ (1.0 = normal) |
"Opacity", "opacity", and "OPACITY" all work. Dot notation accesses sub-properties: "Position.X", "Scale.Y".Interpolation Curves
The interpolation type controls the mathematical curve used to transition between values. Combined with an easing type (In/Out/InOut), this creates a wide range of motion effects:
| Interpolation | Character | Best For |
|---|---|---|
"Linear" | Constant speed, no acceleration | Progress bars, continuous rotation |
"Quadratic" | Gentle curve | Subtle, natural-feeling motion |
"Cubic" | Moderate curve | UI transitions, slide-ins |
"Quartic" | Pronounced curve | Dramatic entrances/exits |
"Quintic" | Very pronounced curve | Emphatic motion |
"Sinusoidal" | Sine-wave smoothness | Pulsing, breathing effects |
"Exponential" | Sharp start or end | Snap-in or fade-out effects |
"Circular" | Arc-based acceleration | Orbital or circular-feeling motion |
"Elastic" | Springy overshoot | Bouncy UI, playful interactions |
"Back" | Overshoots then settles | Pull-back effects, attention-grabbing |
"Bounce" | Bouncing ball effect | Drop animations, playful physics |
Easing Types (AnimationType)
The easing type controls where the interpolation curve is applied:
| Type | Effect | Description |
|---|---|---|
"In" | Slow start → fast end | Acceleration at the beginning |
"Out" | Fast start → slow end | Deceleration at the end |
"InOut" | Slow → fast → slow | Accelerate then decelerate |
' Smooth slide-in: starts fast, decelerates floatani_interpolation#(ani#, "Cubic") floatani_animationtype#(ani#, "Out") ' Bouncing drop: drops and bounces at the end floatani_interpolation#(ani#, "Bounce") floatani_animationtype#(ani#, "Out") ' Elastic snap: overshoots with springy feel floatani_interpolation#(ani#, "Elastic") floatani_animationtype#(ani#, "Out") ' Gentle pulse: smooth in and out floatani_interpolation#(ani#, "Sinusoidal") floatani_animationtype#(ani#, "InOut")
Creation & Destruction
| Function | Signature | Description |
|---|---|---|
floatani#(parent#) | floatani#@# | Create animation attached to parent control |
floatani#(parent#, name$) | floatani#@#$ | Create named animation attached to parent |
floatani_free(ani#) | floatani_free@# | Destroy animation object |
parent# parameter is the control being animated, not a layout container. The animation becomes a child component of that control. Named animations are useful for identification when multiple animations exist on one control.Playback Control
| Function | Signature | Description |
|---|---|---|
floatani_start(ani#) | floatani_start@# | Start the animation |
floatani_stop(ani#) | floatani_stop@# | Stop and reset to start value |
floatani_stopatcurrent(ani#) | floatani_stopatcurrent@# | Stop at the current interpolated position |
floatani_stopatcurrent when you want the property to freeze at wherever it is mid-animation, rather than snapping back to the start value.Core Properties
Property Name
| Function | Signature | Description |
|---|---|---|
floatani_propertyname#(ani#, name$) | floatani_propertyname#@#$ | Set which property to animate |
floatani_propertyname$(ani#) | floatani_propertyname$@# | Get the property name |
Value Range
| Function | Signature | Description |
|---|---|---|
floatani_startvalue#(ani#, value) | floatani_startvalue#@#n | Set starting value |
floatani_startvalue(ani#) | floatani_startvalue@# | Get starting value |
floatani_stopvalue#(ani#, value) | floatani_stopvalue#@#n | Set ending value |
floatani_stopvalue(ani#) | floatani_stopvalue@# | Get ending value |
Timing
| Function | Signature | Description |
|---|---|---|
floatani_duration#(ani#, seconds) | floatani_duration#@#n | Set duration in seconds |
floatani_duration(ani#) | floatani_duration@# | Get duration |
floatani_delay#(ani#, seconds) | floatani_delay#@#n | Set delay before animation starts |
floatani_delay(ani#) | floatani_delay@# | Get delay |
Behavior Flags
Easing & Interpolation
| Function | Signature | Description |
|---|---|---|
floatani_animationtype#(ani#, type$) | floatani_animationtype#@#$ | Set easing: "In", "Out", "InOut" |
floatani_animationtype$(ani#) | floatani_animationtype$@# | Get current easing type |
floatani_interpolation#(ani#, type$) | floatani_interpolation#@#$ | Set interpolation curve (see table above) |
floatani_interpolation$(ani#) | floatani_interpolation$@# | Get current interpolation type |
Boolean Flags
| Function | Signature | Description |
|---|---|---|
floatani_autoreverse#(ani#, flag) | floatani_autoreverse#@#n | If 1, plays forward then backward |
floatani_autoreverse(ani#) | floatani_autoreverse@# | Get autoreverse flag |
floatani_inverse#(ani#, flag) | floatani_inverse#@#n | If 1, plays in reverse direction |
floatani_inverse(ani#) | floatani_inverse@# | Get inverse flag |
floatani_loop#(ani#, flag) | floatani_loop#@#n | If 1, loops indefinitely |
floatani_loop(ani#) | floatani_loop@# | Get loop flag |
floatani_enabled#(ani#, flag) | floatani_enabled#@#n | Enable or disable the animation |
floatani_enabled(ani#) | floatani_enabled@# | Get enabled state |
floatani_startfromcurrent#(ani#, flag) | floatani_startfromcurrent#@#n | If 1, ignores startvalue and begins from current property value |
floatani_startfromcurrent(ani#) | floatani_startfromcurrent@# | Get startfromcurrent flag |
autoreverse = 1 + loop = 1 to create a continuous back-and-forth oscillation (e.g., pulsing opacity, breathing scale effect). Set startfromcurrent = 1 when you want to smoothly transition from wherever the property currently is, without jumping to the start value.State Queries
| Function | Signature | Description |
|---|---|---|
floatani_running(ani#) | floatani_running@# | Returns 1 if currently animating |
floatani_normalizedtime(ani#) | floatani_normalizedtime@# | Returns progress from 0.0 to 1.0 |
floatani_name$(ani#) | floatani_name$@# | Get the animation’s name (set at creation) |
Triggers
Triggers let an animation start or reverse automatically when a parent property changes, without calling floatani_start manually. They use FireMonkey trigger expressions.
| Function | Signature | Description |
|---|---|---|
floatani_trigger#(ani#, expr$) | floatani_trigger#@#$ | Set trigger expression (starts animation) |
floatani_trigger$(ani#) | floatani_trigger$@# | Get trigger expression |
floatani_triggerinverse#(ani#, expr$) | floatani_triggerinverse#@#$ | Set inverse trigger (reverses animation) |
floatani_triggerinverse$(ani#) | floatani_triggerinverse$@# | Get inverse trigger expression |
' Auto-fade when mouse enters/leaves floatani_trigger#(ani#, "IsMouseOver=true") floatani_triggerinverse#(ani#, "IsMouseOver=false")
Events
Each event has a setter and a getter. Use floatani_clearcallbacks# to disconnect all at once.
| Event Setter | Getter | Callback Signature | Description |
|---|---|---|---|
floatani_onfinish#(ani#, func$) | floatani_onfinish$(ani#) | function name(sender#) | Fired when animation completes (after all loops) |
floatani_onprocess#(ani#, func$) | floatani_onprocess$(ani#) | function name(sender#) | Fired on every animation frame |
floatani_clearcallbacks#(ani#) | — | — | Disconnect all callbacks |
onprocess callback fires on every animation frame (typically 60 times per second). Use it sparingly — heavy logic in this callback can cause stuttering. Prefer onfinish when you only need to know when the animation completes.Complete Examples
Fade In/Out
let frm# = form#("Fade Demo", 400, 300) form_position#(frm#, 4) let rect# = rectangle#(frm#, 100, 50, 200, 200) rectangle_fill#(rect#, "#3498db") ' Fade opacity from 1.0 to 0.2 and back, forever let fadeAni# = floatani#(rect#) floatani_propertyname#(fadeAni#, "Opacity") floatani_startvalue#(fadeAni#, 1.0) floatani_stopvalue#(fadeAni#, 0.2) floatani_duration#(fadeAni#, 2.0) floatani_interpolation#(fadeAni#, "Linear") floatani_autoreverse#(fadeAni#, 1) floatani_loop#(fadeAni#, 1) floatani_start(fadeAni#) form_show(frm#) while form_visible(frm#) = 1 processmessages() end while
Bouncing Ball
let frm# = form#("Bounce Demo", 400, 400) form_position#(frm#, 4) let ball# = circle#(frm#, 175, 50, 50, 50) circle_fill#(ball#, "#e74c3c") ' Animate Y position with bounce easing let bounceAni# = floatani#(ball#) floatani_propertyname#(bounceAni#, "Position.Y") floatani_startvalue#(bounceAni#, 50) floatani_stopvalue#(bounceAni#, 300) floatani_duration#(bounceAni#, 1.5) floatani_interpolation#(bounceAni#, "Bounce") floatani_animationtype#(bounceAni#, "Out") floatani_autoreverse#(bounceAni#, 1) floatani_loop#(bounceAni#, 1) floatani_start(bounceAni#) form_show(frm#) while form_visible(frm#) = 1 processmessages() end while
Spinning Rectangle
let frm# = form#("Spin Demo", 400, 400) form_position#(frm#, 4) let rect# = rectangle#(frm#, 150, 150, 100, 100) rectangle_fill#(rect#, "#2ecc71") ' Continuous 360-degree rotation let spinAni# = floatani#(rect#) floatani_propertyname#(spinAni#, "RotationAngle") floatani_startvalue#(spinAni#, 0) floatani_stopvalue#(spinAni#, 360) floatani_duration#(spinAni#, 2.0) floatani_interpolation#(spinAni#, "Linear") floatani_loop#(spinAni#, 1) floatani_start(spinAni#) form_show(frm#) while form_visible(frm#) = 1 processmessages() end while
Pulsing Scale
let frm# = form#("Pulse Demo", 400, 400) form_position#(frm#, 4) let circ# = circle#(frm#, 150, 150, 100, 100) circle_fill#(circ#, "#9b59b6") ' Pulse Scale.X let scaleX# = floatani#(circ#) floatani_propertyname#(scaleX#, "Scale.X") floatani_startvalue#(scaleX#, 1.0) floatani_stopvalue#(scaleX#, 1.2) floatani_duration#(scaleX#, 0.5) floatani_interpolation#(scaleX#, "Sinusoidal") floatani_animationtype#(scaleX#, "InOut") floatani_autoreverse#(scaleX#, 1) floatani_loop#(scaleX#, 1) floatani_start(scaleX#) ' Pulse Scale.Y in sync let scaleY# = floatani#(circ#) floatani_propertyname#(scaleY#, "Scale.Y") floatani_startvalue#(scaleY#, 1.0) floatani_stopvalue#(scaleY#, 1.2) floatani_duration#(scaleY#, 0.5) floatani_interpolation#(scaleY#, "Sinusoidal") floatani_animationtype#(scaleY#, "InOut") floatani_autoreverse#(scaleY#, 1) floatani_loop#(scaleY#, 1) floatani_start(scaleY#) form_show(frm#) while form_visible(frm#) = 1 processmessages() end while
Slide with Finish Callback
let frm# = form#("Slide Demo", 500, 300) form_position#(frm#, 4) let rect# = rectangle#(frm#, 0, 100, 80, 80) rectangle_fill#(rect#, "#e67e22") let lbl# = label#(frm#, "Sliding...") label_bounds#(lbl#, 180, 250, 140, 25) ' Slide across the screen let slideAni# = floatani#(rect#) floatani_propertyname#(slideAni#, "Position.X") floatani_startvalue#(slideAni#, 0) floatani_stopvalue#(slideAni#, 420) floatani_duration#(slideAni#, 3.0) floatani_interpolation#(slideAni#, "Cubic") floatani_animationtype#(slideAni#, "InOut") floatani_onfinish#(slideAni#, "OnSlideComplete") floatani_start(slideAni#) form_show(frm#) function OnSlideComplete(sender#) label_text#(lbl#, "Done!") ' Reverse direction and go back floatani_inverse#(sender#, 1) floatani_start(sender#) endfunction while form_visible(frm#) = 1 processmessages() end while
Multiple Animations on One Control
let frm# = form#("Combined Animations", 500, 400) form_position#(frm#, 4) let rect# = rectangle#(frm#, 50, 150, 80, 80) rectangle_fill#(rect#, "#1abc9c") ' Slide horizontally let moveAni# = floatani#(rect#, "MoveX") floatani_propertyname#(moveAni#, "Position.X") floatani_startvalue#(moveAni#, 50) floatani_stopvalue#(moveAni#, 370) floatani_duration#(moveAni#, 3.0) floatani_interpolation#(moveAni#, "Linear") floatani_autoreverse#(moveAni#, 1) floatani_loop#(moveAni#, 1) ' Spin while sliding let spinAni# = floatani#(rect#, "Spin") floatani_propertyname#(spinAni#, "RotationAngle") floatani_startvalue#(spinAni#, 0) floatani_stopvalue#(spinAni#, 360) floatani_duration#(spinAni#, 1.5) floatani_interpolation#(spinAni#, "Linear") floatani_loop#(spinAni#, 1) ' Fade in and out let fadeAni# = floatani#(rect#, "Fade") floatani_propertyname#(fadeAni#, "Opacity") floatani_startvalue#(fadeAni#, 1.0) floatani_stopvalue#(fadeAni#, 0.3) floatani_duration#(fadeAni#, 2.0) floatani_interpolation#(fadeAni#, "Sinusoidal") floatani_animationtype#(fadeAni#, "InOut") floatani_autoreverse#(fadeAni#, 1) floatani_loop#(fadeAni#, 1) ' Start all three at once floatani_start(moveAni#) floatani_start(spinAni#) floatani_start(fadeAni#) form_show(frm#) while form_visible(frm#) = 1 processmessages() end while
Best Practices
| Practice | Why |
|---|---|
Use autoreverse + loop for continuous oscillation | Creates smooth back-and-forth without manual restart |
Use "Linear" for continuous rotation | Other interpolations create uneven speed, noticeable in looped spins |
Use "Cubic" + "Out" for UI slide-ins | Feels natural — fast entry, gentle deceleration |
Use "Bounce" + "Out" for playful drops | Objects appear to fall and bounce realistically |
Use "Sinusoidal" + "InOut" for pulsing | Most natural breathing/heartbeat rhythm |
Use startfromcurrent = 1 for smooth interruptions | Avoids jarring jumps when restarting mid-animation |
Avoid heavy logic in onprocess | Called every frame (~60/sec) — can cause stuttering |
Name animations with floatani#(parent#, name$) | Easier to identify when multiple animations on one control |
| Pair Scale.X and Scale.Y animations | Animating only one axis creates distortion (unless that’s intended) |
Use delay for staggered animations | Different delays on multiple controls create cascade effects |
Quick Reference
| Function | Signature | Description |
|---|---|---|
floatani_error / errormsg$ / strerror$ / clearerror | various | Error handling (4) |
floatani#(parent#[, name$]) | floatani#@# / floatani#@#$ | Create (2 overloads) |
floatani_free(ani#) | floatani_free@# | Destroy |
floatani_start / stop / stopatcurrent | various | Playback control (3) |
floatani_propertyname# / propertyname$ | various | Target property (2) |
floatani_startvalue / stopvalue | various | Value range (4) |
floatani_duration / delay | various | Timing (4) |
floatani_animationtype / interpolation | various | Easing & curves (4) |
floatani_autoreverse / inverse / loop / enabled / startfromcurrent | various | Behavior flags (10) |
floatani_running / normalizedtime / name$ | various | State queries (3) |
floatani_trigger / triggerinverse | various | Automatic triggers (4) |
floatani_onfinish / onprocess / clearcallbacks# | various | Events (5) |
46 functions. Part of the Plan9Basic Animation library system.