-
-
Notifications
You must be signed in to change notification settings - Fork 0
Custom Animations
LibAnimate's RegisterAnimation API lets you define and register your own animations using the same keyframe system as the built-in animations.
Every animation definition has this structure:
LibAnimate:RegisterAnimation("myAnimation", {
type = "entrance", -- "entrance", "exit", or "attention"
defaultDuration = 0.6, -- default duration in seconds
defaultDistance = 300, -- default distance in pixels (optional)
keyframes = {
{ progress = 0.0, ... },
{ progress = 1.0, ... },
},
})Each keyframe is a table with a required progress field and optional property fields:
| Property | Type | Default | Description |
|---|---|---|---|
progress |
number |
- |
Required. Time point from 0.0 (start) to 1.0 (end) |
translateX |
number |
0 |
Horizontal offset as a fraction of distance
|
translateY |
number |
0 |
Vertical offset as a fraction of distance
|
scale |
number |
1.0 |
Uniform scale factor (minimum 0.001) |
alpha |
number |
1.0 |
Opacity from 0.0 (invisible) to 1.0 (opaque) |
easing |
EasingSpec |
nil |
Easing for the segment starting at this keyframe |
The progress value represents a point in the animation timeline:
-
0.0= the very start of the animation -
0.5= halfway through -
1.0= the very end
Between keyframes, property values are linearly interpolated (after applying any easing function).
Translation values are fractions of the distance parameter, not raw pixel values. This makes animations resolution-independent:
-- If distance = 300:
-- translateY = 1.0 means 300 pixels
-- translateY = 0.5 means 150 pixels
-- translateY = -1.0 means -300 pixelsCoordinate system: WoW uses positive Y = up (opposite of CSS). In LibAnimate's built-in animations, translateY = 1.0 means "above the anchor point" and translateY = -1.0 means "below."
The easing field on a keyframe controls the interpolation for the segment starting at that keyframe. The last keyframe's easing is ignored (there's no segment after it).
keyframes = {
{ progress = 0.0, alpha = 0, easing = "easeOutCubic" }, -- segment 0 to 0.5 uses easeOutCubic
{ progress = 0.5, alpha = 0.8, easing = "easeInQuad" }, -- segment 0.5 to 1.0 uses easeInQuad
{ progress = 1.0, alpha = 1.0 }, -- no segment after this
}LibAnimate:RegisterAnimation("gentleAppear", {
type = "entrance",
defaultDuration = 0.5,
keyframes = {
{ progress = 0.0, alpha = 0, scale = 0.8 },
{ progress = 1.0, alpha = 1.0, scale = 1.0 },
},
})LibAnimate:RegisterAnimation("popIn", {
type = "entrance",
defaultDuration = 0.6,
keyframes = {
{ progress = 0.0, alpha = 0, scale = 0.3, easing = "easeOutCubic" },
{ progress = 0.6, alpha = 1.0, scale = 1.1 },
{ progress = 1.0, scale = 1.0 },
},
})Attention seekers must return to the original state at progress = 1.0:
LibAnimate:RegisterAnimation("blink", {
type = "attention",
defaultDuration = 1.0,
keyframes = {
{ progress = 0.0, alpha = 1.0 },
{ progress = 0.25, alpha = 0 },
{ progress = 0.5, alpha = 1.0 },
{ progress = 0.75, alpha = 0 },
{ progress = 1.0, alpha = 1.0 },
},
})LibAnimate:RegisterAnimation("shrinkOut", {
type = "exit",
defaultDuration = 0.4,
defaultDistance = 200,
keyframes = {
{ progress = 0.0, alpha = 1.0, scale = 1.0, translateY = 0, easing = "easeInBack" },
{ progress = 1.0, alpha = 0, scale = 0.3, translateY = -0.5 },
},
})
-- Usage: hide the frame in onFinished
LibAnimate:Animate(frame, "shrinkOut", {
onFinished = function(f) f:Hide() end,
})Use a {p1x, p1y, p2x, p2y} table for custom easing curves:
LibAnimate:RegisterAnimation("elasticPop", {
type = "entrance",
defaultDuration = 0.8,
keyframes = {
{ progress = 0.0, scale = 0, alpha = 0, easing = {0.34, 1.56, 0.64, 1.0} },
{ progress = 0.7, scale = 1.05, alpha = 1.0 },
{ progress = 1.0, scale = 1.0 },
},
})LibAnimate:RegisterAnimation("dramaticEntry", {
type = "entrance",
defaultDuration = 1.2,
defaultDistance = 400,
keyframes = {
{ progress = 0.0, translateY = 1.0, alpha = 0, scale = 0.5, easing = "easeOutCubic" },
{ progress = 0.4, translateY = 0, alpha = 0.8, scale = 1.15 },
{ progress = 0.6, scale = 0.95, alpha = 0.9, easing = "easeInOutQuad" },
{ progress = 0.8, scale = 1.05, alpha = 1.0 },
{ progress = 1.0, scale = 1.0, alpha = 1.0 },
},
})RegisterAnimation validates your definition and throws descriptive errors:
| Rule | Error Message |
|---|---|
| Name must be a non-empty string | "name must be a non-empty string" |
| Definition must be a table | "definition must be a table" |
| Type must be valid | "type must be 'entrance', 'exit', or 'attention'" |
| At least 2 keyframes | "keyframes must be a table with at least 2 entries" |
| Keyframes sorted by progress | "keyframes must be sorted by ascending progress" |
- Start simple - Begin with 2 keyframes, then add intermediate ones for complexity
- Use easing - Per-segment easing makes animations feel natural
-
Test with different distances - Your
translateX/translateYvalues scale with distance -
Attention seekers must loop back - Ensure
progress = 1.0returns all properties to their defaults (scale=1, alpha=1, translate=0) -
Scale minimum - Scale is clamped to
0.001to prevent WoW engine errors - Overwriting - Registering with an existing name silently overwrites the old animation
-
API Reference - Full
RegisterAnimationAPI docs - Easing Functions - Available easing presets and custom curves
- Animation Catalog - Study built-in animations for inspiration