Compare commits

...

8 Commits

Author SHA1 Message Date
Matthew Griffith
22fb914de9 Redo basic example 2024-05-03 15:44:44 -04:00
Matthew Griffith
4d296736de Animate rotations using a 1000 multiplier 2024-05-03 15:44:30 -04:00
Matthew Griffith
57605f4380 Merge branch 'v2' of https://github.com/mdgriffith/elm-animator into v2 2024-05-03 15:43:12 -04:00
Matthew Griffith
b19ce307c2
Merge pull request #27 from CharlonTank/v2
Fix steps resetting to initial states + Added an example for animating a timeline
2024-05-03 15:41:06 -04:00
Charlon
4e100456ed refacto 2024-05-03 22:46:04 +07:00
Matthew Griffith
e7a0b4bbb8 Fix scale group lookup (e.g. when we set scale, have it set all the scale channels 2024-05-03 11:35:47 -04:00
Charlon
2049391907 Remove stop button and add restart button 2024-05-03 22:06:16 +07:00
Charlon
6f15554870 Fix steps resetting to initial states + Added an example for animating a timeline 2024-05-03 21:16:33 +07:00
8 changed files with 332 additions and 87 deletions

View File

@ -3,81 +3,95 @@ module Basic exposing (main)
{-| -}
import Animator
import Browser
import Animator.Transition as Transition
import Color
import Html exposing (..)
import Html exposing (Html)
import Html.Attributes as Attr
import Html.Events as Events
import Time
type alias Model =
{}
main =
Browser.sandbox
{ init =
{}
, view = view
, update = update
}
type Msg
= Check Bool
update : Msg -> Model -> Model
update msg model =
case msg of
Check newChecked ->
model
view : Model -> Html Msg
view model =
div
[ Attr.class "root"
Html.div
[ Attr.style "display" "flex"
, Attr.style "flex-direction" "column"
, Attr.style "gap" "40px"
, Attr.style "width" "100vw"
, Attr.style "height" "100vh"
, Attr.style "justify-content" "center"
, Attr.style "align-items" "center"
]
[ stylesheet
, box (Animator.spinning (Animator.ms 2000))
, box (Animator.spinning (Animator.ms 1500))
, box (Animator.spinning (Animator.ms 1000))
, title "Some built-in animations"
, Html.div [ Attr.style "display" "flex", Attr.style "gap" "40px" ]
[ box (Animator.spinning (Animator.ms 500))
, box (Animator.pulsing (Animator.ms 2000))
, box (Animator.pinging (Animator.ms 1000))
, box (Animator.bouncing (Animator.ms 1000) 40)
]
, title "Animating with springs"
, Html.div [ Attr.style "display" "flex", Attr.style "gap" "40px" ]
[ box (spinning { wobble = 0, duration = Animator.ms 1000 })
, box (spinning { wobble = 0.5, duration = Animator.ms 1000 })
, box (spinning { wobble = 1, duration = Animator.ms 1000 })
]
]
box animation =
Animator.div animation
[ Attr.class "box"
title : String -> Html msg
title text =
Html.h1
[ Attr.style "font-size" "2em"
, Attr.style "font-weight" "bold"
]
[ Html.text text ]
spinning : { wobble : Float, duration : Animator.Duration } -> Animator.Animation
spinning { wobble, duration } =
Animator.keyframes
[ Animator.loop
[ Animator.set
[ Animator.rotation 0
]
, Animator.step duration
[ Animator.rotation 0.5
|> Animator.withTransition
(Transition.spring
{ wobble = wobble
, quickness = 0
}
)
]
]
]
[]
stylesheet : Html msg
stylesheet =
Html.node "style"
[]
[ text """@import url('https://fonts.googleapis.com/css?family=Roboto&display=swap');
[ Html.text """
.root {
display: flex;
}
.root > * {
margin-right: 20px;
body {
width: 100vw;
height: 100vh;
font-family: "EB Garamond", serif;
}
.box {
box-sizing: border-box;
position: relative;
width: 100px;
height: 100px;
border-radius: 20%;
background-color:red;
border-color: black;
border-style: solid;
}
"""
]
box animation =
Animator.div animation
[ Attr.class "box"
, Attr.style "box-sizing" "border-box"
, Attr.style "position" "relative"
, Attr.style "width" "100px"
, Attr.style "height" "100px"
, Attr.style "border-radius" "20%"
, Attr.style "background-color" "red"
, Attr.style "border-color" "black"
, Attr.style "border-style" "solid"
]
[]

View File

@ -1,7 +1,7 @@
{
"type": "application",
"source-directories": [
".",
"src",
"../src"
],
"elm-version": "0.19.1",
@ -9,33 +9,35 @@
"direct": {
"avh4/elm-color": "1.0.0",
"elm/browser": "1.0.2",
"elm/core": "1.0.2",
"elm/core": "1.0.5",
"elm/html": "1.0.0",
"elm/json": "1.1.3",
"elm/svg": "1.0.1",
"elm/time": "1.0.0",
"elm/url": "1.0.0",
"ianmackenzie/elm-geometry": "2.0.0",
"ianmackenzie/elm-units": "2.2.0",
"terezka/line-charts": "2.0.0"
"ianmackenzie/elm-geometry": "4.0.0",
"ianmackenzie/elm-units": "2.10.0",
"mdgriffith/elm-bezier": "1.0.0",
"terezka/line-charts": "2.0.2"
},
"indirect": {
"debois/elm-dom": "1.3.0",
"elm/parser": "1.1.0",
"elm/virtual-dom": "1.0.2",
"elm/random": "1.0.0",
"elm/virtual-dom": "1.0.3",
"ianmackenzie/elm-1d-parameter": "1.0.1",
"ianmackenzie/elm-float-extra": "1.1.0",
"ianmackenzie/elm-interval": "2.0.0",
"ianmackenzie/elm-triangular-mesh": "1.0.2",
"ianmackenzie/elm-units-interval": "1.0.0",
"justinmimbs/date": "3.2.0",
"justinmimbs/time-extra": "1.1.0",
"myrho/elm-round": "1.0.4",
"ryannhg/date-format": "2.3.0"
"ianmackenzie/elm-interval": "3.1.0",
"ianmackenzie/elm-triangular-mesh": "1.1.0",
"ianmackenzie/elm-units-interval": "3.2.0",
"justinmimbs/date": "4.1.0",
"justinmimbs/time-extra": "1.2.0",
"myrho/elm-round": "1.0.5",
"ryan-haskell/date-format": "1.0.0"
}
},
"test-dependencies": {
"direct": {},
"indirect": {}
}
}
}

View File

@ -0,0 +1,92 @@
module AnimationX exposing (main)
import Animator
import Animator.Timeline
import Animator.Transition
import Animator.Value
import Browser
import Browser.Events
import Html
import Html.Events
import Time
main : Program () Model Msg
main =
Browser.element
{ init = always init
, update = update
, subscriptions = subscriptions
, view = view
}
type alias Model =
{ timeline : Animator.Timeline.Timeline Float }
type Msg
= NewPosix Time.Posix
| Start
init : ( Model, Cmd Msg )
init =
let
initialTimeline : Animator.Timeline.Timeline Float
initialTimeline =
Animator.Timeline.init 10
queuedSteps : List (Animator.Timeline.Step Float)
queuedSteps =
[ Animator.Timeline.transitionTo (Animator.ms 1000) 100
, Animator.Timeline.transitionTo (Animator.ms 1000) 50
, Animator.Timeline.transitionTo (Animator.ms 1000) 5
]
timelineWithSteps : Animator.Timeline.Timeline Float
timelineWithSteps =
Animator.Timeline.scale 3 <| Animator.Timeline.queue queuedSteps initialTimeline
in
( { timeline = timelineWithSteps }
, Cmd.none
)
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
NewPosix posix ->
( { model | timeline = Animator.Timeline.update posix model.timeline }, Cmd.none )
Start ->
init
subscriptions : Model -> Sub Msg
subscriptions model =
if Animator.Timeline.isRunning model.timeline then
Browser.Events.onAnimationFrame NewPosix
else
Sub.none
view : Model -> Html.Html Msg
view model =
let
positionStandard : Float
positionStandard =
Animator.Value.float model.timeline Animator.Value.to
positionLinear : Float
positionLinear =
Animator.Value.float model.timeline (Animator.Value.withTransition Animator.Transition.linear << Animator.Value.to)
in
Html.div []
[ Html.div []
[ Html.div [] [ Html.text ("Position Standard: " ++ String.fromFloat positionStandard) ]
, Html.div [] [ Html.text ("Position Linear: " ++ String.fromFloat positionLinear) ]
]
, Html.button [ Html.Events.onClick Start ] [ Html.text "Re-Start" ]
]

View File

@ -0,0 +1,83 @@
module Basic exposing (main)
{-| -}
import Animator
import Browser
import Color
import Html exposing (..)
import Html.Attributes as Attr
import Html.Events as Events
import Time
type alias Model =
{}
main =
Browser.sandbox
{ init =
{}
, view = view
, update = update
}
type Msg
= Check Bool
update : Msg -> Model -> Model
update msg model =
case msg of
Check newChecked ->
model
view : Model -> Html Msg
view model =
div
[ Attr.class "root"
]
[ stylesheet
, box (Animator.spinning (Animator.ms 2000))
, box (Animator.spinning (Animator.ms 1500))
, box (Animator.spinning (Animator.ms 1000))
]
box animation =
Animator.div animation
[ Attr.class "box"
]
[]
stylesheet : Html msg
stylesheet =
Html.node "style"
[]
[ text """@import url('https://fonts.googleapis.com/css?family=Roboto&display=swap');
.root {
display: flex;
}
.root > * {
margin-right: 20px;
}
.box {
box-sizing: border-box;
position: relative;
width: 100px;
height: 100px;
border-radius: 20%;
background-color:red;
border-color: black;
border-style: solid;
}
"""
]

View File

@ -168,13 +168,14 @@ scaleZ s =
InternalAnim.Css.Props.float
{-| -}
{-| Given as 'turns'.
-}
rotation : Float -> Attribute
rotation n =
Css.Prop
InternalAnim.Css.Props.ids.rotation
"rotate"
(Move.to n)
(Move.to (1000 * n))
(InternalAnim.Css.Props.turns zAxis)
@ -184,7 +185,7 @@ rotationAround axis n =
Css.Prop
InternalAnim.Css.Props.ids.rotation
"rotate"
(Move.to n)
(Move.to (1000 * n))
(InternalAnim.Css.Props.turns axis)
@ -605,6 +606,8 @@ spinning dur =
]
, step dur
[ rotation 1
|> withTransition
Animator.Transition.linear
]
]
]
@ -613,13 +616,21 @@ spinning dur =
{-| -}
pulsing : Duration -> Animation
pulsing dur =
let
half =
dur
|> Quantity.divideBy 2
in
keyframes
[ loop
[ set
[ opacity 1
]
, step dur
[ opacity 0.5
, step half
[ opacity 0.4
]
, step half
[ opacity 1
]
]
]
@ -661,11 +672,11 @@ pinging dur =
keyframes
[ loop
[ set
[ rotation 0
[ scale 1
, opacity 1
]
, step dur
[ rotation 1
[ scale 1.2
, opacity 0
]
]
@ -688,7 +699,7 @@ import Html.Attributes
Anim.div
(Anim.onTimeline model.timeline
(\state ->
if state.open
if state.open then
[ Anim.opacity 1
]

View File

@ -99,9 +99,7 @@ movement timeline lookup =
Timeline.startTime target
isHappening =
(Time.thisAfterOrEqualThat now startTransition
&& Time.thisBeforeOrEqualThat now arrived
)
Time.thisAfterOrEqualThat now startTransition
|| (List.isEmpty future
&& Time.thisAfterThat now interruptedOrEnd
)

View File

@ -1237,18 +1237,24 @@ getVectorStepAt groupId dur trans seqLevel stepLevel props =
{ x =
getTransformSequenceValueAt seqLevel
stepLevel
(Props.groupToCompoundId groupId)
x
props
Nothing
, y =
getTransformSequenceValueAt seqLevel
stepLevel
(Props.groupToCompoundId groupId)
y
props
Nothing
, z =
getTransformSequenceValueAt seqLevel
stepLevel
(Props.groupToCompoundId groupId)
z
props
Nothing
}
@ -1331,31 +1337,44 @@ getTransformStepAt dur trans seqLevel stepLevel props =
{ x =
getTransformSequenceValueAt seqLevel
stepLevel
Nothing
Props.ids.x
props
Nothing
, y =
getTransformSequenceValueAt seqLevel
stepLevel
Nothing
Props.ids.y
props
Nothing
, scale =
getTransformSequenceValueAt seqLevel
stepLevel
Nothing
Props.ids.scale
props
Nothing
, rotation =
getTransformSequenceValueAt seqLevel
stepLevel
Nothing
Props.ids.rotation
props
Nothing
}
getTransformSequenceValueAt : Int -> Int -> Props.Id -> List Prop -> Float
getTransformSequenceValueAt seqLevel stepLevel targetId props =
getTransformSequenceValueAt : Int -> Int -> Maybe Props.Id -> Props.Id -> List Prop -> Maybe Float -> Float
getTransformSequenceValueAt seqLevel stepLevel maybeDefaultId targetId props defaultValue =
case props of
[] ->
Props.defaultPosition targetId
case defaultValue of
Nothing ->
Props.defaultPosition targetId
Just default ->
default
(Prop id name move _) :: remain ->
if id - targetId == 0 then
@ -1374,10 +1393,35 @@ getTransformSequenceValueAt seqLevel stepLevel targetId props =
stepValue
else
getTransformSequenceValueAt seqLevel stepLevel targetId remain
case maybeDefaultId of
Nothing ->
getTransformSequenceValueAt seqLevel stepLevel maybeDefaultId targetId remain defaultValue
Just defaultId ->
if id - defaultId == 0 then
let
newDefaultValue =
case move of
Move.Pos _ v seq ->
case getAt seqLevel seq of
Nothing ->
v
Just (Move.Sequence _ _ _ seqSteps) ->
case getAt stepLevel seqSteps of
Nothing ->
v
Just (Move.Step _ _ stepValue) ->
stepValue
in
getTransformSequenceValueAt seqLevel stepLevel Nothing targetId remain (Just newDefaultValue)
else
getTransformSequenceValueAt seqLevel stepLevel maybeDefaultId targetId remain defaultValue
(ColorProp name movement) :: remain ->
getTransformSequenceValueAt seqLevel stepLevel targetId remain
getTransformSequenceValueAt seqLevel stepLevel maybeDefaultId targetId remain defaultValue
getAt : Int -> List a -> Maybe a

View File

@ -140,7 +140,7 @@ hashFormat form num =
String.fromInt (round num) ++ "px"
Turns vec ->
Hash.float num
String.fromInt (round num)
TranslateX ->
"translateX(" ++ String.fromInt (round num) ++ "px)"
@ -159,7 +159,8 @@ format form num =
String.fromInt (round num) ++ "px"
Turns vec ->
vectorToCssString vec ++ " " ++ String.fromFloat (roundFloat num) ++ "turn"
-- Number here is 1/1000 of a turn
vectorToCssString vec ++ " " ++ String.fromFloat (num / 1000) ++ "turn"
TranslateX ->
"translateX(" ++ String.fromInt (round num) ++ "px)"