mirror of
https://github.com/mdgriffith/elm-animator.git
synced 2024-09-11 12:47:52 +03:00
Compare commits
6 Commits
caa5520580
...
016ede0536
Author | SHA1 | Date | |
---|---|---|---|
|
016ede0536 | ||
|
1bb5bf8b2c | ||
|
7531fc0d3f | ||
|
4d150d82c1 | ||
|
c45109cf96 | ||
|
f9b5d0352e |
@ -294,7 +294,6 @@ keyframes steps =
|
||||
Time.absolute (Time.millisToPosix 1)
|
||||
|
||||
firstEventTime =
|
||||
-- Time.absolute (Time.millisToPosix 2)
|
||||
imminent
|
||||
|
||||
( firstOccurring, remaining ) =
|
||||
|
@ -6,7 +6,7 @@ module Animator.Timeline exposing
|
||||
, Step, wait, transitionTo
|
||||
, scale, delay
|
||||
, current, previous, upcoming, upcomingWith, arrived, arrivedAt, arrivedAtWith
|
||||
, Duration
|
||||
, Duration, hasChanges
|
||||
)
|
||||
|
||||
{-|
|
||||
@ -159,6 +159,16 @@ scale factor (Timeline.Timeline details) =
|
||||
{ details | scale = min 5 (max 0.1 factor) }
|
||||
|
||||
|
||||
{-| The proportion (number between 0 and 1) of progress between the last state and the new one.
|
||||
|
||||
Once we arrive at a new state, this value will be 1 until we start another transition.
|
||||
|
||||
-}
|
||||
progress : Timeline state -> Float
|
||||
progress =
|
||||
Timeline.progress
|
||||
|
||||
|
||||
{-| Get the current `state` of the timeline.
|
||||
|
||||
This value will switch to a new value when a transition begins.
|
||||
@ -310,7 +320,7 @@ to duration ev timeline =
|
||||
{-| Interrupt what's currently happening with a new list.
|
||||
-}
|
||||
interrupt : List (Step state) -> Timeline state -> Timeline state
|
||||
interrupt steps (Timeline.Timeline tl) =
|
||||
interrupt steps ((Timeline.Timeline tl) as fullTimeline) =
|
||||
Timeline.Timeline
|
||||
{ tl
|
||||
| running = True
|
||||
@ -323,12 +333,45 @@ interrupt steps (Timeline.Timeline tl) =
|
||||
-- **NOTE** - if we recieve a new interruption, we throw away the existing one!
|
||||
-- This was leading to issues when the same event was added to the `interrupted` queue
|
||||
-- multiple times in before being scheduled.
|
||||
-- So, I imagine it does make sense to dedup these
|
||||
-- But does it ALWAYS make sense to replace the currently scheduled interruption?
|
||||
[ List.foldl stepsToEvents schedule otherSteps ]
|
||||
-- Also
|
||||
-- If we're returning to a previous state while enroute to a new state,
|
||||
-- we can "discount" the duration to return.
|
||||
let
|
||||
discountedSchedule =
|
||||
if Duration.isZero (scheduleDelay schedule) && previous fullTimeline == currentScheduleTarget schedule then
|
||||
let
|
||||
transitionProgress =
|
||||
Timeline.transitionProgress fullTimeline
|
||||
in
|
||||
schedule
|
||||
|> scaleScheduleDurationBy (Maybe.withDefault 1 <| List.maximum transitionProgress)
|
||||
|
||||
else
|
||||
schedule
|
||||
in
|
||||
[ List.foldl stepsToEvents discountedSchedule otherSteps ]
|
||||
}
|
||||
|
||||
|
||||
scaleScheduleDurationBy : Float -> Timeline.Schedule state -> Timeline.Schedule state
|
||||
scaleScheduleDurationBy factor (Timeline.Schedule currentScheduleDelay (Timeline.Event dur checkpoint dwell) events) =
|
||||
Timeline.Schedule
|
||||
currentScheduleDelay
|
||||
(Timeline.Event (Duration.scale factor dur) checkpoint dwell)
|
||||
events
|
||||
|
||||
|
||||
scheduleDelay : Timeline.Schedule state -> Time.Duration
|
||||
scheduleDelay (Timeline.Schedule d _ _) =
|
||||
d
|
||||
|
||||
|
||||
currentScheduleTarget : Timeline.Schedule state -> state
|
||||
currentScheduleTarget (Timeline.Schedule _ (Timeline.Event _ target _) _) =
|
||||
target
|
||||
|
||||
|
||||
{-| -}
|
||||
initializeSchedule : Time.Duration -> List (Step state) -> Maybe ( Schedule state, List (Step state) )
|
||||
initializeSchedule waiting steps =
|
||||
case steps of
|
||||
@ -413,3 +456,14 @@ update =
|
||||
isRunning : Timeline state -> Bool
|
||||
isRunning (Timeline.Timeline tl) =
|
||||
tl.running
|
||||
|
||||
|
||||
{-| -}
|
||||
hasChanges : Timeline state -> Bool
|
||||
hasChanges (Timeline.Timeline tl) =
|
||||
case tl.queued of
|
||||
Nothing ->
|
||||
not (List.isEmpty tl.interruption)
|
||||
|
||||
Just _ ->
|
||||
True
|
||||
|
@ -112,7 +112,7 @@ movement timeline lookup =
|
||||
targetMovement =
|
||||
lookup (Timeline.getEvent target)
|
||||
in
|
||||
Move.transitionTo progress
|
||||
Move.at progress
|
||||
startTransition
|
||||
arrived
|
||||
targetMovement
|
||||
|
@ -202,7 +202,7 @@ toInitialProps props rendered =
|
||||
[] ->
|
||||
rendered
|
||||
|
||||
(Prop id name _ format) :: remaining ->
|
||||
(Prop id name movement format) :: remaining ->
|
||||
toInitialProps remaining
|
||||
(if Props.isTranslateId id then
|
||||
case rendered.translation of
|
||||
@ -214,14 +214,22 @@ toInitialProps props rendered =
|
||||
, name = name
|
||||
, format = format
|
||||
, sections = []
|
||||
, state = initVector 0
|
||||
, state = Props.initVectorState id (Move.init movement)
|
||||
}
|
||||
, scale = rendered.scale
|
||||
}
|
||||
|
||||
Just _ ->
|
||||
Just translation ->
|
||||
-- we've already initialized the transform
|
||||
rendered
|
||||
-- add the new prop to the list
|
||||
{ props = rendered.props
|
||||
, translation =
|
||||
Just
|
||||
{ translation
|
||||
| state = Props.updateVectorById id (Move.init movement) translation.state
|
||||
}
|
||||
, scale = rendered.scale
|
||||
}
|
||||
|
||||
else if Props.isScaleId id then
|
||||
case rendered.scale of
|
||||
@ -234,13 +242,20 @@ toInitialProps props rendered =
|
||||
, name = name
|
||||
, format = format
|
||||
, sections = []
|
||||
, state = initVector 1
|
||||
, state = Props.initVectorState id (Move.init movement)
|
||||
}
|
||||
}
|
||||
|
||||
Just _ ->
|
||||
Just scale ->
|
||||
-- we've already initialized the transform
|
||||
rendered
|
||||
{ props = rendered.props
|
||||
, scale =
|
||||
Just
|
||||
{ scale
|
||||
| state = Props.updateVectorById id (Move.init movement) scale.state
|
||||
}
|
||||
, translation = rendered.translation
|
||||
}
|
||||
|
||||
else
|
||||
let
|
||||
@ -684,7 +699,7 @@ toPropCurves2 lookup prev target now startTime endTime future cursor =
|
||||
rendered.sections
|
||||
, state =
|
||||
rendered.state
|
||||
|> Move.transitionTo progress
|
||||
|> Move.at progress
|
||||
startTime
|
||||
targetTime
|
||||
targetProp
|
||||
@ -732,11 +747,6 @@ toPropCurves2 lookup prev target now startTime endTime future cursor =
|
||||
getCommonTransformTransition
|
||||
targetProps
|
||||
Transition.standard
|
||||
|> Transition.withVelocities fastestVelocity
|
||||
-- If we do any transition smoothing
|
||||
-- we'll need to normalize this velocity too
|
||||
--Estimation.velocityAtTarget lookupState target future
|
||||
0
|
||||
|
||||
else
|
||||
getCommonTransformTransition
|
||||
|
@ -1,6 +1,6 @@
|
||||
module InternalAnim.Css.Props exposing
|
||||
( Id, ids, hash, default, defaultPosition, groups
|
||||
, isTranslateId, isScaleId
|
||||
, isTranslateId, isScaleId, initVector, initVectorState, updateVectorById
|
||||
, Format, format, float, int, px, turns
|
||||
, isGroup
|
||||
, VectorSlot(..), colorHash, groupToCompoundId, noId, transparent, vectorSlotToId, vectorToString
|
||||
@ -10,7 +10,7 @@ module InternalAnim.Css.Props exposing
|
||||
|
||||
@docs Id, ids, hash, default, defaultPosition, groups
|
||||
|
||||
@docs isTranslateId, isScaleId
|
||||
@docs isTranslateId, isScaleId, initVector, initVectorState, updateVectorById
|
||||
|
||||
@docs Format, format, float, int, px, turns
|
||||
|
||||
@ -65,6 +65,105 @@ type alias Vector =
|
||||
}
|
||||
|
||||
|
||||
type alias VectorState =
|
||||
{ x : Move.State
|
||||
, y : Move.State
|
||||
, z : Move.State
|
||||
}
|
||||
|
||||
|
||||
updateVectorById : Id -> val -> { x : val, y : val, z : val } -> { x : val, y : val, z : val }
|
||||
updateVectorById id val vec =
|
||||
case id of
|
||||
0 ->
|
||||
{ vec | x = val }
|
||||
|
||||
1 ->
|
||||
{ vec | y = val }
|
||||
|
||||
2 ->
|
||||
{ vec | z = val }
|
||||
|
||||
4 ->
|
||||
{ x = val, y = val, z = val }
|
||||
|
||||
5 ->
|
||||
{ vec | x = val }
|
||||
|
||||
6 ->
|
||||
{ vec | y = val }
|
||||
|
||||
7 ->
|
||||
{ vec | z = val }
|
||||
|
||||
_ ->
|
||||
vec
|
||||
|
||||
|
||||
initVectorState : Id -> Move.State -> VectorState
|
||||
initVectorState id state =
|
||||
case id of
|
||||
0 ->
|
||||
{ x = state, y = Move.toState 0, z = Move.toState 0 }
|
||||
|
||||
1 ->
|
||||
{ x = Move.toState 0, y = state, z = Move.toState 0 }
|
||||
|
||||
2 ->
|
||||
{ x = Move.toState 0, y = Move.toState 0, z = state }
|
||||
|
||||
4 ->
|
||||
-- Scale all
|
||||
{ x = state, y = state, z = state }
|
||||
|
||||
5 ->
|
||||
-- scale x
|
||||
{ x = state, y = Move.toState 1, z = Move.toState 1 }
|
||||
|
||||
6 ->
|
||||
-- scale y
|
||||
{ x = Move.toState 1, y = state, z = Move.toState 1 }
|
||||
|
||||
7 ->
|
||||
-- scale z
|
||||
{ x = Move.toState 1, y = Move.toState 1, z = state }
|
||||
|
||||
_ ->
|
||||
{ x = Move.toState 0, y = Move.toState 0, z = Move.toState 0 }
|
||||
|
||||
|
||||
initVector : Id -> Float -> Vector
|
||||
initVector id val =
|
||||
case id of
|
||||
0 ->
|
||||
{ x = val, y = 0, z = 0 }
|
||||
|
||||
1 ->
|
||||
{ x = 0, y = val, z = 0 }
|
||||
|
||||
2 ->
|
||||
{ x = 0, y = 0, z = val }
|
||||
|
||||
4 ->
|
||||
-- Scale all
|
||||
{ x = val, y = val, z = val }
|
||||
|
||||
5 ->
|
||||
-- scale x
|
||||
{ x = val, y = 1, z = 1 }
|
||||
|
||||
6 ->
|
||||
-- scale y
|
||||
{ x = 1, y = val, z = 1 }
|
||||
|
||||
7 ->
|
||||
-- scale z
|
||||
{ x = 1, y = 1, z = val }
|
||||
|
||||
_ ->
|
||||
{ x = 0, y = 0, z = 0 }
|
||||
|
||||
|
||||
{-| Giving the scaling group, return the property that sets all scaling/
|
||||
-}
|
||||
groupToCompoundId : Id -> Maybe Id
|
||||
|
@ -1,4 +1,4 @@
|
||||
module InternalAnim.Duration exposing (Duration, inMilliseconds, inSeconds, isZero, milliseconds)
|
||||
module InternalAnim.Duration exposing (Duration, inMilliseconds, inSeconds, isZero, milliseconds, scale)
|
||||
|
||||
import InternalAnim.Quantity as Quantity
|
||||
|
||||
@ -11,6 +11,11 @@ type Seconds
|
||||
= Seconds
|
||||
|
||||
|
||||
scale : Float -> Duration -> Duration
|
||||
scale factor (Quantity.Quantity seconds) =
|
||||
Quantity.Quantity (factor * seconds)
|
||||
|
||||
|
||||
isZero : Duration -> Bool
|
||||
isZero (Quantity.Quantity seconds) =
|
||||
seconds == 0
|
||||
|
@ -7,15 +7,15 @@ module InternalAnim.Move exposing
|
||||
, sequences
|
||||
, addSequence, cssForSections
|
||||
, withTransition, withVelocities
|
||||
, at, transitionTo
|
||||
, move
|
||||
, at
|
||||
, move, toState
|
||||
)
|
||||
|
||||
{-|
|
||||
|
||||
@docs Move, to, toWith
|
||||
|
||||
@docs State, init
|
||||
@docs State, init, fromFloat
|
||||
|
||||
@docs lerpColor, lerpFloat, lerpVector
|
||||
|
||||
@ -28,7 +28,7 @@ module InternalAnim.Move exposing
|
||||
|
||||
@docs withTransition, withVelocities
|
||||
|
||||
@docs at, transitionTo
|
||||
@docs at
|
||||
|
||||
-}
|
||||
|
||||
@ -56,6 +56,14 @@ init movement =
|
||||
}
|
||||
|
||||
|
||||
toState : Float -> State
|
||||
toState x =
|
||||
{ position =
|
||||
Units.pixels x
|
||||
, velocity = Units.pixelsPerSecond 0
|
||||
}
|
||||
|
||||
|
||||
{-|
|
||||
|
||||
Adjust the transition by taking into account intro and exit velocity if necessary
|
||||
@ -231,66 +239,6 @@ at progress startTime targetTime (Pos transition targetPosition dwell) startingS
|
||||
targetPosition
|
||||
|
||||
|
||||
{-| This is the same as `at`, but with velocity transitions built in.
|
||||
-}
|
||||
transitionTo :
|
||||
Float
|
||||
-> Time.Absolute
|
||||
-> Time.Absolute
|
||||
-> Move Float
|
||||
-> State
|
||||
-> State
|
||||
transitionTo progress startTime targetTime (Pos trans targetPosition dwell) startingState =
|
||||
let
|
||||
startPosition =
|
||||
Units.inPixels startingState.position
|
||||
|
||||
introVelocity =
|
||||
normalizeVelocity
|
||||
startTime
|
||||
targetTime
|
||||
startPosition
|
||||
targetPosition
|
||||
startingState.velocity
|
||||
|
||||
-- exitVelocity =
|
||||
-- If we do any transition smoothing
|
||||
-- we'll need to normalize this velocity too
|
||||
--Estimation.velocityAtTarget lookupState target future
|
||||
transition =
|
||||
if introVelocity == 0 then
|
||||
trans
|
||||
|
||||
else
|
||||
Transition.withVelocities introVelocity 0 trans
|
||||
in
|
||||
Transition.atX progress transition
|
||||
|> denormalize startTime
|
||||
targetTime
|
||||
startPosition
|
||||
targetPosition
|
||||
|
||||
|
||||
normalizeVelocity :
|
||||
Time.Absolute
|
||||
-> Time.Absolute
|
||||
-> Float
|
||||
-> Float
|
||||
-> Units.PixelsPerSecond
|
||||
-> Float
|
||||
normalizeVelocity startTime targetTime startPosition targetPosition velocity =
|
||||
let
|
||||
pixelsPerSecond =
|
||||
Units.inPixelsPerSecond velocity
|
||||
in
|
||||
if pixelsPerSecond == 0 then
|
||||
0
|
||||
|
||||
else
|
||||
(pixelsPerSecond * Duration.inSeconds (Time.duration startTime targetTime))
|
||||
/ (targetPosition - startPosition)
|
||||
|
||||
|
||||
{-|
|
||||
|
||||
Go from 0-1:0-1
|
||||
|
@ -10,6 +10,7 @@ module InternalAnim.Timeline exposing
|
||||
, foldpAll
|
||||
, gc, atTime, dwellingTime, getCurrentTime, linesAreActive
|
||||
, Transition
|
||||
, transitionProgress
|
||||
)
|
||||
|
||||
{-|
|
||||
@ -78,6 +79,8 @@ type alias TimelineDetails event =
|
||||
, events : Timetable event
|
||||
, queued : Maybe (Schedule event)
|
||||
, interruption : List (Schedule event)
|
||||
|
||||
-- Running means that there are ongoing events
|
||||
, running : Bool
|
||||
}
|
||||
|
||||
@ -1088,19 +1091,44 @@ visitAll2 toAnchor transitionTo details prev queue future state =
|
||||
|
||||
type Status
|
||||
= Dwelling Time.Duration
|
||||
| Transitioning Float
|
||||
| Transitioning
|
||||
{ progress : Float
|
||||
, transitionProgress : List Float
|
||||
}
|
||||
|
||||
|
||||
status : Timeline event -> Status
|
||||
status timeline =
|
||||
foldpAll identity
|
||||
(\_ -> Dwelling Time.zeroDuration)
|
||||
(\_ _ _ now start end _ _ ->
|
||||
if Time.thisAfterThat now end then
|
||||
Dwelling (Time.duration now end)
|
||||
(\_ prev target now start end theFuture found ->
|
||||
-- Some notes because I have this loaded in my brain now.
|
||||
-- end: either the endtime of `target event` or the interruption time
|
||||
-- We generally care about progress towards the start time of the target
|
||||
-- so we don't want to use `end` necessarily.
|
||||
let
|
||||
startTimeTarget =
|
||||
startTime target
|
||||
in
|
||||
if Time.thisAfterThat now startTimeTarget then
|
||||
Dwelling (Time.duration now startTimeTarget)
|
||||
|
||||
else
|
||||
Transitioning (Time.progress start end now)
|
||||
case found of
|
||||
Transitioning trans ->
|
||||
Transitioning
|
||||
{ progress =
|
||||
Time.progress start startTimeTarget now
|
||||
, transitionProgress =
|
||||
trans.progress :: trans.transitionProgress
|
||||
}
|
||||
|
||||
Dwelling _ ->
|
||||
Transitioning
|
||||
{ progress =
|
||||
Time.progress start startTimeTarget now
|
||||
, transitionProgress = []
|
||||
}
|
||||
)
|
||||
timeline
|
||||
|
||||
@ -1118,7 +1146,17 @@ progress timeline =
|
||||
1
|
||||
|
||||
Transitioning t ->
|
||||
t
|
||||
t.progress
|
||||
|
||||
|
||||
transitionProgress : Timeline state -> List Float
|
||||
transitionProgress timeline =
|
||||
case status timeline of
|
||||
Dwelling _ ->
|
||||
[]
|
||||
|
||||
Transitioning t ->
|
||||
t.progress :: t.transitionProgress
|
||||
|
||||
|
||||
{-| The number of milliseconds that has occurred since we came to rest at the most recent state.
|
||||
|
Loading…
Reference in New Issue
Block a user