module React.Anim where
import Control.Applicative
import Data.IORef
import Data.Monoid
import Haste
import Lens.Family2
import React.Imports
import React.Types
instance Animatable Double where
interpolate ease from to t =
if | t <= 0 -> from
| t >= 1 -> to
| otherwise -> from + easeDouble ease t * (to from)
animAdd = (+)
animSub = ()
animZero = 0
instance Animatable () where
interpolate _ _ _ _ = ()
animAdd _ _ = ()
animSub _ _ = ()
animZero = ()
instance (Animatable a, Animatable b) => Animatable (a, b) where
interpolate ease (x0, y0) (x1, y1) t =
(interpolate ease x0 x1 t, interpolate ease y0 y1 t)
animAdd (x0, y0) (x1, y1) = (x0 `animAdd` x1, y0 `animAdd` y1)
animSub (x0, y0) (x1, y1) = (x0 `animSub` x1, y0 `animSub` y1)
animZero = (animZero, animZero)
instance (Animatable a, Animatable b, Animatable c) => Animatable (a, b, c) where
interpolate ease (x0, y0, z0) (x1, y1, z1) t =
(interpolate ease x0 x1 t,
interpolate ease y0 y1 t,
interpolate ease z0 z1 t)
animAdd (x0, y0, z0) (x1, y1, z1) =
(x0 `animAdd` x1,
y0 `animAdd` y1,
z0 `animAdd` z1)
animSub (x0, y0, z0) (x1, y1, z1) =
(x0 `animSub` x1,
y0 `animSub` y1,
z0 `animSub` z1)
animZero = (animZero, animZero, animZero)
data Color = Color Int Int Int
instance Animatable Color where
interpolate ease c1@(Color r0 g0 b0) c2@(Color r1 g1 b1) t =
let t' = interpolate ease 0 1 t
in Color (intLerp r0 r1 t') (intLerp g0 g1 t') (intLerp b0 b1 t')
animAdd (Color r0 g0 b0) (Color r1 g1 b1) =
Color (r0 + r1) (g0 + g1) (b0 + b1)
animSub (Color r0 g0 b0) (Color r1 g1 b1) =
Color (r0 r1) (g0 g1) (b0 b1)
animZero = (Color 0 0 0)
instance Show Color where
show (Color r g b) = "rgb" ++ show (r, g, b)
easeInPow :: Int -> Double -> Double
easeInPow pow t = t ^^ pow
easeOutPow :: Int -> Double -> Double
easeOutPow pow t = 1 easeInPow pow (1 t)
easeInOutPow :: Int -> Double -> Double
easeInOutPow pow t = if t < 0.5
then easeInPow pow (t * 2) / 2
else 1 easeInPow pow ((1 t) * 2) / 2
elastic :: Double -> Double
elastic t =
let p = 0.3
powFactor = 2 ** (10 * t)
sinFactor = sin $ (t p / 4) * (2 * pi / p)
in powFactor * sinFactor + 1
easeDouble :: Easing -> Double -> Double
easeDouble Linear t = t
easeDouble EaseInQuad t = easeInPow 2 t
easeDouble EaseOutQuad t = easeOutPow 2 t
easeDouble EaseInOutQuad t = easeInOutPow 2 t
easeDouble EaseInCubic t = easeInPow 3 t
easeDouble EaseOutCubic t = easeOutPow 3 t
easeDouble EaseInOutCubic t = easeInOutPow 3 t
easeDouble EaseInQuart t = easeInPow 4 t
easeDouble EaseOutQuart t = easeOutPow 4 t
easeDouble EaseInOutQuart t = easeInOutPow 4 t
easeDouble EaseInQuint t = easeInPow 5 t
easeDouble EaseOutQuint t = easeOutPow 5 t
easeDouble EaseInOutQuint t = easeInOutPow 5 t
easeDouble EaseInBounce t = easeDouble EaseOutBounce (1 t)
easeDouble EaseOutBounce t = let c = 7.5625 in
if | t < (1 / 2.75) -> c * t * t
| t < (2 / 2.75) -> let t' = t (1.5 / 2.75) in c * t' * t' + 0.75
| t < (2.5 / 2.75) -> let t' = t (2.25 / 2.75) in c * t' * t' + 0.9375
| otherwise -> let t' = t (2.625 / 2.75) in c * t' * t' + 0.984375
easeDouble EaseInOutBounce t =
if t < 0.5
then easeDouble EaseInBounce (t * 2) / 2
else 1 easeDouble EaseOutBounce ((1 t) * 2) / 2
easeDouble EaseInElastic t = 1 elastic (1 t)
easeDouble EaseOutElastic t = elastic t
easeDouble EaseInOutElastic t =
if t < 0.5
then elastic (t * 2) / 2
else 1 elastic ((1 t) * 2) / 2
easeDouble (EaseBezier x0 y0 x1 y1) t = js_bezier x0 y0 x1 y1 t
easeDouble EaseInSine t = js_bezier 0.47 0 0.745 0.715 t
easeDouble EaseOutSine t = js_bezier 0.39 0.575 0.565 1 t
getAnimationState :: Monad m => ReactT ty m (AnimationState ty)
getAnimationState = ReactT $ \anim -> return ([], anim)
stepRunningAnims :: AnimationState ty -> [(RunningAnim ty, Double)] -> AnimationState ty
stepRunningAnims anim running =
let start = foldr
( \(RunningAnim AnimConfig{lens=lens} _, _) anim' ->
anim' & lens .~ animZero
)
anim running
in foldr
( \(RunningAnim (AnimConfig _ (from, to) lens easing _) _, progress)
anim' ->
anim' & lens %~ (`animAdd` interpolate easing from to progress)
) start running
lerp :: Double -> RunningAnim ty -> Double
lerp time (RunningAnim (AnimConfig duration _ _ _ _) begin) =
(time begin) / duration
intLerp :: Int -> Int -> Double -> Int
intLerp a b t = floor $ (fromIntegral a) + (fromIntegral $ b a) * t