+++ title = "Building dioxus-motion: A Physics-based Animation Library for Rust" date = "2024-03-15" description = "A deep dive into creating a modern animation library for Dioxus, bringing fluid, physics-based animations to Rust web development" image = "http://rustacean.net/assets/rustacean-flat-happy.png" tags = ["rust", "web-dev", "tutorial"] +++
In the evolving landscape of Rust web development, Dioxus has emerged as a powerful framework for building user interfaces. However, one crucial piece was missing: a sophisticated animation system. While JavaScript ecosystems flourish with animation libraries like Framer Motion, the Rust community lacked an equivalent solution. This gap inspired the creation of dioxus-motion, a physics-based animation library that brings fluid, natural-feeling animations to Rust applications.
My journey began when I was using Framer Motion alongside Dioxus. The experience was promising, but the integration felt incomplete. After searching for native Rust alternatives and finding limited options, I decided to build a solution that would feel natural in the Rust ecosystem while maintaining the intuitive API design that makes Framer Motion so popular.
The library was built with four fundamental principles:
- Rust-First Design: Leveraging Rust's type system to provide compile-time safety for animations
- Zero-Cost Philosophy: Implementing performant animations with minimal runtime overhead
- Universal Compatibility: Supporting both web and native platforms seamlessly
- Developer Experience: Creating an intuitive API that feels natural to Rust developers
dioxus-motion is built on two foundational animation concepts:
- Spring Physics: Implementing Hooke's law for natural, physical animations
- Tweening System: Supporting traditional keyframe animations with customizable easing
let mut position = use_motion(0.0f32);
position.animate_to(
100.0,
AnimationConfig::new(AnimationMode::Spring(Spring {
stiffness: 100.0,
damping: 10.0,
mass: 1.0,
velocity: 0.0,
}))
);
The animation system was built on three key components:
- Motion Hook:
pub fn use_motion<T: Animatable>(initial: T) -> Motion<T> {
let state = use_signal(|| AnimationState::new(initial));
Motion::new(state)
}
This hook manages the animation state and provides a clean API for components.
-
Browser Compatibility
- Challenge: Different browsers handle requestAnimationFrame differently
- Solution: Created a platform-agnostic timing system with fallbacks
-
State Management
- Challenge: Keeping animations smooth during React-style rerenders
- Solution: Implemented a separate animation loop that runs independent of the render cycle
-
Type System Integration
- Challenge: Making animations work with any type while maintaining type safety
- Solution: Created the
Animatable
trait with safe defaults:
pub trait Animatable: 'static + Copy + Send + Sync {
fn zero() -> Self;
fn epsilon() -> f32;
fn magnitude(&self) -> f32;
fn scale(&self, factor: f32) -> Self;
fn add(&self, other: &Self) -> Self;
fn sub(&self, other: &Self) -> Self;
fn interpolate(&self, target: &Self, t: f32) -> Self;
}
The library supports complex 3D transformations, as demonstrated by this rotating cube example:
#[derive(Debug, Clone, Copy)]
struct Transform3D {
rotate_x: f32,
rotate_y: f32,
rotate_z: f32,
translate_x: f32,
translate_y: f32,
scale: f32,
}
#[component]
fn SwingingCube() -> Element {
let mut transform = use_motion(Transform3D::zero());
// Animate the cube with spring physics
transform.animate_to(
Transform3D::new(
PI / 3.0, // X rotation
PI / 2.0, // Y rotation
PI / 4.0, // Z rotation
2.0, // X translation
-1.0, // Y translation
1.2, // Scale
),
AnimationConfig::new(AnimationMode::Spring(Spring {
stiffness: 35.0,
damping: 5.0,
mass: 1.0,
velocity: 2.0,
}))
.with_loop(LoopMode::Infinite),
);
// ...rest of the implementation
}
Another example showcasing organic animations with multiple coordinated elements:
#[derive(Debug, Clone, Copy)]
struct PetalTransform {
rotate: f32,
scale: f32,
translate_x: f32,
translate_y: f32,
}
#[component]
fn AnimatedFlower() -> Element {
let mut petal_transform = use_motion(PetalTransform::zero());
let mut center_scale = use_motion(0.0f32);
// Animate petals blooming
petal_transform.animate_to(
PetalTransform::new(PI / 4.0, 1.2, 3.0, 3.0),
AnimationConfig::new(AnimationMode::Spring(Spring {
stiffness: 60.0,
damping: 8.0,
mass: 0.5,
velocity: 1.0,
}))
.with_loop(LoopMode::Infinite),
);
// ...rest of the implementation
}
-
Performance Enhancements
- Implementation of batch animation updates
- WebAssembly-specific optimizations
- Advanced caching strategies
-
API Evolution
- Direct DOM style manipulation integration
- Enhanced gesture support
- Animation composition utilities
dioxus-motion represents a significant step forward for animation capabilities in the Rust ecosystem. While we've achieved our initial goals of creating a robust, physics-based animation system, this is just the beginning. We're excited to see how the community will use and enhance these tools to create more dynamic and engaging user interfaces.
Ready to start animating? Visit our GitHub repository to contribute or try it out!