diff --git a/docs/gifs/doc_oDraw.gif b/docs/gifs/doc_oDraw.gif
new file mode 100644
index 0000000..a1cc8cc
Binary files /dev/null and b/docs/gifs/doc_oDraw.gif differ
diff --git a/docs/gifs/doc_oScaleIn.gif b/docs/gifs/doc_oScaleIn.gif
new file mode 100644
index 0000000..3f8472b
Binary files /dev/null and b/docs/gifs/doc_oScaleIn.gif differ
diff --git a/docs/gifs/doc_oScaleOut.gif b/docs/gifs/doc_oScaleOut.gif
new file mode 100644
index 0000000..3e8ea59
Binary files /dev/null and b/docs/gifs/doc_oScaleOut.gif differ
diff --git a/examples/doc_oDraw.hs b/examples/doc_oDraw.hs
new file mode 100755
index 0000000..78ec017
--- /dev/null
+++ b/examples/doc_oDraw.hs
@@ -0,0 +1,20 @@
+#!/usr/bin/env stack
+-- stack runghc --package reanimate
+{-# LANGUAGE OverloadedStrings #-}
+module Main(main) where
+
+import Reanimate
+import Reanimate.Scene
+import Reanimate.Builtin.Documentation
+import Control.Lens
+
+main :: IO ()
+-- main = reanimate $ docEnv $ oDraw $
+-- withFillOpacity 1 $ withStrokeWidth (defaultStrokeWidth*0) $ center $ scale 2 $ latex "oDraw"
+main = reanimate $ docEnv $ scene $ do
+ txt <- oNew $ withStrokeWidth 0 $ withFillOpacity 1 $
+ center $ scale 4 $ latex "oDraw"
+ oModify txt $ oEasing .~ id
+ oShowWith txt oDraw
+ wait 1
+ oFadeOut txt 1
diff --git a/examples/doc_oScaleIn.hs b/examples/doc_oScaleIn.hs
new file mode 100755
index 0000000..bcfd89e
--- /dev/null
+++ b/examples/doc_oScaleIn.hs
@@ -0,0 +1,16 @@
+#!/usr/bin/env stack
+-- stack runghc --package reanimate
+{-# LANGUAGE OverloadedStrings #-}
+module Main(main) where
+
+import Reanimate
+import Reanimate.Scene
+import Reanimate.Builtin.Documentation
+import Control.Lens
+
+main :: IO ()
+main = reanimate $ docEnv $ scene $ do
+ txt <- oNew $ withStrokeWidth 0 $ withFillOpacity 1 $
+ center $ scale 3 $ latex "oScaleIn"
+ oShowWith txt $ adjustDuration (*2) . oScaleIn
+ wait 1; oFadeOut txt 1
diff --git a/examples/doc_oScaleOut.hs b/examples/doc_oScaleOut.hs
new file mode 100755
index 0000000..f34d08a
--- /dev/null
+++ b/examples/doc_oScaleOut.hs
@@ -0,0 +1,16 @@
+#!/usr/bin/env stack
+-- stack runghc --package reanimate
+{-# LANGUAGE OverloadedStrings #-}
+module Main(main) where
+
+import Reanimate
+import Reanimate.Scene
+import Reanimate.Builtin.Documentation
+import Control.Lens
+
+main :: IO ()
+main = reanimate $ docEnv $ scene $ do
+ txt <- oNew $ withStrokeWidth 0 $ withFillOpacity 1 $
+ center $ scale 3 $ latex "oScaleOut"
+ oFadeIn txt 1
+ oHideWith txt $ adjustDuration (*2) . oScaleOut
diff --git a/examples/obj_replace.hs b/examples/obj_replace.hs
new file mode 100755
index 0000000..b9386f8
--- /dev/null
+++ b/examples/obj_replace.hs
@@ -0,0 +1,24 @@
+#!/usr/bin/env stack
+-- stack runghc --package reanimate
+{-# LANGUAGE OverloadedStrings #-}
+module Main(main) where
+
+import Reanimate
+import Reanimate.Scene
+import Reanimate.Builtin.Documentation
+import Control.Lens
+
+main :: IO ()
+main = reanimate $ scene $ do
+ newSpriteSVG_ $ mkBackground "white"
+ t1 <- oNew $ centerX $ scale 3 $ latex "reanimate"
+ t2 <- oNew $ centerX $ scale 3 $ latex "and \\LaTeX"
+
+ oShow t1
+ replace t1 t2; wait 1
+ replace t2 t1; wait 1
+
+replace a b = do
+ fork $ oHideWith a $ adjustDuration (*3) . oScaleOut
+ wait 0.2
+ oShowWith b $ adjustDuration (*3) . oScaleIn
\ No newline at end of file
diff --git a/playground/gen_snippets.sh b/playground/gen_snippets.sh
old mode 100644
new mode 100755
diff --git a/playground/snippets/04_Basic Objects.hs b/playground/snippets/04_Basic Objects.hs
index 07a9d68..0d95bb0 100644
--- a/playground/snippets/04_Basic Objects.hs
+++ b/playground/snippets/04_Basic Objects.hs
@@ -12,6 +12,6 @@ animation = env $
oModify box $
oContext .~ withFillColor "lightblue"
- oGrow circ 1; wait 1
+ oShowWith circ oGrow; wait 1
oTransform circ box 1; wait 1
- oFadeOut box 1; wait 1
+ oHideWith box oFadeOut; wait 1
diff --git a/playground/snippets/08_Object Positions.hs b/playground/snippets/08_Object Positions.hs
index a8d7082..781395d 100644
--- a/playground/snippets/08_Object Positions.hs
+++ b/playground/snippets/08_Object Positions.hs
@@ -37,9 +37,8 @@ animation = env $
switchTo topL txt
switchTo src dst = do
- fork $ oFadeOut src 1
- oModify dst $ oOpacity .~ 1
- oFadeIn dst 1
+ fork $ oHideWith src oFadeOut
+ oShowWith dst oFadeIn
wait 1
newText txt =
diff --git a/playground/viewer-elm/dist/snippets.js b/playground/viewer-elm/dist/snippets.js
index c910fe0..f38c329 100644
--- a/playground/viewer-elm/dist/snippets.js
+++ b/playground/viewer-elm/dist/snippets.js
@@ -2,11 +2,11 @@ const snippets = [{"title": "Hello World","url": "https://reanimate.clozecards.c
,{"title": "Composition","url": "https://reanimate.clozecards.com/ECv3cbsbuMV/150.svg","code": "animation :: Animation\nanimation = docEnv $ sceneAnimation $ do\n play $ drawBox `parA` drawCircle\n & label \"parA\"\n play $ drawBox `seqA` drawCircle\n & label \"seqA\"\n play $ drawBox `andThen` drawCircle\n & label \"andThen\"\n\nlabel txt = addStatic $\n withFillOpacity 1 $ withStrokeWidth 0 $\n withFillColor \"black\" $\n translate screenLeft (screenBottom+0.2) $\n latex txt\n"}
,{"title": "Color Maps","url": "https://reanimate.clozecards.com/Ezuf27J2YrF/60.svg","code": "animation :: Animation\nanimation = docEnv $ sceneAnimation $ do\n play $ staticFrame 1 (showColorMap parula)\n & label \"Parula\"\n play $ staticFrame 1 (showColorMap viridis)\n & label \"Viridis\"\n play $ staticFrame 1 (showColorMap turbo)\n & label \"Turbo\"\n play $ staticFrame 1 (showColorMap greyscale)\n & label \"Greyscale\"\n\nlabel txt = overlay $\n withFillOpacity 1 $ withStrokeWidth 0 $\n withFillColor \"white\" $\n translate screenLeft (screenBottom+0.2) $\n latex txt\n\noverlay svg ani = ani `parA` staticFrame (duration ani) svg\n"}
,{"title": "Try it live","url": "https://reanimate.clozecards.com/OwjMi4FCJ4Z/90.svg","code": "background = \"lightblue\"\n\nshape :: SVG\nshape = mkCircle 4\n--shape = mkRect 6 6\n--shape = mkLine (screenLeft, screenBottom) (screenRight, screenTop)\n\nanimation :: Animation\nanimation = docEnv $\n addStatic (mkBackground background) $\n playThenReverseA $\n signalA (curveS 2) $\n setDuration 3 $ animate $ \\t ->\n partialSvg t $ pathify shape\n"}
- ,{"title": "Basic Objects","url": "https://reanimate.clozecards.com/O0105YfECEH/90.svg","code": "env =\n addStatic (mkBackground \"white\") .\n mapA (withStrokeColor \"black\")\n\nanimation :: Animation\nanimation = env $\n sceneAnimation $ do\n circ <- newObject $ Circle 3\n oModify circ $\n oContext .~ withFillColor \"pink\"\n box <- newObject $ Rectangle 5 5\n oModify box $\n oContext .~ withFillColor \"lightblue\"\n \n oGrow circ 1; wait 1\n oTransform circ box 1; wait 1\n oFadeOut box 1; wait 1\n"}
+ ,{"title": "Basic Objects","url": "https://reanimate.clozecards.com/LvoPSTEzZlg/90.svg","code": "env =\n addStatic (mkBackground \"white\") .\n mapA (withStrokeColor \"black\")\n\nanimation :: Animation\nanimation = env $\n sceneAnimation $ do\n circ <- newObject $ Circle 3\n oModify circ $\n oContext .~ withFillColor \"pink\"\n box <- newObject $ Rectangle 5 5\n oModify box $\n oContext .~ withFillColor \"lightblue\"\n \n oShowWith circ oGrow; wait 1\n oTransform circ box 1; wait 1\n oHideWith box oFadeOut; wait 1\n"}
,{"title": "LaTeX","url": "https://reanimate.clozecards.com/PXPuugoHzrR/181.svg","code": "env =\n addStatic (mkBackground \"white\") .\n mapA (withStrokeColor \"black\")\n\nanimation :: Animation\nanimation = env $\n sceneAnimation $ do\n drawLatex \"e^{i\\\\pi}+1=0\"\n drawLatex \"\\\\sum_{k=1}^\\\\infty {1 \\\\over k^2} = {\\\\pi^2 \\\\over 6}\"\n drawLatex \"\\\\sum_{k=1}^\\\\infty\"\n wait 1\n\ndrawLatex txt = do\n -- Draw outline\n fork $ do\n play $ animate $ \\t ->\n withFillOpacity 0 $\n partialSvg t svg\n -- Fade outline\n play $ animate $ \\t ->\n withFillOpacity 0 $\n withStrokeWidth (defaultStrokeWidth*(1-t)) $\n svg\n wait 0.7\n -- Fill in letters\n play $ animate $ \\t ->\n withFillOpacity t $ withStrokeWidth 0 $\n svg\n -- Hold static image and then fade out\n play $ staticFrame 2 (withStrokeWidth 0 svg)\n & applyE (overEnding 0.3 fadeOutE)\n where\n svg = scale 2 $ center $ latexAlign txt\n"}
,{"title": "Easing Functions","url": "https://reanimate.clozecards.com/P4F3jzh2Mcj/45.svg","code": "animation :: Animation\nanimation = docEnv $ pauseAtEnd 1 $ sceneAnimation $ do\n showEasing 0 \"curveS\" (curveS 2)\n showEasing 1 \"bellS\" (bellS 2)\n showEasing 2 \"constantS\" (constantS 0.7)\n showEasing 3 \"oscillateS\" oscillateS\n showEasing 4 \"powerS\" (powerS 2)\n showEasing 5 \"reverseS\" reverseS\n showEasing 6 \"id\" id\n\nshowEasing nth txt fn = do\n let yOffset | even nth = 0\n | odd nth = -1\n xOffset = -6 + fromIntegral nth*2\n newSpriteSVG_ $\n \ttranslate xOffset yOffset $\n label txt\n fork $ play $ mapA (translate xOffset 0) $\n mapA (rotate 90) $\n mapA (scale (screenHeight/screenWidth * 0.6)) $\n signalA fn drawProgress\n\nlabel txt =\n translate 0 (-2.5) $\n scale 0.7 $\n center $\n withStrokeWidth 0 $\n withFillOpacity 1 $\n svg\n where\n svg = latex txt\n"}
,{"title": "Easing Graphs","url": "https://reanimate.clozecards.com/FEyCDsj0V82/255.svg","code": "colorPalette = parula -- try: viridis, sinebow, turbo, cividis\nfns =\n [(\"curveS\", curveS 2)\n ,(\"bellS\", bellS 2)\n ,(\"constantS\", constantS 0.7)\n ,(\"oscillateS\", oscillateS)\n ,(\"powerS\", powerS 2)\n ,(\"reverseS\", reverseS)\n ,(\"id\", id)\n ]\n\nanimation :: Animation\nanimation = docEnv $ pauseAtEnd 1 $ sceneAnimation $ do\n newSpriteSVG_ $ mkBackground \"white\"\n play $ signalA (curveS 2) $ animate $ \\t -> partialSvg t grid\n newSpriteSVG_ grid\n wait 1\n flip mapM_ (zip [0..] fns) $ \\(nth, (txt, fn)) -> do\n let color = promotePixel $ colorPalette (nth / fromIntegral (length fns-1))\n showEasing nth txt fn color\n {- showEasing 3 \"oscillateS\" oscillateS\n showEasing 4 \"powerS\" (powerS 2)\n showEasing 5 \"reverseS\" reverseS\n showEasing 6 \"id\" id -}\n\ngridOffset = -2\ngridHeight = 5\ngridWidth = 8\n\ngrid :: SVG\ngrid = translate gridOffset 0 $\n withStrokeWidth defaultStrokeWidth $\n withStrokeColor \"grey\" $ mkGroup\n [ mkPath $ concat\n [[ SVG.MoveTo SVG.OriginAbsolute [V2 (-gridWidth/2) (gridHeight/2-n)]\n ,SVG.HorizontalTo SVG.OriginRelative [gridWidth] ]\n | n <- [1..gridHeight-1]\n ]\n , mkPath \n [ SVG.MoveTo SVG.OriginAbsolute [V2 (-gridWidth/2) (gridHeight/2)]\n , SVG.VerticalTo SVG.OriginRelative [-gridHeight]\n , SVG.HorizontalTo SVG.OriginRelative [gridWidth]\n , SVG.VerticalTo SVG.OriginRelative [gridHeight]\n , SVG.EndPath\n ]\n ]\n\nshowEasing nth txt fn color = do\n let steps = 100\n slope = withStrokeColorPixel color $\n translate gridOffset 0 $ mkLinePath\n [ ((x/steps-0.5)*gridWidth, (y-0.5)*gridHeight)\n | x <- [0..steps]\n , let y = fn (x/steps) ]\n s <- newSpriteSVG $\n withFillColorPixel color $\n translate (gridWidth/2+gridOffset+0.5) (gridHeight/2-nth) $\n label txt\n spriteE s $ overBeginning 0.2 fadeInE\n play $ animate $ \\t -> partialSvg t slope\n newSpriteSVG_ slope\n wait 1\n \nlabel txt = withStrokeColor \"black\" $\n withStrokeWidth (defaultStrokeWidth*2) $\n withFillOpacity 1 $\n latex txt\n"}
- ,{"title": "Object Positions","url": "https://reanimate.clozecards.com/OdYISph5tKE/195.svg","code": "env =\n addStatic (mkBackground \"white\") .\n mapA (withStrokeColor \"black\")\n\nanimation :: Animation\nanimation = env $\n sceneAnimation $ do\n -- Configure objects\n txt <- newText \"Center\"\n top <- newText \"Top\"\n oModifyS top $ \n oTopY .= screenTop\n topR <- newText \"Top right\"\n oModifyS topR $ do\n oTopY .= screenTop\n oRightX .= screenRight\n botR <- newText \"Bottom right\"\n oModifyS botR $ do\n oTranslate .= (0, screenBottom+0.5)\n oRightX .= screenRight\n botL <- newText \"Bottom left\"\n oModifyS botL $ do\n oTranslate .= (0, screenBottom+0.5)\n oLeftX .= screenLeft\n topL <- newText \"Top left\"\n oModifyS topL $ do\n oTopY .= screenTop\n oLeftX .= screenLeft\n -- Show objects\n oShow txt\n wait 1\n switchTo txt top\n switchTo top topR\n switchTo topR botR\n switchTo botR botL\n switchTo botL topL\n switchTo topL txt\n\nswitchTo src dst = do\n fork $ oFadeOut src 1\n oModify dst $ oOpacity .~ 1\n oFadeIn dst 1\n wait 1\n\nnewText txt =\n newObject $ scale 1.5 $ centerX $ latex txt\n"}
+ ,{"title": "Object Positions","url": "https://reanimate.clozecards.com/Mf4zcImo+I7/195.svg","code": "env =\n addStatic (mkBackground \"white\") .\n mapA (withStrokeColor \"black\")\n\nanimation :: Animation\nanimation = env $\n sceneAnimation $ do\n -- Configure objects\n txt <- newText \"Center\"\n top <- newText \"Top\"\n oModifyS top $ \n oTopY .= screenTop\n topR <- newText \"Top right\"\n oModifyS topR $ do\n oTopY .= screenTop\n oRightX .= screenRight\n botR <- newText \"Bottom right\"\n oModifyS botR $ do\n oTranslate .= (0, screenBottom+0.5)\n oRightX .= screenRight\n botL <- newText \"Bottom left\"\n oModifyS botL $ do\n oTranslate .= (0, screenBottom+0.5)\n oLeftX .= screenLeft\n topL <- newText \"Top left\"\n oModifyS topL $ do\n oTopY .= screenTop\n oLeftX .= screenLeft\n -- Show objects\n oShow txt\n wait 1\n switchTo txt top\n switchTo top topR\n switchTo topR botR\n switchTo botR botL\n switchTo botL topL\n switchTo topL txt\n\nswitchTo src dst = do\n fork $ oHideWith src oFadeOut\n oShowWith dst oFadeIn\n wait 1\n\nnewText txt =\n newObject $ scale 1.5 $ centerX $ latex txt\n"}
,{"title": "Camera","url": "https://reanimate.clozecards.com/Hcx00P+aeph/150.svg","code": "animation :: Animation\nanimation = docEnv $ mapA (withFillOpacity 1) $ sceneAnimation $ do\n cam <- newObject Camera\n\n txt <- newObject $ center $ latex \"Fixed (non-cam)\"\n oModifyS txt $ do\n oTopY .= screenTop \n oZIndex .= 2\n\n circle <- newObject $ withFillColor \"blue\" $ mkCircle 1\n cameraAttach cam circle\n circleRight <- oRead circle oRightX\n\n box <- newObject $ withFillColor \"green\" $ mkRect 2 2\n cameraAttach cam box\n oModify box $ oLeftX .~ circleRight\n boxCenter <- oRead box oCenterXY\n\n small <- newObject $ center $ latex \"This text is very small\"\n cameraAttach cam small\n oModifyS small $ do\n oCenterXY .= boxCenter\n oScale .= 0.1\n \n oShow txt\n oShow small\n oShow circle\n oShow box\n\n wait 1\n\n cameraFocus cam boxCenter\n waitOn $ do\n fork $ cameraPan cam 3 boxCenter\n fork $ cameraZoom cam 3 15\n \n wait 2\n cameraZoom cam 3 1\n cameraPan cam 1 (0,0)\n"}
];
const playgroundVersion = "2020-08-28 (185c3)";
diff --git a/src/Reanimate/Scene.hs b/src/Reanimate/Scene.hs
index 0f2c187..94f4900 100644
--- a/src/Reanimate/Scene.hs
+++ b/src/Reanimate/Scene.hs
@@ -107,6 +107,20 @@ module Reanimate.Scene
, oGrow
, oShrink
, oTransform
+ , oShowWith
+ , oHideWith
+ , Origin
+ , oScaleIn
+ , oScaleIn'
+ , oScaleOut
+ , oScaleOut'
+ , oSim
+ , oStagger
+ , oStaggerRev
+ , oStagger'
+ , oStaggerRev'
+ , oDraw
+ -- , oBalloon
-- ** Pre-defined objects
, Circle(..)
@@ -134,21 +148,23 @@ module Reanimate.Scene
where
import Control.Lens
-import Control.Monad (void)
+import Control.Monad (forM_, void)
import Control.Monad.Fix
import Control.Monad.ST
-import Control.Monad.State (execState, State)
+import Control.Monad.State (State, execState)
import Data.List
+import Data.Monoid
import Data.STRef
-import Graphics.SvgTree (Tree (None))
+import Graphics.SvgTree (Number (..), Tree (None), strokeWidth, toUserUnit)
import Reanimate.Animation
-import Reanimate.Ease (Signal, curveS, fromToS)
+import Reanimate.Constants
+import Reanimate.Ease (Signal, curveS, fromToS)
import Reanimate.Effect
-import Reanimate.Svg.Constructors
-import Reanimate.Svg.BoundingBox
-import Reanimate.Transition
+import Reanimate.Math.Balloon
import Reanimate.Morph.Common (morph)
import Reanimate.Morph.Linear (linear)
+import Reanimate.Svg
+import Reanimate.Transition
-- | The ZIndex property specifies the stack order of sprites and animations. Elements
-- with a higher ZIndex will be drawn on top of elements with a lower index.
@@ -844,7 +860,7 @@ oValue = lens _oValueRef $ \obj newVal ->
oTopY :: Lens' (ObjectData a) Double
oTopY = lens getter setter
where
- getter obj =
+ getter obj =
let top = obj ^. oMarginTop
miny = obj ^. oBBMinY
h = obj ^. oBBHeight
@@ -857,12 +873,12 @@ oTopY = lens getter setter
oBottomY :: Lens' (ObjectData a) Double
oBottomY = lens getter setter
where
- getter obj =
+ getter obj =
let bot = obj ^. oMarginBottom
miny = obj ^. oBBMinY
dy = obj ^. oTranslate . _2
in dy+miny-bot
- setter obj val =
+ setter obj val =
obj & (oTranslate . _2) +~ val-getter obj
-- | Derived location of the left-most point of an object + margin.
@@ -1028,35 +1044,113 @@ oShow o = oModify o $ oShown .~ True
oHide :: Object s a -> Scene s ()
oHide o = oModify o $ oShown .~ False
+oShowWith :: Object s a -> (SVG -> Animation) -> Scene s ()
+oShowWith o fn = do
+ oModify o $ oShown .~ True
+ initSVG <- oRead o oSVG
+ let ani = fn initSVG
+ oTween o (duration ani) $ \t obj ->
+ obj{ _oSVG = getAnimationFrame SyncStretch ani t 1 }
+ oModify o $ \obj -> obj { _oSVG = initSVG }
+
+oHideWith :: Object s a -> (SVG -> Animation) -> Scene s ()
+oHideWith o fn = do
+ initSVG <- oRead o oSVG
+ let ani = fn initSVG
+ oTween o (duration ani) $ \t obj ->
+ obj{ _oSVG = getAnimationFrame SyncStretch ani t 1 }
+ oModify o $ \obj -> obj { _oSVG = initSVG }
+ oModify o $ oShown .~ False
+
-- | Fade in object over a set duration.
-oFadeIn :: Object s a -> Duration -> Scene s ()
-oFadeIn o d = do
- oModify o $
- oShown .~ True
- oTweenS o d $ \t ->
- oOpacity *= t
+oFadeIn :: SVG -> Animation
+oFadeIn svg = animate $ \t -> withGroupOpacity t svg
-- | Fade out object over a set duration.
-oFadeOut :: Object s a -> Duration -> Scene s ()
-oFadeOut o d = do
- oModify o $
- oShown .~ True
- oTweenS o d $ \t ->
- oOpacity *= 1-t
+oFadeOut :: SVG -> Animation
+oFadeOut = reverseA . oFadeIn
-- | Scale in object over a set duration.
-oGrow :: Object s a -> Duration -> Scene s ()
-oGrow o d = do
- oModify o $
- oShown .~ True
- oTweenS o d $ \t ->
- oScale *= t
+oGrow :: SVG -> Animation
+oGrow svg = animate $ \t -> scale t svg
-- | Scale out object over a set duration.
-oShrink :: Object s a -> Duration -> Scene s ()
-oShrink o d =
- oTweenS o d $ \t ->
- oScale *= 1-t
+oShrink :: SVG -> Animation
+oShrink = reverseA . oGrow
+
+type Origin = (Double, Double)
+
+svgOrigin :: SVG -> Origin -> (Double, Double)
+svgOrigin svg (originX, originY) =
+ case boundingBox svg of
+ (polyX, polyY, polyWidth, polyHeight) ->
+ ( polyX + polyWidth * originX
+ , polyY + polyHeight * originY)
+
+oScaleIn :: SVG -> Animation
+oScaleIn = oScaleIn' (curveS 2) (0.5,1)
+
+oScaleIn' :: Signal -> Origin -> SVG -> Animation
+oScaleIn' easing origin = oStagger' 0.05 $ \svg ->
+ let (cx, cy) = svgOrigin svg origin
+ in signalA easing $ mkAnimation 0.3 $ \t ->
+ translate cx cy $
+ scale t $
+ translate (-cx) (-cy) $
+ svg
+
+oScaleOut :: SVG -> Animation
+oScaleOut = reverseA . oStaggerRev' 0.05 (oScaleIn' (curveS 2) (0.5,0))
+
+oScaleOut' :: Signal -> Origin -> SVG -> Animation
+oScaleOut' easing origin = reverseA . oStaggerRev' 0.05 (oScaleIn' easing origin)
+
+oSim :: (SVG -> Animation) -> SVG -> Animation
+oSim = oStagger' 0
+
+-- oSim (oStagger fn) = oSim fn
+-- oStagger (oStagger fn) = oStagger fn
+oStagger :: (SVG -> Animation) -> SVG -> Animation
+oStagger = oStagger' 0.2
+
+oStaggerRev :: (SVG -> Animation) -> SVG -> Animation
+oStaggerRev = oStaggerRev' 0.2
+
+oStagger' :: Duration -> (SVG -> Animation) -> SVG -> Animation
+oStagger' staggerDelay fn svg = scene $
+ forM_ (svgGlyphs svg) $ \(ctx, _attr, node) -> do
+ void $ fork $ newSpriteA' SyncFreeze (fn $ ctx node)
+ wait staggerDelay
+
+oStaggerRev' :: Duration -> (SVG -> Animation) -> SVG -> Animation
+oStaggerRev' staggerDelay fn svg = scene $
+ forM_ (reverse $ svgGlyphs svg) $ \(ctx, _attr, node) -> do
+ void $ fork $ newSpriteA' SyncFreeze (fn $ ctx node)
+ wait staggerDelay
+
+oDraw :: SVG -> Animation
+oDraw = oStagger $ \svg -> scene $ do
+ forM_ (svgGlyphs $ pathify svg) $ \(ctx, attr, node) -> do
+ let sWidth =
+ case toUserUnit defaultDPI <$> getLast (attr ^. strokeWidth) of
+ Just (Num d) -> max defaultStrokeWidth d
+ _ -> defaultStrokeWidth
+ -- wait 1
+ play $
+ mapA ctx $
+ applyE (overEnding fillDur $ fadeLineOutE sWidth) $
+ animate $ \t -> withStrokeWidth sWidth $
+ mkGroup
+ [withFillOpacity 0 $ partialSvg t node]
+ wait (-fillDur)
+ newSpriteA' SyncFreeze $ mkAnimation fillDur $ \t ->
+ withGroupOpacity t $
+ mkGroup [ ctx node ]
+ where
+ fillDur = 0.3
+
+_oBalloon :: SVG -> Animation
+_oBalloon = animate . balloon
-- FIXME: Also transform attributes: 'opacity', 'scale', 'scaleOrigin'.
-- | Morph source object into target object over a set duration.
@@ -1067,7 +1161,7 @@ oTransform src dst d = do
srcEase <- oRead src oEasing
srcLoc <- oRead src oTranslate
oModify src $ oShown .~ False
-
+
dstSvg <- oRead dst oSVG
dstCtx <- oRead dst oContext
dstLoc <- oRead dst oTranslate
@@ -1223,4 +1317,4 @@ cameraPan :: Object s Camera -> Duration -> (Double, Double) -> Scene s ()
cameraPan cam d (x,y) =
oTweenS cam d $ \t -> do
oTranslate._1 %= \v -> fromToS v x t
- oTranslate._2 %= \v -> fromToS v y t
\ No newline at end of file
+ oTranslate._2 %= \v -> fromToS v y t
diff --git a/src/Reanimate/Svg.hs b/src/Reanimate/Svg.hs
index 2601571..bd5107b 100644
--- a/src/Reanimate/Svg.hs
+++ b/src/Reanimate/Svg.hs
@@ -34,7 +34,7 @@ import qualified Reanimate.Transform as Transform
-- width is affected by directly applying scaling.
--
-- @lowerTransformations (scale 2 (mkCircle 1)) = mkCircle 2@
-lowerTransformations :: Tree -> Tree
+lowerTransformations :: SVG -> SVG
lowerTransformations = worker False Transform.identity
where
updLineCmd m cmd =
@@ -70,7 +70,7 @@ lowerTransformations = worker False Transform.identity
_ -> worker True m (pathify t)
-- | Remove all @id@ attributes.
-lowerIds :: Tree -> Tree
+lowerIds :: SVG -> SVG
lowerIds = mapTree worker
where
worker t@GroupTree{} = t & attrId .~ Nothing
@@ -78,7 +78,7 @@ lowerIds = mapTree worker
worker t = t
-- | Optimize SVG tree without affecting how it is rendered.
-simplify :: Tree -> Tree
+simplify :: SVG -> SVG
simplify root =
case worker root of
[] -> None
@@ -110,7 +110,7 @@ simplify root =
-- @removeGroups (withFillColor "blue" $ mkGroup [mkCircle 1, mkRect 1 1])
-- = [ withFillColor "blue" $ mkCircle 1
-- , withFillColor "blue" $ mkRect 1 1 ]@
-removeGroups :: Tree -> [Tree]
+removeGroups :: SVG -> [SVG]
removeGroups = worker defaultSvg
where
worker _attr None = []
@@ -133,7 +133,7 @@ removeGroups = worker defaultSvg
dropNulls t = [t]
-- | Extract all path commands from a node (and its children) and concatenate them.
-extractPath :: Tree -> [PathCommand]
+extractPath :: SVG -> [PathCommand]
extractPath = worker . simplify . lowerTransformations . pathify
where
worker (GroupTree g) = concatMap worker (g^.groupChildren)
@@ -144,7 +144,7 @@ extractPath = worker . simplify . lowerTransformations . pathify
--
-- @withSubglyphs [0,2] (scale 2) (mkGroup [mkCircle 1, mkRect 2, mkEllipse 1 2])
-- = mkGroup [scale 2 (mkCircle 1), mkRect 2, scale 2 (mkEllipse 1 2)]@
-withSubglyphs :: [Int] -> (Tree -> Tree) -> Tree -> Tree
+withSubglyphs :: [Int] -> (SVG -> SVG) -> SVG -> SVG
withSubglyphs target fn = \t -> evalState (worker t) 0
where
worker :: Tree -> State Int Tree
@@ -172,18 +172,18 @@ withSubglyphs target fn = \t -> evalState (worker t) 0
--
-- @splitGlyphs [0,2] (mkGroup [mkCircle 1, mkRect 2, mkEllipse 1 2])
-- = ([mkRect 2], [mkCircle 1, mkEllipse 1 2])@
-splitGlyphs :: [Int] -> Tree -> (Tree, Tree)
+splitGlyphs :: [Int] -> SVG -> (SVG, SVG)
splitGlyphs target = \t ->
let (_, l, r) = execState (worker id t) (0, [], [])
in (mkGroup l, mkGroup r)
where
- handleGlyph :: Tree -> State (Int, [Tree], [Tree]) ()
+ handleGlyph :: SVG -> State (Int, [SVG], [SVG]) ()
handleGlyph t = do
(n, l, r) <- get
if n `elem` target
then put (n+1, l, t:r)
else put (n+1, t:l, r)
- worker :: (Tree -> Tree) -> Tree -> State (Int, [Tree], [Tree]) ()
+ worker :: (SVG -> SVG) -> SVG -> State (Int, [SVG], [SVG]) ()
worker acc t =
case t of
GroupTree g -> do
@@ -213,7 +213,7 @@ splitGlyphs target = \t ->
, (\svg -> svg, )]
-}
-- | Split symbols and include their context and drawing attributes.
-svgGlyphs :: Tree -> [(Tree -> Tree, DrawAttributes, Tree)]
+svgGlyphs :: SVG -> [(SVG -> SVG, DrawAttributes, SVG)]
svgGlyphs = worker id defaultSvg
where
worker acc attr =
@@ -243,7 +243,7 @@ svgGlyphs = worker id defaultSvg
<>
-}
-pathify :: Tree -> Tree
+pathify :: SVG -> SVG
pathify = mapTree worker
where
worker =