Add autoscroll and animation to intro carousel
Add cancelable loop animation Set index manually Increase progress bar size Signed-off-by: Gheorghe Pinzaru <feross95@gmail.com>
This commit is contained in:
parent
b5fda1234e
commit
e290881bf8
|
@ -51,7 +51,8 @@
|
||||||
(def bezier (.-bezier ^js Easing))
|
(def bezier (.-bezier ^js Easing))
|
||||||
(def linear (.-linear ^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-out (bezier 0 0 0.58 1)
|
||||||
:ease-in-out (bezier 0.42 0 0.58 1)})
|
:ease-in-out (bezier 0.42 0 0.58 1)})
|
||||||
|
|
||||||
|
@ -182,6 +183,31 @@
|
||||||
(defn snap-point [value velocity snap-points]
|
(defn snap-point [value velocity snap-points]
|
||||||
(.snapPoint ^js redash value velocity (to-array 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
|
(defn with-easing
|
||||||
[{val :value
|
[{val :value
|
||||||
:keys [snap-points velocity offset state easing duration on-snap]
|
:keys [snap-points velocity offset state easing duration on-snap]
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
(def platform (.-Platform ^js rn))
|
(def platform (.-Platform ^js rn))
|
||||||
|
|
||||||
(def view (reagent/adapt-react-class (.-View ^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 text (reagent/adapt-react-class (.-Text ^js rn)))
|
||||||
|
|
||||||
(def scroll-view (reagent/adapt-react-class (.-ScrollView ^js rn)))
|
(def scroll-view (reagent/adapt-react-class (.-ScrollView ^js rn)))
|
||||||
|
|
|
@ -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)}]]))))
|
|
@ -1,33 +1,38 @@
|
||||||
(ns status-im.ui.screens.intro.styles
|
(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
|
(def intro-view
|
||||||
{:flex 1
|
{:flex 1
|
||||||
:justify-content :flex-end
|
:justify-content :flex-end
|
||||||
:margin-bottom 12})
|
:margin-bottom 12})
|
||||||
|
|
||||||
(defn dot-selector [n]
|
(defn dot-selector []
|
||||||
(let [diameter 6
|
|
||||||
interval 10]
|
|
||||||
{:flex-direction :row
|
{:flex-direction :row
|
||||||
:justify-content :space-between
|
:justify-content :space-between
|
||||||
:align-items :center
|
:align-items :center})
|
||||||
:height diameter
|
|
||||||
:width (+ diameter (* (+ diameter interval)
|
|
||||||
(dec n)))}))
|
|
||||||
|
|
||||||
(defn dot [color selected?]
|
(defn dot-style [active]
|
||||||
{:background-color (if selected?
|
{:background-color colors/blue-light
|
||||||
color
|
:overflow :hidden
|
||||||
(colors/alpha color 0.2))
|
:opacity 1
|
||||||
:width 6
|
:margin-horizontal 2
|
||||||
:height 6
|
:width (animated/mix active dot-size progress-size)
|
||||||
|
:height dot-size
|
||||||
:border-radius 3})
|
: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
|
(def wizard-title
|
||||||
{:typography :header
|
{:margin-bottom 16})
|
||||||
:text-align :center
|
|
||||||
:margin-bottom 16})
|
|
||||||
|
|
||||||
(def wizard-text
|
(def wizard-text
|
||||||
{:color colors/gray
|
{:color colors/gray
|
||||||
|
|
|
@ -18,85 +18,16 @@
|
||||||
[status-im.utils.identicon :as identicon]
|
[status-im.utils.identicon :as identicon]
|
||||||
[status-im.utils.platform :as platform]
|
[status-im.utils.platform :as platform]
|
||||||
[status-im.utils.security :as security]
|
[status-im.utils.security :as security]
|
||||||
|
[status-im.ui.screens.intro.carousel :as carousel]
|
||||||
[quo.core :as quo]
|
[quo.core :as quo]
|
||||||
[status-im.utils.utils :as utils])
|
[status-im.utils.utils :as utils])
|
||||||
(:require-macros [status-im.utils.views :refer [defview letsubs]]))
|
(: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 []
|
(defview intro []
|
||||||
(letsubs [{window-height :height} [:dimensions/window]
|
(letsubs [{window-height :height} [:dimensions/window]
|
||||||
view-id [:view-id]]
|
view-id [:view-id]]
|
||||||
[react/view {:style styles/intro-view}
|
[react/view {:style styles/intro-view}
|
||||||
[intro-viewer [{:image (resources/get-theme-image :chat)
|
[carousel/viewer [{:image (resources/get-theme-image :chat)
|
||||||
:title :intro-title1
|
:title :intro-title1
|
||||||
:text :intro-text1}
|
:text :intro-text1}
|
||||||
{:image (resources/get-theme-image :wallet)
|
{:image (resources/get-theme-image :wallet)
|
||||||
|
|
Loading…
Reference in New Issue