Created
March 18, 2016 08:54
-
-
Save raspasov/f9ca712571efd932169e to your computer and use it in GitHub Desktop.
ClojureScript Animation Library
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(ns my-project.cljs-animate | |
(:require-macros [cljs.core.async.macros :refer [go]]) | |
(:require [cljs.core.async :refer [offer! put! promise-chan chan <! >! timeout alts! chan timeout dropping-buffer sliding-buffer]])) | |
;THIS IS A COPY/PASTE of https://github.com/gstamp/tween-clj | |
;TODO make tween-clj a ClojureScript lib (easy) | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;; Transition types | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
(defn transition-linear | |
"Calculates a linear transition. | |
'pos' is a number between 0 and 1 representing the position of the transition between | |
starting (0) and finished (1)" | |
^double | |
[^double pos] pos) | |
(defn transition-pow | |
"Calculates a power transition. | |
'x' the the power to raise to. | |
'pos' is a number between 0 and 1 representing the position of the transition between | |
starting (0) and finished (1)" | |
(^double [^double pos] (transition-pow 6 pos)) | |
(^double [^double x ^double pos] (js/Math.pow pos x))) | |
(defn transition-expo | |
"Calculates a exponential transition. | |
'pos' is a number between 0 and 1 representing the position of the transition between | |
starting (0) and finished (1)" | |
^double | |
[^double pos] | |
(js/Math.pow 2 (* 8 (- pos 1)))) | |
(defn transition-sine | |
"Calculates a sineousidal transition. | |
'pos' is a number between 0 and 1 representing the position of the transition between | |
starting (0) and finished (1)" | |
^double | |
[^double pos] | |
(- (double 1) | |
(js/Math.sin (/ (* (- (double 1) pos) | |
js/Math.PI) | |
(double 2))))) | |
(defn transition-circ | |
"Calculates a circular transition. | |
'pos' is a number between 0 and 1 representing the position of the transition between | |
starting (0) and finished (1)" | |
^double | |
[^double pos] | |
(- 1 (js/Math.sin (js/Math.acos pos)))) | |
(defn transition-back | |
"Calculates a transition that moves backwards before moving forwards. | |
'x' controls the bounceback size. | |
'pos' is a number between 0 and 1 representing the position of the transition between | |
starting (0) and finished (1)" | |
(^double [^double pos] (transition-back (double 1.618) pos)) | |
(^double [^double x ^double pos] (* (js/Math.pow pos (double 2)) | |
(- (* (inc x) pos) x)))) | |
(defn transition-bounce | |
"Calculates a transition that looks somewhat like a bouncing ball. | |
'pos' is a number between 0 and 1 representing the position of the transition between | |
starting (0) and finished (1)" | |
^double | |
[^double pos] | |
(loop [a (double 0) b (double 1)] | |
(if (or (>= pos (/ (- 7 (* 4 a)) 11)) | |
(not (<= 0 pos 1.0 ))) | |
(- (* b b) | |
(js/Math.pow (/ (- 11 (* 6 a) (* 11 pos)) 4) | |
2)) | |
(recur (+ a b) (/ b 2))))) | |
(defn transition-elastic | |
"Calculates a transition that looks somewhat like a bouncing ball. | |
'x' controls the elasticity | |
'pos' is a number between 0 and 1 representing the position of the transition between | |
starting (0) and finished (1)" | |
(^double [^double pos] (transition-elastic (double 1) pos)) | |
(^double [^double x ^double pos] | |
(* (js/Math.pow (double 2) (* (double 10) (dec pos))) | |
(js/Math.cos (/ (* (double 20) (dec pos) js/Math.PI x) | |
(double 3)))))) | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;; Easing functions | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
(defn ease-in | |
"Eases into the transition where | |
'transition' is a function defining the transition type and | |
'p' is a number between 0 and 1 representing the position of the transition between | |
starting (0) and finished (1)" | |
^double | |
[transition ^double p] | |
(transition p)) | |
(defn ease-out | |
"Eases out of a transition where | |
'transition' is a function defining the transition type and | |
'p' is a number between 0 and 1 representing the position of the transition between | |
starting (0) and finished (1)" | |
^double | |
[transition ^double p] | |
(- (double 1) (transition (- (double 1) p) ))) | |
(defn ease-in-out | |
"Eases in to then out of of a transition where | |
'transition' is a function defining the transition type and | |
'p' is a number between 0 and 1 representing the position of the transition between | |
starting (0) and finished (1)" | |
^double | |
[transition ^double p] | |
(if (<= p (double 0.5)) | |
(/ (transition (* (double 2) p)) | |
(double 2)) | |
(/ (- 2 (transition (* (double 2) (- (double 1) p)))) | |
(double 2)))) | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;;; Extra functions | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
(defn constrain | |
"Constrains a value to not exceed a maximum and minimum value." | |
^double | |
[^double amt ^double low ^double high] | |
(if (< amt low) low | |
(if (> amt high) | |
high | |
amt))) | |
(defn range-to-p | |
"Converts the current position in a range into a p value for use in the transition functions. | |
'start' is the starting value of the range | |
'end' is the ending value of the range | |
'current' is the current value between 'start' and 'end'" | |
^double | |
[^double start ^double end ^double current] | |
(constrain | |
(/ (- current start) (- end start)) | |
(double 0) | |
(double 1))) | |
(defn p-to-range | |
"Converts a p value (between 0 & 1) back into a range between start and end. | |
'start' is the starting value of the range | |
'end' is the ending value of the range | |
'p' is a value between 0 & 1." | |
^double | |
[^double start ^double end ^double p] | |
(constrain | |
(+ start | |
(* p (- end start))) | |
start | |
end)) | |
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; | |
;END OF COPY/PASTE | |
;Start cljs-animate library | |
(defn animation-stateful-transducer-ch | |
[^cljs.core/Atom from-to anim-f] | |
(chan | |
(sliding-buffer 1) | |
(map (fn [[x [easing-fn transition-fn]]] | |
;dynamically resolve from and to from an atom | |
(let [[start end] @from-to | |
[start end] (if (< end start) [end start] [start end]) | |
v (cond | |
(= x start) | |
start | |
(= x end) | |
end | |
:else | |
(p-to-range start end (easing-fn transition-fn (range-to-p start end x))))] | |
(anim-f v) | |
v))))) | |
(def default [ease-in transition-expo]) | |
(def linear [ease-in transition-linear]) | |
(def ease-out [ease-out transition-expo]) | |
(def ease-in-out [ease-in-out transition-pow]) | |
(defn animated-value-fn! | |
"This is the core of the lib. Give : ) it's ok. ITS ON GIT lol. yea what is that. I've heard of Dropbox. | |
That's easy. Only thing you need to change is.... | |
a-fn takes one argument, the old value, and returns the new value" | |
([animated-value a-fn duration-ms] | |
(animated-value-fn! animated-value a-fn duration-ms default)) | |
([{:keys [from-to anim-xf-ch ^cljs.core/IFn get-last-value]} a-fn duration-ms anim-config] | |
(let [;get last value | |
from (get-last-value) | |
to (a-fn from) | |
;set the new animation state | |
new-from-to (reset! from-to [from to]) | |
num-of-updates (int (* (/ duration-ms 1000) 60)) | |
diff (- to from) | |
step (float (/ diff num-of-updates)) | |
values-range (range from to step)] | |
(go (loop [values values-range] | |
;at every frame check if this loop is still valid to proceed | |
(if (= new-from-to @from-to) | |
(if-let [value (first values)] | |
(do | |
(>! anim-xf-ch [value anim-config]) | |
(<! (timeout 17)) | |
(recur (rest values))) | |
(do | |
;final value | |
(>! anim-xf-ch [to anim-config]) | |
to)))))))) | |
(defn animated-value-to! | |
"Animated directly to a value" | |
([animated-value to duration-ms] | |
(animated-value-to! animated-value to duration-ms default)) | |
([animated-value to duration-ms anim-config] | |
(animated-value-fn! animated-value (fn [_] to) duration-ms anim-config))) | |
(defn init-animated-value-reagent | |
"local-atom - local atom that holds the state to be animated, | |
i.e. an opacity, position (left, right, top, etc) or any other HTML element property | |
k - path to the key inside local-atom that's going to be animated, i.e. [:a :b :c], always a vector" | |
[local-atom k] | |
(let [from-to (atom [(get-in local-atom k) (get-in local-atom k)]) | |
anim-f (fn [v] (swap! local-atom assoc-in k v)) | |
c (animation-stateful-transducer-ch from-to anim-f)] | |
{:from-to from-to :anim-xf-ch c :get-last-value (fn [] (get-in local-atom k))})) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment