Skip to content

Commit

Permalink
Add autoscroll and animation to intro carousel
Browse files Browse the repository at this point in the history
Add cancelable loop animation

Set index manually

Increase progress bar size

Signed-off-by: Gheorghe Pinzaru <feross95@gmail.com>
  • Loading branch information
Ferossgp committed Jul 1, 2020
1 parent b5fda12 commit e290881
Show file tree
Hide file tree
Showing 5 changed files with 193 additions and 102 deletions.
28 changes: 27 additions & 1 deletion src/quo/animated.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,8 @@
(def bezier (.-bezier ^js Easing))
(def linear (.-linear ^js Easing))

(def easings {:ease-in (bezier 0.42 0 1 1)
(def easings {:linear linear
:ease-in (bezier 0.42 0 1 1)
:ease-out (bezier 0 0 0.58 1)
:ease-in-out (bezier 0.42 0 0.58 1)})

Expand Down Expand Up @@ -182,6 +183,31 @@
(defn snap-point [value velocity snap-points]
(.snapPoint ^js redash value velocity (to-array snap-points)))

(defn cancelable-loop
[{:keys [clock duration finished on-reach]}]
(let [time (value 0)
frame-time (value 0)
position (value 0)
to-value (value 1)
state {:time time
:frameTime frame-time
:finished finished
:position position}
config {:toValue to-value
:duration duration
:easing (:linear easings)}]
(block
[(timing clock state config)
(cond* (and* finished
(eq position to-value))
(call* [] on-reach))
(cond* finished
[(set finished 0)
(set time 0)
(set frame-time 0)
(set position 0)])
position])))

(defn with-easing
[{val :value
:keys [snap-points velocity offset state easing duration on-snap]
Expand Down
2 changes: 1 addition & 1 deletion src/quo/react_native.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
(def platform (.-Platform ^js rn))

(def view (reagent/adapt-react-class (.-View ^js rn)))

(def image (reagent/adapt-react-class (.-Image rn)))
(def text (reagent/adapt-react-class (.-Text ^js rn)))

(def scroll-view (reagent/adapt-react-class (.-ScrollView ^js rn)))
Expand Down
129 changes: 129 additions & 0 deletions src/status_im/ui/screens/intro/carousel.cljs
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
(ns status-im.ui.screens.intro.carousel
(:require [quo.animated :as animated]
[quo.react-native :as rn]
[quo.core :as quo]
[reagent.core :as r]
[status-im.i18n :as i18n]
[status-im.ui.screens.intro.styles :as styles]))

(defn dot []
(let [active (animated/value 0)
active-transition (animated/with-timing-transition active {:duration 100})]
(fn [{:keys [selected progress]}]
[animated/view {:style (styles/dot-style active-transition)}
[animated/code {:exec (animated/set active (if selected 1 0))}]
[animated/view {:style (styles/dot-progress active-transition progress)}]])))

(defn dots-selector [{:keys [n selected progress]}]
[rn/view {:style (styles/dot-selector)}
(for [i (range n)]
^{:key i}
[dot {:progress progress
:selected (= selected i)}])])

(defn viewer [slides window-height _]
(let [scroll-x (r/atom 0)
scroll-view-ref (atom nil)
width (r/atom 0)
height (r/atom 0)
text-height (r/atom 0)
index (r/atom 0)
manual-scroll (atom false)
text-temp-height (atom 0)
text-temp-timer (atom nil)
bottom-margin (if (> window-height 600) 32 16)
progress (animated/value 1)
autoscroll (animated/value 1)
finished (animated/value 0)
clock (animated/clock)
go-next (fn []
(let [x (if (>= @scroll-x (* (dec (count slides))
@width))
0
(+ @scroll-x @width))]
(reset! index (Math/round (/ x @width)))
(some-> ^js @scroll-view-ref (.scrollTo #js {:x x :animated true}))))
code (animated/block
[(animated/cond* (animated/and* (animated/not* (animated/clock-running clock))
autoscroll)
(animated/start-clock clock))
(animated/cond* (animated/and* (animated/clock-running clock)
(animated/not* autoscroll))
[(animated/stop-clock clock)
(animated/set finished 1)])
(animated/set progress (animated/cancelable-loop
{:clock clock
:finished finished
:duration 4000
:on-reach go-next}))])
cancel-animation (fn []
(reset! manual-scroll true)
(animated/set-value autoscroll 0))
restart-animation (fn []
(animated/set-value autoscroll 1))]
(fn [_ _ view-id]
(let [current-screen? (or (nil? view-id) (= view-id :intro))]
[rn/view {:style {:align-items :center
:flex 1
:margin-bottom bottom-margin
:justify-content :flex-end}
:on-layout (fn [^js e]
(when current-screen?
(reset! width (-> e .-nativeEvent .-layout .-width))))}
(when current-screen?
[animated/code {:exec code}])
[rn/scroll-view {:horizontal true
:paging-enabled true
:ref #(reset! scroll-view-ref %)
:shows-vertical-scroll-indicator false
:shows-horizontal-scroll-indicator false
:pinch-gesture-enabled false
:scroll-event-throttle 16
:on-scroll #(let [x (.-nativeEvent.contentOffset.x ^js %)]
(when @manual-scroll
;; NOTE: Will be not synced if velocity is big
(reset! index (Math/round (/ x @width))))
(reset! scroll-x x))
:on-scroll-begin-drag cancel-animation
:on-scroll-end-drag restart-animation
:on-momentum-scroll-end #(reset! manual-scroll false)
:style {:margin-bottom bottom-margin}}
(doall
(for [s slides]
^{:key (:title s)}
[rn/view {:style {:flex 1
:width @width
:justify-content :flex-end
:align-items :center
:padding-horizontal 32}}
(let [size (min @width @height)]
[rn/view {:style {:flex 1}
:on-layout (fn [^js e]
(let [new-height (-> e .-nativeEvent .-layout .-height)]
(when current-screen?
(swap! height #(if (pos? %) (min % new-height) new-height)))))}
[rn/image {:source (:image s)
:resize-mode :contain
:style {:width size
:height size}}]])
[quo/text {:style styles/wizard-title
:align :center
:weight :bold
:size :x-large}
(i18n/label (:title s))]
[quo/text {:style (styles/wizard-text-with-height @text-height)
:on-layout
(fn [^js e]
(let [new-height (-> e .-nativeEvent .-layout .-height)]
(when (and current-screen?
(not= new-height @text-temp-height)
(not (zero? new-height))
(< new-height 200))
(swap! text-temp-height #(if (pos? %) (max % new-height) new-height))
(when @text-temp-timer (js/clearTimeout @text-temp-timer))
(reset! text-temp-timer
(js/setTimeout #(reset! text-height @text-temp-height) 500)))))}
(i18n/label (:text s))]]))]
[dots-selector {:selected @index
:progress progress
:n (count slides)}]]))))
47 changes: 26 additions & 21 deletions src/status_im/ui/screens/intro/styles.cljs
Original file line number Diff line number Diff line change
@@ -1,33 +1,38 @@
(ns status-im.ui.screens.intro.styles
(:require [status-im.ui.components.colors :as colors]))
(:require [status-im.ui.components.colors :as colors]
[quo.animated :as animated]))

(def dot-size 6)
(def progress-size 36)

(def intro-view
{:flex 1
:justify-content :flex-end
:margin-bottom 12})

(defn dot-selector [n]
(let [diameter 6
interval 10]
{:flex-direction :row
:justify-content :space-between
:align-items :center
:height diameter
:width (+ diameter (* (+ diameter interval)
(dec n)))}))

(defn dot [color selected?]
{:background-color (if selected?
color
(colors/alpha color 0.2))
:width 6
:height 6
:border-radius 3})
(defn dot-selector []
{:flex-direction :row
:justify-content :space-between
:align-items :center})

(defn dot-style [active]
{:background-color colors/blue-light
:overflow :hidden
:opacity 1
:margin-horizontal 2
:width (animated/mix active dot-size progress-size)
:height dot-size
:border-radius 3})

(defn dot-progress [active progress]
{:background-color colors/blue
:height dot-size
:width progress-size
:opacity (animated/mix active 0 1)
:transform [{:translateX (animated/mix progress (- progress-size) 0)}]})

(def wizard-title
{:typography :header
:text-align :center
:margin-bottom 16})
{:margin-bottom 16})

(def wizard-text
{:color colors/gray
Expand Down
89 changes: 10 additions & 79 deletions src/status_im/ui/screens/intro/views.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -18,93 +18,24 @@
[status-im.utils.identicon :as identicon]
[status-im.utils.platform :as platform]
[status-im.utils.security :as security]
[status-im.ui.screens.intro.carousel :as carousel]
[quo.core :as quo]
[status-im.utils.utils :as utils])
(:require-macros [status-im.utils.views :refer [defview letsubs]]))

(defn dots-selector [{:keys [n selected color]}]
[react/view {:style (styles/dot-selector n)}
(doall
(for [i (range n)]
^{:key i}
[react/view {:style (styles/dot color (selected i))}]))])

(defn intro-viewer [slides window-height _]
(let [scroll-x (r/atom 0)
scroll-view-ref (atom nil)
width (r/atom 0)
height (r/atom 0)
text-height (r/atom 0)
text-temp-height (atom 0)
text-temp-timer (atom nil)
bottom-margin (if (> window-height 600) 32 16)]
(fn [_ _ view-id]
(let [current-screen? (or (nil? view-id) (= view-id :intro))]
[react/view {:style {:align-items :center
:flex 1
:margin-bottom bottom-margin
:justify-content :flex-end}
:on-layout (fn [^js e]
(when current-screen?
(reset! width (-> e .-nativeEvent .-layout .-width))))}
[react/scroll-view {:horizontal true
:paging-enabled true
:ref #(reset! scroll-view-ref %)
:shows-vertical-scroll-indicator false
:shows-horizontal-scroll-indicator false
:pinch-gesture-enabled false
:on-scroll #(let [^js x (.-nativeEvent.contentOffset.x ^js %)]
(reset! scroll-x x))
:style {:margin-bottom bottom-margin}}
(doall
(for [s slides]
^{:key (:title s)}
[react/view {:style {:flex 1
:width @width
:justify-content :flex-end
:align-items :center
:padding-horizontal 32}}
(let [size (min @width @height)]
[react/view {:style {:flex 1}
:on-layout (fn [^js e]
(let [new-height (-> e .-nativeEvent .-layout .-height)]
(when current-screen?
(swap! height #(if (pos? %) (min % new-height) new-height)))))}
[react/image {:source (:image s)
:resize-mode :contain
:style {:width size
:height size}}]])
[react/i18n-text {:style styles/wizard-title :key (:title s)}]
[react/text {:style (styles/wizard-text-with-height @text-height)
:on-layout
(fn [^js e]
(let [new-height (-> e .-nativeEvent .-layout .-height)]
(when (and current-screen?
(not= new-height @text-temp-height)
(not (zero? new-height))
(< new-height 200))
(swap! text-temp-height #(if (pos? %) (max % new-height) new-height))
(when @text-temp-timer (js/clearTimeout @text-temp-timer))
(reset! text-temp-timer
(js/setTimeout #(reset! text-height @text-temp-height) 500)))))}
(i18n/label (:text s))]]))]
(let [selected (hash-set (quot (int @scroll-x) (int @width)))]
[dots-selector {:selected selected :n (count slides)
:color colors/blue}])]))))

(defview intro []
(letsubs [{window-height :height} [:dimensions/window]
view-id [:view-id]]
[react/view {:style styles/intro-view}
[intro-viewer [{:image (resources/get-theme-image :chat)
:title :intro-title1
:text :intro-text1}
{:image (resources/get-theme-image :wallet)
:title :intro-title2
:text :intro-text2}
{:image (resources/get-theme-image :browser)
:title :intro-title3
:text :intro-text3}] window-height view-id]
[carousel/viewer [{:image (resources/get-theme-image :chat)
:title :intro-title1
:text :intro-text1}
{:image (resources/get-theme-image :wallet)
:title :intro-title2
:text :intro-text2}
{:image (resources/get-theme-image :browser)
:title :intro-title3
:text :intro-text3}] window-height view-id]
[react/view styles/buttons-container
[components.common/button {:button-style (assoc styles/bottom-button :margin-bottom 16)
:on-press #(re-frame/dispatch [:multiaccounts.create.ui/intro-wizard])
Expand Down

0 comments on commit e290881

Please sign in to comment.