Skip to content

Commit

Permalink
:animate directive
Browse files Browse the repository at this point in the history
  • Loading branch information
cgrand committed Sep 4, 2024
1 parent e05433c commit b152be0
Show file tree
Hide file tree
Showing 3 changed files with 155 additions and 4 deletions.
112 changes: 108 additions & 4 deletions clj/src/cljd/flutter.cljd
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
(ns cljd.flutter
(:require ["package:flutter/widgets.dart" :as widgets]
["package:flutter/material.dart" :as material]
["package:flutter/foundation.dart" :as foundation]
["dart:async" :as dart:async]
[cljd.string :as str])
Expand Down Expand Up @@ -609,13 +610,111 @@
.height ~height
.child (-widget-cont ~(assoc env :key nil :closest-ctx false) ~@forms))))

(defprotocol ITweenable
(-tween [end]))

(extend-protocol ITweenable
fallback
(-tween [end] (widgets/Tween .end end))

widgets/AlignmentGeometry
(-tween [end] (widgets/AlignmentGeometryTween .end end))

widgets/Alignment
(-tween [end] (widgets/AlignmentTween .end end))

widgets/BorderRadius
(-tween [end] (widgets/BorderRadiusTween .end end))

widgets/Border
(-tween [end] (widgets/BorderTween .end end))

widgets/BoxConstraints
(-tween [end] (widgets/BoxConstraintsTween .end end))

widgets/Color
(-tween [end] (widgets/ColorTween .end end))

widgets/Decoration
(-tween [end] (widgets/DecorationTween .end end))

widgets/EdgeInsetsGeometry
(-tween [end] (widgets/EdgeInsetsGeometryTween .end end))

widgets/EdgeInsets
(-tween [end] (widgets/EdgeInsetsTween .end end))

widgets/FractionalOffset
(-tween [end] (widgets/FractionalOffsetTween .end end))

int
(-tween [end] (widgets/IntTween .end end))

widgets/Matrix4
(-tween [end] (widgets/Matrix4Tween .end end))

widgets/Rect
(-tween [end] (widgets/RectTween .end end))

widgets/RelativeRect
(-tween [end] (widgets/RelativeRectTween .end end))

material/ShapeBorder
(-tween [end] (material/ShapeBorderTween .end end))

widgets/Size
(-tween [end] (widgets/SizeTween .end end))

widgets/TextStyle
(-tween [end] (widgets/TextStyleTween .end end))

material/ThemeData
(-tween [end] (material/ThemeDataTween .end end)))

(deftype CustomTween [f ^:mutable lerpf]
:extends widgets/Tween
(begin [this v] (set! lerpf nil) (.-begin! ^super this v))
(end [this v] (set! lerpf nil) (.-end! ^super this v))
(lerp [this t]
(let [lerpf (or lerpf (set! lerpf (f (.-begin this) (.-end this))))]
(lerpf t))))

(defn ^widgets/Tween tween-with
"Create a tween ending at the provided value. Or return the value if it's already a tween.
When f is not provided, a tween is created using the ITweenable protocol.
When f is provided it must be a function of two args (begin and end values),
returning an interpolation function from t (between 0.0 and 1.0) to
interpolated value."
[f end]
(cond
f
(doto (CustomTween f nil)
(.-end! end))
(instance? widgets/Tween end) end
:else
(-tween end)))

(defn ^:macro-support expand-animate
[&env env binding expr {:keys [lerp on-end duration curve] :as opts
:or {duration `(Duration .milliseconds 500)
curve `widgets/Curves.linear}} forms]
`(widgets/TweenAnimationBuilder
.key ~(:key env)
.tween (tween-with ~lerp ~expr)
.duration ~duration
.curve ~curve
.onEnd ~(when on-end `(fn [] ~on-end nil))
.builder
(fn [~closest-context ~binding _#]
(-widget-cont ~(assoc env :key nil :closest-ctx true) ~@forms))))

(deftype SpyWidget [k child f]
:extends (widgets/StatelessWidget .key k)
(build [_ _] child)
(debugFillProperties [this props-builder]
(.debugFillProperties ^super this props-builder)
(f props-builder)
nil))
(.debugFillProperties ^super this props-builder)
(f props-builder)
nil))

(defn ^:macro-support expand-spy [env expr forms]
`(SpyWidget ~(:key env) (-widget-cont ~(assoc env :key nil) ~@forms)
Expand Down Expand Up @@ -777,7 +876,12 @@
:padding (expand-padding env v forms)
:color (expand-color env v forms)
(:height :width) (expand-height-width env (list* k v forms))
:when (expand-visible env v forms))))
:when (expand-visible env v forms)
:animate
(if-some [[binding expr & more] (seq v)]
(let [[opts & more] (collect-options more #{:duration :curve :on-end :lerp})]
(expand-animate &env env binding expr opts (list* :animate more forms)))
`(-widget-cont ~env ~@forms)))))

(defmacro -widget-cont
"PRIVATE DONT USE"
Expand Down
6 changes: 6 additions & 0 deletions samples/animated_string/deps.edn
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{:paths ["src"] ; where your cljd files are
:deps {org.clojure/clojure {:mvn/version "1.10.1"}
tensegritics/clojuredart {:local/root "../../"}}
:aliases {:cljd {:main-opts ["-m" "cljd.build"]}}
:cljd/opts {:main sample.animated-string
:kind :flutter}}
41 changes: 41 additions & 0 deletions samples/animated_string/src/sample/animated_string.cljd
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
(ns sample.animated-string
(:require
["package:flutter/material.dart" :as m]
[cljd.flutter :as f]))

(def animated-text
(f/widget
(m/Scaffold
.appBar (m/AppBar .title (m/Text ":animate demo")))
.body
:watch [text (atom "") :as *text]
m/Column
.children
[(m/Text "Enter text below and press enter:")
(m/TextField
.onSubmitted (fn [x] (reset! *text x) nil))
(m/Text "See text being slowly animated (interpolated) between actual value.")
(m/Text "(Try changing text again while the animation is running.)")
(f/widget
:animate [s text
:duration (Duration .seconds 3)
:lerp (fn [from to]
(if (= from to)
(constantly to)
(let [common (count (take-while true? (map = from to)))
nfrom (- (count from) common)
nto (- (count to) common)
n (+ nfrom nto)
threshold (/ nfrom (double n))]
(fn [t]
(if (< t threshold)
(subs from 0 (+ common (int (* n (- threshold t)))))
(subs to 0 (+ common (int (* n (- t threshold))))))))))]
(m/Text (str s "◼️")
.style (m/TextStyle .fontSize 36 .fontFamily "courier")))
(m/Text "Animated length but with a shorter duration")
(f/widget
:animate [n (count text)]
(m/Text (str "n=" n)))]))

(defn main [] (f/run m/MaterialApp .home animated-text))

0 comments on commit b152be0

Please sign in to comment.