Module

Hylograph.Simulation.Core.Tick

Package
purescript-hylograph-simulation-core
Repository
afcondon/purescript-hylograph-simulation-core

Tick-Driven Transitions

Primitives for animating transitions using simulation ticks rather than CSS. This is the simulation-aware analog to d3-transition.

Key insight: Force simulations already have a tick loop. We can use that same loop to drive enter/exit/update animations, giving us:

  • Predictable, debuggable behavior
  • No CSS timing coordination
  • Pure PureScript interpolation

Usage:

import Hylograph.Simulation.Core.Tick as T

-- In your state
type State = { entering :: Map String Progress, exiting :: Array (Transitioning Node) }

-- In tick handler
onTick state = do
  let { active: stillEntering } = T.tickProgressMap 0.025 state.entering
  let { active: stillExiting } = T.tickTransitions 0.025 state.exiting
  -- render with interpolated values

-- In render
radius = case enterProgress of
  Just p -> T.lerp 20.0 5.0 (T.easeOut p)
  Nothing -> 5.0

#Progress Source

type Progress = Number

Progress from 0.0 (start) to 1.0 (complete)

#TickDelta Source

type TickDelta = Number

Amount to advance progress each tick At 60fps: 0.025 ≈ 40 ticks ≈ 0.67 seconds At 60fps: 0.020 ≈ 50 ticks ≈ 0.83 seconds At 60fps: 0.015 ≈ 67 ticks ≈ 1.1 seconds

#Transitioning Source

type Transitioning a = { item :: a, progress :: Progress }

An item in transition, carrying its state and progress

#Easing Source

type Easing = Progress -> Progress

Easing function: maps linear progress to eased progress

#tickProgressMap Source

tickProgressMap :: forall k. Ord k => TickDelta -> Map k Progress -> { active :: Map k Progress, completed :: Array k }

Advance all progress values in a Map, partitioning into active and completed

let { active, completed } = tickProgressMap 0.025 enteringNodes
-- active: nodes still animating
-- completed: keys that just finished (for cleanup, callbacks, etc.)

#startProgress Source

startProgress :: forall k. Ord k => Array k -> Map k Progress -> Map k Progress

Start tracking progress for new keys (from 0.0)

#startProgressFrom Source

startProgressFrom :: forall k. Ord k => Progress -> Array k -> Map k Progress -> Map k Progress

Start tracking progress for new keys from a specific value

#tickTransitions Source

tickTransitions :: forall a. TickDelta -> Array (Transitioning a) -> { active :: Array (Transitioning a), completed :: Array a }

Advance all transitions, partitioning into active and completed

let { active, completed } = tickTransitions 0.025 exitingNodes
-- active: items still animating out
-- completed: items that finished (now safe to remove from DOM)

#startTransitions Source

startTransitions :: forall a. Array a -> Array (Transitioning a)

Wrap items as transitions starting at progress 0.0

#startTransitionsFrom Source

startTransitionsFrom :: forall a. Progress -> Array a -> Array (Transitioning a)

Wrap items as transitions starting at a specific progress

#lerp Source

lerp :: Number -> Number -> Progress -> Number

Linear interpolation between two numbers

lerp 0.0 100.0 0.5  -- 50.0
lerp 20.0 5.0 1.0   -- 5.0 (shrink from 20 to 5)

#lerpClamped Source

lerpClamped :: Number -> Number -> Progress -> Number

Linear interpolation with progress clamped to [0, 1]

#lerpInt Source

lerpInt :: Int -> Int -> Progress -> Int

Linear interpolation for integers

#linear Source

linear :: Easing

No easing (linear)

#easeIn Source

easeIn :: Easing

Ease in (slow start, fast end) - alias for easeInQuad

#easeOut Source

easeOut :: Easing

Ease out (fast start, slow end) - alias for easeOutQuad

#easeInOut Source

easeInOut :: Easing

Ease in-out (slow start and end) - alias for easeInOutQuad

#easeInQuad Source

easeInQuad :: Easing

Quadratic ease in: t²

#easeOutQuad Source

easeOutQuad :: Easing

Quadratic ease out: 1 - (1-t)²

#easeInOutQuad Source

easeInOutQuad :: Easing

Quadratic ease in-out

#easeInCubic Source

easeInCubic :: Easing

Cubic ease in: t³

#easeOutCubic Source

easeOutCubic :: Easing

Cubic ease out: 1 - (1-t)³

#easeInOutCubic Source

easeInOutCubic :: Easing

Cubic ease in-out

#withEasing Source

withEasing :: forall a. Easing -> (Progress -> a) -> Progress -> a

Apply easing to an interpolation function

-- Ease-out shrink from 20 to 5
radius = withEasing easeOut (lerp 20.0 5.0) progress

#ticksForDuration Source

ticksForDuration :: Int -> TickDelta

Calculate tick delta for a desired duration at assumed 60fps

ticksForDuration 1000  -- 0.0167 (1 second at 60fps)
ticksForDuration 500   -- 0.0333 (0.5 seconds)