Skip to content

Commit

Permalink
Making chains a list. Allowing to interrupt bouncing animations if sc…
Browse files Browse the repository at this point in the history
…rolling on the opposite direction.
  • Loading branch information
jchavarri committed Feb 10, 2019
1 parent 6d37395 commit ed68875
Show file tree
Hide file tree
Showing 2 changed files with 70 additions and 36 deletions.
61 changes: 41 additions & 20 deletions src/UI/Animation.re
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ module type AnimationTicker = {
let onTick: Event.t(Time.t);
};

let optCall = (opt, param) =>
switch (opt) {
| Some(f) => f(param)
| None => ()
};

module Make = (AnimationTickerImpl: AnimationTicker) => {
type animationValue = {mutable current: float};

Expand Down Expand Up @@ -84,7 +90,7 @@ module Make = (AnimationTickerImpl: AnimationTicker) => {
};
newton(aX, 0);
};
let get = aX => {
let get = aX =>
if (mX1 === mY1 && mX2 === mY2) {
aX;
} else if (aX <= 0.) {
Expand All @@ -94,7 +100,6 @@ module Make = (AnimationTickerImpl: AnimationTicker) => {
} else {
calcBezier(getTForX(aX), mY1, mY2);
};
};
get;
};
};
Expand Down Expand Up @@ -150,17 +155,11 @@ module Make = (AnimationTickerImpl: AnimationTicker) => {
let newT = getLocalTime(clock, anim);
anim.value.current = interpolate(newT, anim.startValue, anim.toValue);
} else {
switch (complete) {
| Some(c) => c()
| _ => ()
};
optCall(complete, ());
};
} else {
anim.value.current = interpolate(t, anim.startValue, anim.toValue);
switch (update) {
| Some(u) => u(anim.value.current)
| _ => ()
};
optCall(update, anim.value.current);
};
};

Expand Down Expand Up @@ -206,17 +205,39 @@ module Make = (AnimationTickerImpl: AnimationTicker) => {
};

module Chain = {
type t = {
first: animation,
second: animation,
type t = {animations: list(animation)};
let make = animation => {animations: [animation]};
let add = (animation, {animations: l}) => {
animations: List.cons(animation, l),
};
let start = (~update=?, ~complete=?, {animations: l}) => {
let currentPlayback = ref(None);
let rec runAnimation = (animations, index) => {
switch (animations) {
| [a, ...xl] =>
let playback =
a |> start(~update?, ~complete=() => runAnimation(xl, index + 1));
currentPlayback := Some(playback);
| [] => optCall(complete, ())
};
};
runAnimation(List.rev(l), 0);
let playback = {
pause: () => {
switch (currentPlayback^) {
| Some(p) => p.pause()
| None => ()
};
},
stop: () => {
switch (currentPlayback^) {
| Some(p) => p.stop()
| None => ()
};
},
};
playback;
};
let make = (second, first) => {first, second};
let start = (~update, ~complete, {first, second}) =>
first
|> start(~update, ~complete=() =>
second |> start(~update, ~complete) |> ignore
)
|> ignore;
};

let cancel = (anim: animation) =>
Expand Down
45 changes: 29 additions & 16 deletions src/UI_Components/ScrollView.re
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@ open Revery_Core;
open Revery_UI;
open Revery_UI.Transform;

type direction =
| Top
| Bottom;
type bouncingState =
| Transitioning
| Bouncing(direction, Animated.playback)
| Idle;

let component = React.component("ScrollView");
Expand Down Expand Up @@ -113,11 +116,16 @@ let make =
let isAtBottom = newScrollTop > maxHeight;

switch (bouncingState) {
| Transitioning => ()
| Bouncing(Top, playback) when wheelEvent.deltaY < 0. =>
playback.stop();
setBouncingState(Idle);
| Bouncing(Bottom, playback) when wheelEvent.deltaY > 0. =>
playback.stop();
setBouncingState(Idle);
| Bouncing(_) => ()
| Idle when isAtTop || isAtBottom =>
open Animated;
setBouncingState(Transitioning);

let direction = isAtTop ? Top : Bottom;
let bounceAwayAnim = {
toValue: float_of_int(newScrollTop),
duration: Milliseconds(100.),
Expand All @@ -132,18 +140,23 @@ let make =
repeat: false,
easing: Animated.cubicBezier(0.23, 0., 0.32, 1.),
};
tween(floatValue(float_of_int(actualScrollTop)), bounceAwayAnim)
|> Chain.make(
tween(
floatValue(float_of_int(newScrollTop)),
bounceBackAnim,
),
)
|> Chain.start(
~update=v => setScrollTop(int_of_float(v)),
~complete=() => setBouncingState(Idle),
)
|> ignore;
let playback =
tween(
floatValue(float_of_int(actualScrollTop)),
bounceAwayAnim,
)
|> Chain.make
|> Chain.add(
tween(
floatValue(float_of_int(newScrollTop)),
bounceBackAnim,
),
)
|> Chain.start(
~update=v => setScrollTop(int_of_float(v)),
~complete=() => setBouncingState(Idle),
);
setBouncingState(Bouncing(direction, playback));
| Idle => setScrollTop(newScrollTop)
};
};
Expand Down

0 comments on commit ed68875

Please sign in to comment.