This module can be used to create and track oscillating values. A value like that will change over time in a way similar to a center of mass attached to an anchored spring. See the ReadMe for an overview, demos and use cases.
Let's say we are creating a program that animates the position of an element towards the last click position.
type alias Model =
{ x : Spring
, y : Spring
}
create : { strength : Basics.Float, dampness : Basics.Float } -> Spring
Create a spring with the value and target set to 0
.
Let's say we are creating a program that animates the position of an element toward last click position.
type alias Model =
{ x : Spring
, y : Spring
}
init : Flags -> ( Model, Cmd msg )
init flags =
( { x = Spring.create { strength = 20, dampness = 4.5 }
, y = Spring.create { strength = 20, dampness = 4.5 }
}
, Cmd.none
)
Note that the dampness
is a logarythmic value: dampness = 5
results in damping ratio 25 times higher than dampness = 1
.
setTarget : Basics.Float -> Spring -> Spring
Set a new target (relaxed value) for the spring.
The current value and its velocity will be preserved, so the spring will smoothly transition its movement. Typically you would set a target in response to an event, e.g.:
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Click x y ->
( { model
| x = Spring.setTarget x model.x
, y = Spring.setTarget y model.y
}
, Cmd.none
)
animate : Basics.Float -> Spring -> Spring
Gently update the internal state of the spring
Typically you would do it in response to an animation frame message, like this:
subscriptions : Model -> Sub Msg
subscriptions model =
Browser.Events.onAnimationFrameDelta Animate
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
Animate delta ->
( { model
| x = Spring.animate delta model.x
, y = Spring.animate delta model.y
}
, Cmd.none
)
Note: that if the frame rate is low, the time passage experienced by the spring will deviate from the real time. See the comment in source code for more details.
jumpTo : Basics.Float -> Spring -> Spring
Forcefully set the value and interrupt the motion.
The target will be preserved, but velocity will be set to 0. It is useful when you set the spring for the first time (e.g. in the init
function) or you want to reset the animation.
init : Flags -> ( Model, Cmd msg )
init flags =
( { x =
Spring.create 20 20
|> Spring.setTarget 200
|> Spring.jumpTo 200
, y =
Spring.create 20 20
|> Spring.setTarget 200
|> Spring.jumpTo 200
}
, Cmd.none
)
value : Spring -> Basics.Float
Measure the current value of the spring.
Typically you want to access it in the view function, like this:
Element.el
[ Element.width (Element.px 20)
, Element.height (Element.px 20)
, Font.size 30
, model.x
|> Spring.value
|> Element.moveRight
, model.y
|> Spring.value
|> Element.moveDown
]
(Element.text "\u{1F991}")
Above I use Elm UI Elements and Attributes, but it's not difficult to implement the same behaviour using CSS transformations. The value of a Spring is just a
Float
- you can do with it whatever you need.
target : Spring -> Basics.Float
Get current target of a spring
Can be useful to see where the spring is going. Maybe you want to display something there?
atRest : Spring -> Basics.Bool
Check if the spring is at rest
It indicates that the spring has reached its target and no motion is going on. Maybe you want to unsubscribe from animation frames? Or remove an element?
subscriptions : Model -> Sub Msg
subscriptions model =
if Spring.atRest model.x && Spring.atRest model.y then
Sub.none
else
Browser.Events.onAnimationFrameDelta Animate