RectAnimationLib — Rect Animation Library

Animates the bounds (position and size) of a visual control as a single composite animation. Unlike FloatAnimationLib which targets one property at a time, RectAnimationLib animates Position.X, Position.Y, Width, and Height simultaneously — making it ideal for expand/collapse, slide-and-resize, thumbnail-to-fullscreen, and card-flip effects. 44 functions.

CategoryCountDescription
Error Handling4rectani_error, errormsg$, strerror$, clearerror
Creation & Destruction3rectani# (2 overloads), rectani_free
Playback Control3start, stop, stopatcurrent
Bounds — Start5startbounds# (setter), startx, starty, startwidth, startheight (getters)
Bounds — Stop5stopbounds# (setter), stopx, stopy, stopwidth, stopheight (getters)
Timing4duration, delay (get/set)
Behavior — Easing4animationtype, interpolation (get/set)
Behavior — Flags8autoreverse, inverse, loop, enabled (get/set)
State Queries3running, normalizedtime, name$
Events5onfinish, onprocess (get/set), clearcallbacks#

Cross-Platform Support

PlatformStatusNotes
Windows✅ Full SupportWin32/Win64
Linux✅ Full SupportGTK-based
Android✅ Full SupportHardware-accelerated

Error Handling

FunctionSignatureDescription
rectani_error()rectani_error@Last error code (0 = no error)
rectani_errormsg$()rectani_errormsg$@Last error message as string
rectani_strerror$(code)rectani_strerror$@nDescription for a given error code
rectani_clearerror()rectani_clearerror@Clear the error state

Error Codes

CodeConstantDescription
0ERR_NONENo error
1ERR_NIL_ANIMATIONAnimation pointer is nil
2ERR_INVALID_PROPERTYInvalid property or object
3ERR_INVALID_VALUEInvalid bounds value
4ERR_ANIMATION_RUNNINGCannot modify while animation is running

How It Works

RectAnimationLib works differently from the other animation libraries. Instead of targeting a single named property, it always animates all four bounds values at once: X, Y, Width, and Height. You define a start rectangle and a stop rectangle, and the animation interpolates all four values simultaneously:

╯ basic pattern
' 1. Create the animation on a control
let ani# = rectani#(myRect#)

' 2. Define start bounds (x, y, width, height)
rectani_startbounds#(ani#, 150, 150, 100, 100)

' 3. Define stop bounds (x, y, width, height)
rectani_stopbounds#(ani#, 50, 50, 300, 300)

' 4. Set timing and easing
rectani_duration#(ani#, 1.5)
rectani_interpolation#(ani#, "Cubic")
rectani_animationtype#(ani#, "Out")

' 5. Start!
rectani_start(ani#)
ⓘ No propertyname needed: Unlike FloatAnimationLib and IntAnimationLib, RectAnimationLib does not have a propertyname function. It always targets the control’s bounds (position + size) as a composite unit.

Rect vs Float Animation

Choose the right animation library based on what you need to animate:

ScenarioRecommendedWhy
Animate position only (slide)FloatAnimationLibTarget Position.X or Position.Y individually
Animate size only (grow/shrink)FloatAnimationLibTarget Width or Height individually
Animate opacity, rotation, scaleFloatAnimationLibThese are single float properties
Move and resize simultaneouslyRectAnimationLibAnimates all 4 bounds as one unit
Expand/collapse effectsRectAnimationLibPosition and size must change together
Thumbnail → fullscreen zoomRectAnimationLibCoordinated move + resize
Card flip (width to 0 and back)RectAnimationLibX position shifts as width collapses
Slide panel from off-screenRectAnimationLibPosition changes while size stays constant
ⓘ Key difference: With FloatAnimationLib, moving and resizing a control requires 4 separate animations (one per property). With RectAnimationLib, it’s a single animation that keeps all four values perfectly synchronized.

Interpolation & Easing

Interpolation Curves

InterpolationCharacterBest For
"Linear"Constant speedMechanical, even motion
"Quadratic"Gentle curveSmooth panel slides
"Cubic"Moderate curveUI expand/collapse (most common)
"Quartic"Pronounced curveDramatic size changes
"Quintic"Very pronouncedEmphatic motion
"Sinusoidal"Sine-wave smoothCard flip effects
"Exponential"Sharp curveSnap-open dialogs
"Circular"Arc-basedOrganic feeling motion
"Elastic"Springy overshootBouncy expand effects
"Back"Overshoots then settlesThumbnail zoom with overshoot
"Bounce"Bouncing ballPlayful expand/collapse

Easing Types (AnimationType)

TypeEffectDescription
"In"Slow start → fast endAccelerating resize
"Out"Fast start → slow endDecelerating resize (most natural)
"InOut"Slow → fast → slowSmooth start and end

Creation & Destruction

FunctionSignatureDescription
rectani#(parent#)rectani#@#Create animation attached to parent control
rectani#(parent#, name$)rectani#@#$Create named animation attached to parent
rectani_free(ani#)rectani_free@#Destroy animation object

Playback Control

FunctionSignatureDescription
rectani_start(ani#)rectani_start@#Start the animation
rectani_stop(ani#)rectani_stop@#Stop and reset to start bounds
rectani_stopatcurrent(ani#)rectani_stopatcurrent@#Stop at current interpolated bounds

Bounds (Start / Stop)

Instead of a single start/stop value, RectAnimationLib uses rectangles defined by four values: X, Y, Width, Height. The setter takes all four at once; individual getters let you read each component.

Start Bounds

FunctionSignatureDescription
rectani_startbounds#(ani#, x, y, w, h)rectani_startbounds#@#nnnnSet starting rectangle (x, y, width, height)
rectani_startx(ani#)rectani_startx@#Get starting X position
rectani_starty(ani#)rectani_starty@#Get starting Y position
rectani_startwidth(ani#)rectani_startwidth@#Get starting width
rectani_startheight(ani#)rectani_startheight@#Get starting height

Stop Bounds

FunctionSignatureDescription
rectani_stopbounds#(ani#, x, y, w, h)rectani_stopbounds#@#nnnnSet ending rectangle (x, y, width, height)
rectani_stopx(ani#)rectani_stopx@#Get ending X position
rectani_stopy(ani#)rectani_stopy@#Get ending Y position
rectani_stopwidth(ani#)rectani_stopwidth@#Get ending width
rectani_stopheight(ani#)rectani_stopheight@#Get ending height

Timing

FunctionSignatureDescription
rectani_duration#(ani#, seconds)rectani_duration#@#nSet duration in seconds
rectani_duration(ani#)rectani_duration@#Get duration
rectani_delay#(ani#, seconds)rectani_delay#@#nSet delay before animation starts
rectani_delay(ani#)rectani_delay@#Get delay

Behavior Flags

Easing & Interpolation

FunctionSignatureDescription
rectani_animationtype#(ani#, type$)rectani_animationtype#@#$Set easing: "In", "Out", "InOut"
rectani_animationtype$(ani#)rectani_animationtype$@#Get easing type
rectani_interpolation#(ani#, type$)rectani_interpolation#@#$Set interpolation curve
rectani_interpolation$(ani#)rectani_interpolation$@#Get interpolation type

Boolean Flags

FunctionSignatureDescription
rectani_autoreverse#(ani#, flag)rectani_autoreverse#@#nIf 1, plays forward then backward
rectani_autoreverse(ani#)rectani_autoreverse@#Get autoreverse flag
rectani_inverse#(ani#, flag)rectani_inverse#@#nIf 1, plays in reverse direction
rectani_inverse(ani#)rectani_inverse@#Get inverse flag
rectani_loop#(ani#, flag)rectani_loop#@#nIf 1, loops indefinitely
rectani_loop(ani#)rectani_loop@#Get loop flag
rectani_enabled#(ani#, flag)rectani_enabled#@#nEnable or disable the animation
rectani_enabled(ani#)rectani_enabled@#Get enabled state
ⓘ Toggle pattern: Use rectani_inverse# to toggle between expand and collapse. Set inverse = 0 to play forward (start → stop), inverse = 1 to play backward (stop → start). This is the standard pattern for expand/collapse buttons and sliding panels.

State Queries

FunctionSignatureDescription
rectani_running(ani#)rectani_running@#Returns 1 if currently animating
rectani_normalizedtime(ani#)rectani_normalizedtime@#Returns progress from 0.0 to 1.0
rectani_name$(ani#)rectani_name$@#Get the animation’s name

Events

Event SetterGetterCallback SignatureDescription
rectani_onfinish#(ani#, func$)rectani_onfinish$(ani#)function name(sender#)Fired when animation completes
rectani_onprocess#(ani#, func$)rectani_onprocess$(ani#)function name(sender#)Fired on every animation frame
rectani_clearcallbacks#(ani#)Disconnect all callbacks

Complete Examples

Expand / Collapse

╯ expand.bas
let frm# = form#("Expand Demo", 400, 400)
form_position#(frm#, 4)

let rect# = rectangle#(frm#, 150, 150, 100, 100)
rectangle_fill#(rect#, "#3498db")

' Small centered → large centered, with elastic bounce
let rectAni# = rectani#(rect#)
rectani_startbounds#(rectAni#, 150, 150, 100, 100)
rectani_stopbounds#(rectAni#, 50, 50, 300, 300)
rectani_duration#(rectAni#, 1.5)
rectani_interpolation#(rectAni#, "Elastic")
rectani_animationtype#(rectAni#, "Out")
rectani_autoreverse#(rectAni#, 1)
rectani_loop#(rectAni#, 1)
rectani_start(rectAni#)

form_show(frm#)

while form_visible(frm#) = 1
    processmessages()
end while

Thumbnail to Full Size (with Button Toggle)

╯ thumbnail.bas
let frm# = form#("Thumbnail Demo", 500, 400)
form_position#(frm#, 4)

let img# = image#(frm#, 20, 20, 80, 80)
image_load#(img#, "https://picsum.photos/400/300")

' Animate from thumbnail to full size with overshoot
let rectAni# = rectani#(img#)
rectani_startbounds#(rectAni#, 20, 20, 80, 80)
rectani_stopbounds#(rectAni#, 50, 50, 400, 300)
rectani_duration#(rectAni#, 0.8)
rectani_interpolation#(rectAni#, "Back")
rectani_animationtype#(rectAni#, "Out")

let btn# = button#(frm#, "Expand", 20, 320, 100, 40)
button_onclick#(btn#, "togglesize")

let expanded = 0

form_show(frm#)

function togglesize(sender#)
    if expanded = 0 then
        rectani_inverse#(rectAni#, 0)
        rectani_start(rectAni#)
        expanded = 1
        button_text#(btn#, "Collapse")
    else
        rectani_inverse#(rectAni#, 1)
        rectani_start(rectAni#)
        expanded = 0
        button_text#(btn#, "Expand")
    end if
endfunction

while form_visible(frm#) = 1
    processmessages()
end while

Card Flip Effect

╯ cardflip.bas
let frm# = form#("Card Flip Demo", 400, 300)
form_position#(frm#, 4)

let card# = rectangle#(frm#, 100, 50, 200, 200)
rectangle_fill#(card#, "#e74c3c")
rectangle_corners#(card#, 10, 10)

' Collapse horizontally: width → 0, X shifts to center
let rectAni# = rectani#(card#)
rectani_startbounds#(rectAni#, 100, 50, 200, 200)
rectani_stopbounds#(rectAni#, 200, 50, 0, 200)
rectani_duration#(rectAni#, 0.5)
rectani_interpolation#(rectAni#, "Sinusoidal")
rectani_animationtype#(rectAni#, "InOut")
rectani_autoreverse#(rectAni#, 1)
rectani_loop#(rectAni#, 1)
rectani_start(rectAni#)

form_show(frm#)

while form_visible(frm#) = 1
    processmessages()
end while

Sliding Side Panel

╯ panel-slide.bas
let frm# = form#("Panel Slide Demo", 500, 400)
form_position#(frm#, 4)

' Main content area
let content# = rectangle#(frm#, 0, 0, 500, 400)
rectangle_fill#(content#, "#ecf0f1")

' Side panel (starts off-screen to the left)
let panel# = rectangle#(frm#, -200, 0, 200, 400)
rectangle_fill#(panel#, "Navy")

' Slide from off-screen to visible
let slideAni# = rectani#(panel#)
rectani_startbounds#(slideAni#, -200, 0, 200, 400)
rectani_stopbounds#(slideAni#, 0, 0, 200, 400)
rectani_duration#(slideAni#, 0.4)
rectani_interpolation#(slideAni#, "Cubic")
rectani_animationtype#(slideAni#, "Out")

let btn# = button#(frm#, "Toggle Panel", 250, 180, 120, 40)
button_onclick#(btn#, "togglepanel")

let panelOpen = 0

form_show(frm#)

function togglepanel(sender#)
    if panelOpen = 0 then
        rectani_inverse#(slideAni#, 0)
        rectani_start(slideAni#)
        panelOpen = 1
    else
        rectani_inverse#(slideAni#, 1)
        rectani_start(slideAni#)
        panelOpen = 0
    end if
endfunction

while form_visible(frm#) = 1
    processmessages()
end while

Move and Resize Image

╯ move-resize.bas
let frm# = form#("Move Resize Demo", 500, 400)
form_position#(frm#, 4)

let img# = image#(frm#, 20, 20, 100, 100)
image_load#(img#, "https://picsum.photos/200")

' Small in top-left → large in center
let rectAni# = rectani#(img#)
rectani_startbounds#(rectAni#, 20, 20, 100, 100)
rectani_stopbounds#(rectAni#, 100, 50, 300, 300)
rectani_duration#(rectAni#, 2.0)
rectani_interpolation#(rectAni#, "Cubic")
rectani_animationtype#(rectAni#, "InOut")
rectani_autoreverse#(rectAni#, 1)
rectani_loop#(rectAni#, 1)
rectani_start(rectAni#)

form_show(frm#)

while form_visible(frm#) = 1
    processmessages()
end while

Best Practices

PracticeWhy
Use RectAni when position and size change togetherKeeps all 4 values perfectly synchronized in one animation
Use inverse# for expand/collapse togglesReuses one animation, inverse=0 expands, inverse=1 collapses
Use "Cubic" + "Out" for panel slidesFast entry with gentle deceleration feels natural
Use "Elastic" + "Out" for bouncy expandsPlayful overshoot effect on UI elements
Use "Back" + "Out" for thumbnail zoomSlight overshoot creates a satisfying “pop” effect
Use "Sinusoidal" + "InOut" for card flipsSmooth acceleration and deceleration for width collapse
Keep card flip X position = center of cardWhen width goes to 0, X should be at the card’s center point
Use short durations (0.3–0.8s) for UI transitionsLonger animations feel sluggish for interactive elements
Use negative X/Y for off-screen panelsStart at -width for left panels, parentWidth for right panels
Use onfinish for chained animationsStart a second animation when the first completes

Quick Reference

FunctionSignatureDescription
rectani_error / errormsg$ / strerror$ / clearerrorvariousError handling (4)
rectani#(parent#[, name$])rectani#@# / rectani#@#$Create (2 overloads)
rectani_free(ani#)rectani_free@#Destroy
rectani_start / stop / stopatcurrentvariousPlayback control (3)
rectani_startbounds# / startx / starty / startwidth / startheightvariousStart bounds (5)
rectani_stopbounds# / stopx / stopy / stopwidth / stopheightvariousStop bounds (5)
rectani_duration / delayvariousTiming (4)
rectani_animationtype / interpolationvariousEasing & curves (4)
rectani_autoreverse / inverse / loop / enabledvariousBehavior flags (8)
rectani_running / normalizedtime / name$variousState queries (3)
rectani_onfinish / onprocess / clearcallbacks#variousEvents (5)

44 functions. Part of the Plan9Basic Animation library system.