# v0.8.0 Features
> DrawSVG, MorphPath, Inertia, DragState, Advanced Easings, and WASM-DOM Plugins
This page documents the features added in spanda 0.8.0. For the advanced parametric easings, see [easing.md](easing.md). For the WASM-DOM plugins (FLIP, SplitText, ScrollSmoother, Draggable, Observer), see [integrations.md](integrations.md).
---
## DrawSVG
Thin convenience helpers for the classic SVG stroke-dashoffset draw-on/draw-off effect. These return a `TweenBuilder<f32>` so you chain `.duration()`, `.easing()`, `.build()` as usual.
### API
| `draw_on(path_length)` | `TweenBuilder<f32>` | Tweens from `path_length` to `0.0` (draws the stroke on) |
| `draw_on_reverse(path_length)` | `TweenBuilder<f32>` | Tweens from `0.0` to `path_length` (erases the stroke) |
### Example
```rust
use spanda::svg_draw::{draw_on, draw_on_reverse};
use spanda::easing::Easing;
use spanda::traits::Update;
// Draw on a path with total length 300px
let mut tween = draw_on(300.0)
.duration(1.5)
.easing(Easing::EaseInOutCubic)
.build();
// Each frame:
tween.update(dt);
let dash_offset = tween.value();
// Apply: element.style.strokeDashoffset = dash_offset
// Reverse: erase the stroke
let mut erase = draw_on_reverse(300.0)
.duration(0.8)
.easing(Easing::EaseInCubic)
.build();
```
### How It Works
Set `stroke-dasharray` to the total path length, then animate `stroke-dashoffset` from `path_length` (fully hidden) to `0.0` (fully visible). This is the same technique used by CSS `stroke-dashoffset` animations and GSAP's DrawSVGPlugin.
---
## MorphPath
Point-by-point shape morphing with automatic resampling. Interpolates between two sets of 2D points, creating smooth shape transitions.
### API
| `MorphPath::new(from, to)` | Create a `MorphPathBuilder`. Auto-resamples if point counts differ. |
| `.duration(seconds)` | Set morph duration (default: 1.0) |
| `.easing(easing)` | Set easing curve (default: Linear) |
| `.build()` | Build the `MorphPath` |
| `.value()` | Current interpolated `Vec<[f32; 2]>` |
| `.progress()` | Raw progress 0.0..=1.0 |
| `.is_complete()` | Whether the morph has completed |
| `.seek(t)` | Jump to a specific progress |
| `.reset()` | Reset to beginning |
| `resample(points, target_count)` | Utility: resample a polyline to evenly-spaced points |
### Example
```rust
use spanda::morph::{MorphPath, resample};
use spanda::easing::Easing;
use spanda::traits::Update;
// Triangle -> Square morph
let triangle = vec![[0.0, 0.0], [50.0, 100.0], [100.0, 0.0]];
let square = vec![[0.0, 0.0], [0.0, 100.0], [100.0, 100.0], [100.0, 0.0]];
// Point counts differ — the builder auto-resamples the shorter shape
let mut morph = MorphPath::new(triangle, square)
.duration(1.0)
.easing(Easing::EaseInOutCubic)
.build();
// Each frame:
morph.update(dt);
let current_shape: Vec<[f32; 2]> = morph.value();
// Render current_shape as a polygon
```
### Resampling
When the source and target shapes have different point counts, the builder automatically resamples the shorter one using arc-length parameterisation. You can also resample manually:
```rust
use spanda::morph::resample;
let rough = vec![[0.0, 0.0], [100.0, 50.0], [200.0, 0.0]];
let smooth = resample(&rough, 20); // 20 evenly-spaced points along the polyline
```
---
## Inertia
Friction-based deceleration physics. An object coasts to a stop under exponential velocity decay — no target position, just gradual deceleration. Think scroll momentum or fling gestures.
### InertiaConfig
| `InertiaConfig::default_flick()` | 0.05 | Moderate deceleration — general-purpose |
| `InertiaConfig::heavy()` | 0.02 | Slow deceleration, long coast |
| `InertiaConfig::snappy()` | 0.15 | Fast deceleration, quick stop |
### Inertia (1D)
Single-axis friction deceleration:
```rust
use spanda::inertia::{Inertia, InertiaConfig};
use spanda::traits::Update;
let mut inertia = Inertia::new(InertiaConfig::default_flick())
.with_velocity(500.0) // pixels per second
.with_position(100.0); // starting position
// Each frame:
while !inertia.is_settled() {
inertia.update(1.0 / 60.0);
let pos = inertia.position();
let vel = inertia.velocity();
// render at pos
}
```
### InertiaN (Multi-Dimensional)
For 2D/3D fling gestures, `InertiaN<T>` uses the `SpringAnimatable` trait:
```rust
use spanda::inertia::{InertiaN, InertiaConfig};
use spanda::traits::Update;
let mut inertia = InertiaN::new(InertiaConfig::default_flick(), [100.0_f32, 200.0])
.with_velocity([300.0, -150.0]);
inertia.update(1.0 / 60.0);
let pos: [f32; 2] = inertia.position();
// pos has moved in the direction of the initial velocity
```
### Physics
Velocity decays via frame-rate independent exponential formula:
```
velocity *= (1.0 - friction).powf(dt * 60.0)
```
This ensures identical behaviour whether running at 30fps, 60fps, or 144fps. The animation settles when velocity drops below `epsilon` (default: 0.1).
### Kick
Re-apply velocity to a settled inertia (e.g., a second fling gesture):
```rust
inertia.kick(800.0); // new velocity impulse, restarts settling
```
---
## DragState
Pure-math pointer drag tracker. No DOM dependency — works everywhere. Tracks position, computes smoothed velocity via exponential moving average, and applies constraints.
### API
| `DragState::new()` | Create at origin, no constraints |
| `.with_position(pos)` | Builder: set initial position |
| `.with_constraints(c)` | Builder: set constraints |
| `.on_pointer_down(x, y)` | Start drag from pointer position |
| `.on_pointer_move(x, y, dt)` | Move during drag (provide dt for velocity) |
| `.on_pointer_up()` | End drag, returns `InertiaN<[f32; 2]>` for momentum |
| `.position()` | Current `[f32; 2]` position |
| `.velocity()` | Smoothed `[f32; 2]` velocity |
| `.is_dragging()` | Whether the pointer is held |
### DragConstraints
| `bounds` | `Option<[f32; 4]>` | Bounding rect: `[min_x, min_y, max_x, max_y]` |
| `axis_lock` | `Option<DragAxis>` | Lock to `DragAxis::X` or `DragAxis::Y` |
| `snap_to_grid` | `Option<[f32; 2]>` | Snap position to grid: `[grid_x, grid_y]` |
### Example
```rust
use spanda::drag::{DragState, DragConstraints, DragAxis};
use spanda::traits::Update;
let mut drag = DragState::new()
.with_position([100.0, 100.0])
.with_constraints(DragConstraints {
bounds: Some([0.0, 0.0, 500.0, 500.0]),
axis_lock: None,
snap_to_grid: Some([20.0, 20.0]),
..Default::default()
});
// On pointer down:
drag.on_pointer_down(150.0, 150.0);
// On pointer move (each frame):
drag.on_pointer_move(170.0, 160.0, 1.0 / 60.0);
let pos = drag.position(); // snapped to [160.0, 160.0]
// On pointer up — get momentum for fling:
let mut inertia = drag.on_pointer_up();
// The inertia carries the drag's velocity:
inertia.update(1.0 / 60.0);
let fling_pos = inertia.position();
```
### Velocity Smoothing
Velocity is tracked using an exponential moving average (EMA):
```
velocity = 0.8 * instantaneous_velocity + 0.2 * previous_velocity
```
This prevents velocity spikes from noisy pointer events while still being responsive.
### DOM Binding
For web apps, use `Draggable` (requires `feature = "wasm-dom"`) which wraps `DragState` with DOM pointer event listeners. See [integrations.md](integrations.md#draggable).
---
## Advanced Easings
Five new parametric easing variants added in v0.8.0:
| `RoughEase { strength, points, seed }` | Deterministic noise overlay — hand-drawn feel |
| `SlowMo { ratio, power, yoyo_mode }` | Slow-fast-slow — cinematic movements |
| `ExpoScale { start_scale, end_scale }` | Perceptual exponential correction — zoom animations |
| `Wiggle { frequency, amplitude }` | Sinusoidal oscillation — vibration / shake |
| `CustomBounce { strength, squash }` | Parametric bounce with decay and squash |
See the full [Easing documentation](easing.md#advanced-parametric-easings) for parameters, examples, and usage recommendations.
---
## WASM-DOM Plugins
Five DOM interaction plugins requiring `feature = "wasm-dom"`:
| Observer | `integrations::observer` | Unified pointer/touch/mouse event normaliser |
| FLIP | `integrations::flip` | First-Last-Invert-Play layout animations |
| SplitText | `integrations::split_text` | Character/word splitting + staggered timelines |
| ScrollSmoother | `integrations::scroll_smoother` | Spring-driven smooth scroll |
| Draggable | `integrations::draggable` | DOM pointer binding for DragState |
See the full [Integrations Guide](integrations.md#wasm-dom-plugins) for API details and examples.