mirror of
https://github.com/penpot/penpot.git
synced 2025-05-23 05:06:13 +02:00
🎉 Add animations to interactions
This commit is contained in:
parent
24062beebe
commit
81cbc33dbb
24 changed files with 1479 additions and 160 deletions
|
@ -9,6 +9,7 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.pages :as cp]
|
||||
[app.common.spec :as us]
|
||||
[app.common.types.interactions :as cti]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.constants :as c]
|
||||
[app.main.data.comments :as dcm]
|
||||
|
@ -316,6 +317,12 @@
|
|||
(update [_ state]
|
||||
(d/dissoc-in state [:viewer-local :nav-scroll]))))
|
||||
|
||||
(defn complete-animation
|
||||
[]
|
||||
(ptk/reify ::complete-animation
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(d/dissoc-in state [:viewer-local :current-animation]))))
|
||||
|
||||
;; --- Navigation inside page
|
||||
|
||||
|
@ -335,23 +342,38 @@
|
|||
(rx/of (rt/nav screen pparams (assoc qparams :index index)))))))
|
||||
|
||||
(defn go-to-frame
|
||||
[frame-id]
|
||||
(us/verify ::us/uuid frame-id)
|
||||
(ptk/reify ::go-to-frame
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(assoc-in state [:viewer-local :overlays] []))
|
||||
([frame-id] (go-to-frame frame-id nil))
|
||||
([frame-id animation]
|
||||
(us/verify ::us/uuid frame-id)
|
||||
(us/verify (s/nilable ::cti/animation) animation)
|
||||
(ptk/reify ::go-to-frame
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(let [route (:route state)
|
||||
qparams (:query-params route)
|
||||
page-id (:page-id qparams)
|
||||
index (:index qparams)
|
||||
frames (get-in state [:viewer :pages page-id :frames])
|
||||
frame (get frames index)]
|
||||
(cond-> state
|
||||
:always
|
||||
(assoc-in [:viewer-local :overlays] [])
|
||||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [route (:route state)
|
||||
qparams (:query-params route)
|
||||
page-id (:page-id qparams)
|
||||
(some? animation)
|
||||
(assoc-in [:viewer-local :current-animation]
|
||||
{:kind :go-to-frame
|
||||
:orig-frame-id (:id frame)
|
||||
:animation animation}))))
|
||||
|
||||
frames (get-in state [:viewer :pages page-id :frames])
|
||||
index (d/index-of-pred frames #(= (:id %) frame-id))]
|
||||
(when index
|
||||
(rx/of (go-to-frame-by-index index)))))))
|
||||
ptk/WatchEvent
|
||||
(watch [_ state _]
|
||||
(let [route (:route state)
|
||||
qparams (:query-params route)
|
||||
page-id (:page-id qparams)
|
||||
frames (get-in state [:viewer :pages page-id :frames])
|
||||
index (d/index-of-pred frames #(= (:id %) frame-id))]
|
||||
(when index
|
||||
(rx/of (go-to-frame-by-index index))))))))
|
||||
|
||||
(defn go-to-frame-auto
|
||||
[]
|
||||
|
@ -383,12 +405,39 @@
|
|||
|
||||
;; --- Overlays
|
||||
|
||||
(defn- do-open-overlay
|
||||
[state frame position close-click-outside background-overlay animation]
|
||||
(cond-> state
|
||||
:always
|
||||
(update-in [:viewer-local :overlays] conj
|
||||
{:frame frame
|
||||
:position position
|
||||
:close-click-outside close-click-outside
|
||||
:background-overlay background-overlay})
|
||||
(some? animation)
|
||||
(assoc-in [:viewer-local :current-animation]
|
||||
{:kind :open-overlay
|
||||
:overlay-id (:id frame)
|
||||
:animation animation})))
|
||||
|
||||
(defn- do-close-overlay
|
||||
[state frame-id animation]
|
||||
(if (nil? animation)
|
||||
(update-in state [:viewer-local :overlays]
|
||||
(fn [overlays]
|
||||
(d/removev #(= (:id (:frame %)) frame-id) overlays)))
|
||||
(assoc-in state [:viewer-local :current-animation]
|
||||
{:kind :close-overlay
|
||||
:overlay-id frame-id
|
||||
:animation animation})))
|
||||
|
||||
(defn open-overlay
|
||||
[frame-id position close-click-outside background-overlay]
|
||||
[frame-id position close-click-outside background-overlay animation]
|
||||
(us/verify ::us/uuid frame-id)
|
||||
(us/verify ::us/point position)
|
||||
(us/verify (s/nilable ::us/boolean) close-click-outside)
|
||||
(us/verify (s/nilable ::us/boolean) background-overlay)
|
||||
(us/verify (s/nilable ::cti/animation) animation)
|
||||
(ptk/reify ::open-overlay
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
|
@ -399,19 +448,21 @@
|
|||
frame (d/seek #(= (:id %) frame-id) frames)
|
||||
overlays (get-in state [:viewer-local :overlays])]
|
||||
(if-not (some #(= (:frame %) frame) overlays)
|
||||
(update-in state [:viewer-local :overlays] conj
|
||||
{:frame frame
|
||||
:position position
|
||||
:close-click-outside close-click-outside
|
||||
:background-overlay background-overlay})
|
||||
(do-open-overlay state
|
||||
frame
|
||||
position
|
||||
close-click-outside
|
||||
background-overlay
|
||||
animation)
|
||||
state)))))
|
||||
|
||||
(defn toggle-overlay
|
||||
[frame-id position close-click-outside background-overlay]
|
||||
[frame-id position close-click-outside background-overlay animation]
|
||||
(us/verify ::us/uuid frame-id)
|
||||
(us/verify ::us/point position)
|
||||
(us/verify (s/nilable ::us/boolean) close-click-outside)
|
||||
(us/verify (s/nilable ::us/boolean) background-overlay)
|
||||
(us/verify (s/nilable ::cti/animation) animation)
|
||||
(ptk/reify ::toggle-overlay
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
|
@ -422,23 +473,27 @@
|
|||
frame (d/seek #(= (:id %) frame-id) frames)
|
||||
overlays (get-in state [:viewer-local :overlays])]
|
||||
(if-not (some #(= (:frame %) frame) overlays)
|
||||
(update-in state [:viewer-local :overlays] conj
|
||||
{:frame frame
|
||||
:position position
|
||||
:close-click-outside close-click-outside
|
||||
:background-overlay background-overlay})
|
||||
(update-in state [:viewer-local :overlays]
|
||||
(fn [overlays]
|
||||
(d/removev #(= (:id (:frame %)) frame-id) overlays))))))))
|
||||
(do-open-overlay state
|
||||
frame
|
||||
position
|
||||
close-click-outside
|
||||
background-overlay
|
||||
animation)
|
||||
(do-close-overlay state
|
||||
(:id frame)
|
||||
(cti/invert-direction animation)))))))
|
||||
|
||||
(defn close-overlay
|
||||
[frame-id]
|
||||
(ptk/reify ::close-overlay
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(update-in state [:viewer-local :overlays]
|
||||
(fn [overlays]
|
||||
(d/removev #(= (:id (:frame %)) frame-id) overlays))))))
|
||||
([frame-id] (close-overlay frame-id nil))
|
||||
([frame-id animation]
|
||||
(us/verify ::us/uuid frame-id)
|
||||
(us/verify (s/nilable ::cti/animation) animation)
|
||||
(ptk/reify ::close-overlay
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(do-close-overlay state
|
||||
frame-id
|
||||
animation)))))
|
||||
|
||||
;; --- Objects selection
|
||||
|
||||
|
|
|
@ -17,6 +17,10 @@
|
|||
(def align-middle (icon-xref :align-middle))
|
||||
(def align-top (icon-xref :align-top))
|
||||
(def alignment (icon-xref :alignment))
|
||||
(def animate-down (icon-xref :animate-down))
|
||||
(def animate-left (icon-xref :animate-left))
|
||||
(def animate-right (icon-xref :animate-right))
|
||||
(def animate-up (icon-xref :animate-up))
|
||||
(def arrow-down (icon-xref :arrow-down))
|
||||
(def arrow-end (icon-xref :arrow-end))
|
||||
(def arrow-slide (icon-xref :arrow-slide))
|
||||
|
@ -42,6 +46,11 @@
|
|||
(def copy (icon-xref :copy))
|
||||
(def curve (icon-xref :curve))
|
||||
(def download (icon-xref :download))
|
||||
(def easing-linear (icon-xref :easing-linear))
|
||||
(def easing-ease (icon-xref :easing-ease))
|
||||
(def easing-ease-in (icon-xref :easing-ease-in))
|
||||
(def easing-ease-out (icon-xref :easing-ease-out))
|
||||
(def easing-ease-in-out (icon-xref :easing-ease-in-out))
|
||||
(def exit (icon-xref :exit))
|
||||
(def export (icon-xref :export))
|
||||
(def eye (icon-xref :eye))
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
(ns app.main.ui.viewer
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.main.data.comments :as dcm]
|
||||
|
@ -31,9 +32,22 @@
|
|||
(defn- calculate-size
|
||||
[frame zoom]
|
||||
(let [{:keys [_ _ width height]} (filters/get-filters-bounds frame)]
|
||||
{:width (* width zoom)
|
||||
:height (* height zoom)
|
||||
:vbox (str "0 0 " width " " height)}))
|
||||
{:base-width width
|
||||
:base-height height
|
||||
:width (* width zoom)
|
||||
:height (* height zoom)
|
||||
:vbox (str "0 0 " width " " height)}))
|
||||
|
||||
(defn- calculate-wrapper
|
||||
[size1 size2 zoom]
|
||||
(cond
|
||||
(nil? size1) size2
|
||||
(nil? size2) size1
|
||||
:else (let [width (max (:base-width size1) (:base-width size2))
|
||||
height (max (:base-height size1) (:base-height size2))]
|
||||
{:width (* width zoom)
|
||||
:height (* height zoom)
|
||||
:vbox (str "0 0 " width " " height)})))
|
||||
|
||||
(mf/defc viewer
|
||||
[{:keys [params data]}]
|
||||
|
@ -41,24 +55,41 @@
|
|||
(let [{:keys [page-id section index]} params
|
||||
{:keys [file users project permissions]} data
|
||||
|
||||
local (mf/deref refs/viewer-local)
|
||||
local (mf/deref refs/viewer-local)
|
||||
|
||||
nav-scroll (:nav-scroll local)
|
||||
orig-viewport-ref (mf/use-ref nil)
|
||||
current-viewport-ref (mf/use-ref nil)
|
||||
current-animation (:current-animation local)
|
||||
|
||||
page-id (or page-id (-> file :data :pages first))
|
||||
|
||||
page (mf/use-memo
|
||||
(mf/deps data page-id)
|
||||
(fn []
|
||||
(get-in data [:pages page-id])))
|
||||
page (mf/use-memo
|
||||
(mf/deps data page-id)
|
||||
(fn []
|
||||
(get-in data [:pages page-id])))
|
||||
|
||||
zoom (:zoom local)
|
||||
frames (:frames page)
|
||||
frame (get frames index)
|
||||
zoom (:zoom local)
|
||||
frames (:frames page)
|
||||
frame (get frames index)
|
||||
|
||||
size (mf/use-memo
|
||||
(mf/deps frame zoom)
|
||||
(fn [] (calculate-size frame zoom)))
|
||||
overlays (:overlays local)
|
||||
|
||||
orig-frame
|
||||
(when (:orig-frame-id current-animation)
|
||||
(d/seek #(= (:id %) (:orig-frame-id current-animation)) frames))
|
||||
|
||||
size (mf/use-memo
|
||||
(mf/deps frame zoom)
|
||||
(fn [] (calculate-size frame zoom)))
|
||||
|
||||
orig-size (mf/use-memo
|
||||
(mf/deps orig-frame zoom)
|
||||
(fn [] (when orig-frame (calculate-size orig-frame zoom))))
|
||||
|
||||
wrapper-size (mf/use-memo
|
||||
(mf/deps size orig-size zoom)
|
||||
(fn [] (calculate-wrapper size orig-size zoom)))
|
||||
|
||||
interactions-mode
|
||||
(:interactions-mode local)
|
||||
|
@ -96,11 +127,67 @@
|
|||
(mf/use-layout-effect
|
||||
(mf/deps nav-scroll)
|
||||
(fn []
|
||||
;; Set scroll position after navigate
|
||||
(when (number? nav-scroll)
|
||||
(let [viewer-section (dom/get-element "viewer-section")]
|
||||
(st/emit! (dv/reset-nav-scroll))
|
||||
(dom/set-scroll-pos! viewer-section nav-scroll)))))
|
||||
|
||||
(mf/use-layout-effect
|
||||
(mf/deps index)
|
||||
(fn []
|
||||
;; Navigate animation needs to be started after navigation
|
||||
;; is complete, and we have the next page index.
|
||||
(when (and current-animation
|
||||
(= (:kind current-animation) :go-to-frame))
|
||||
(let [orig-viewport (mf/ref-val orig-viewport-ref)
|
||||
current-viewport (mf/ref-val current-viewport-ref)]
|
||||
(interactions/animate-go-to-frame
|
||||
(:animation current-animation)
|
||||
current-viewport
|
||||
orig-viewport
|
||||
size
|
||||
orig-size
|
||||
wrapper-size)))))
|
||||
|
||||
(mf/use-layout-effect
|
||||
(mf/deps current-animation)
|
||||
(fn []
|
||||
;; Overlay animations may be started when needed.
|
||||
(when current-animation
|
||||
(case (:kind current-animation)
|
||||
|
||||
:open-overlay
|
||||
(let [overlay-viewport (dom/get-element (str "overlay-" (str (:overlay-id current-animation))))
|
||||
overlay (d/seek #(= (:id (:frame %)) (:overlay-id current-animation))
|
||||
overlays)
|
||||
overlay-size (calculate-size (:frame overlay) zoom)
|
||||
overlay-position {:x (* (:x (:position overlay)) zoom)
|
||||
:y (* (:y (:position overlay)) zoom)}]
|
||||
(interactions/animate-open-overlay
|
||||
(:animation current-animation)
|
||||
overlay-viewport
|
||||
wrapper-size
|
||||
overlay-size
|
||||
overlay-position))
|
||||
|
||||
:close-overlay
|
||||
(let [overlay-viewport (dom/get-element (str "overlay-" (str (:overlay-id current-animation))))
|
||||
overlay (d/seek #(= (:id (:frame %)) (:overlay-id current-animation))
|
||||
overlays)
|
||||
overlay-size (calculate-size (:frame overlay) zoom)
|
||||
overlay-position {:x (* (:x (:position overlay)) zoom)
|
||||
:y (* (:y (:position overlay)) zoom)}]
|
||||
(interactions/animate-close-overlay
|
||||
(:animation current-animation)
|
||||
overlay-viewport
|
||||
wrapper-size
|
||||
overlay-size
|
||||
overlay-position
|
||||
(:id (:frame overlay))))
|
||||
|
||||
nil))))
|
||||
|
||||
[:div {:class (dom/classnames
|
||||
:force-visible (:show-thumbnails local)
|
||||
:viewer-layout (not= section :handoff)
|
||||
|
@ -139,57 +226,81 @@
|
|||
:section section
|
||||
:local local}]
|
||||
|
||||
[:div.viewport-container
|
||||
{:style {:width (:width size)
|
||||
:height (:height size)
|
||||
:position "relative"}}
|
||||
[:*
|
||||
[:div.viewer-wrapper
|
||||
{:style {:width (:width wrapper-size)
|
||||
:height (:height wrapper-size)}}
|
||||
|
||||
(when (= section :comments)
|
||||
[:& comments-layer {:file file
|
||||
:users users
|
||||
:frame frame
|
||||
:page page
|
||||
:zoom zoom}])
|
||||
(when orig-frame
|
||||
[:div.viewport-container
|
||||
{:ref orig-viewport-ref
|
||||
:style {:width (:width orig-size)
|
||||
:height (:height orig-size)
|
||||
:position "relative"}}
|
||||
|
||||
[:& interactions/viewport
|
||||
{:frame frame
|
||||
:base-frame frame
|
||||
:frame-offset (gpt/point 0 0)
|
||||
:size size
|
||||
:page page
|
||||
:file file
|
||||
:users users
|
||||
:interactions-mode interactions-mode}]
|
||||
[:& interactions/viewport
|
||||
{:frame orig-frame
|
||||
:base-frame orig-frame
|
||||
:frame-offset (gpt/point 0 0)
|
||||
:size orig-size
|
||||
:page page
|
||||
:file file
|
||||
:users users
|
||||
:interactions-mode :hide}]])
|
||||
|
||||
(for [overlay (:overlays local)]
|
||||
(let [size-over (calculate-size (:frame overlay) zoom)]
|
||||
[:*
|
||||
(when (or (:close-click-outside overlay)
|
||||
(:background-overlay overlay))
|
||||
[:div.viewer-overlay-background
|
||||
{:class (dom/classnames
|
||||
:visible (:background-overlay overlay))
|
||||
:style {:width (:width frame)
|
||||
:height (:height frame)
|
||||
:position "absolute"
|
||||
:left 0
|
||||
:top 0}
|
||||
:on-click #(when (:close-click-outside overlay)
|
||||
(close-overlay (:frame overlay)))}])
|
||||
[:div.viewport-container.viewer-overlay
|
||||
{:style {:width (:width size-over)
|
||||
:height (:height size-over)
|
||||
:left (* (:x (:position overlay)) zoom)
|
||||
:top (* (:y (:position overlay)) zoom)}}
|
||||
[:& interactions/viewport
|
||||
{:frame (:frame overlay)
|
||||
:base-frame frame
|
||||
:frame-offset (:position overlay)
|
||||
:size size-over
|
||||
:page page
|
||||
:file file
|
||||
:users users
|
||||
:interactions-mode interactions-mode}]]]))]))]]]))
|
||||
[:div.viewport-container
|
||||
{:ref current-viewport-ref
|
||||
:style {:width (:width size)
|
||||
:height (:height size)
|
||||
:position "relative"}
|
||||
}
|
||||
(when (= section :comments)
|
||||
[:& comments-layer {:file file
|
||||
:users users
|
||||
:frame frame
|
||||
:page page
|
||||
:zoom zoom}])
|
||||
|
||||
[:& interactions/viewport
|
||||
{:frame frame
|
||||
:base-frame frame
|
||||
:frame-offset (gpt/point 0 0)
|
||||
:size size
|
||||
:page page
|
||||
:file file
|
||||
:users users
|
||||
:interactions-mode interactions-mode}]
|
||||
|
||||
(for [overlay overlays]
|
||||
(let [size-over (calculate-size (:frame overlay) zoom)]
|
||||
[:*
|
||||
(when (or (:close-click-outside overlay)
|
||||
(:background-overlay overlay))
|
||||
[:div.viewer-overlay-background
|
||||
{:class (dom/classnames
|
||||
:visible (:background-overlay overlay))
|
||||
:style {:width (:width wrapper-size)
|
||||
:height (:height wrapper-size)
|
||||
:position "absolute"
|
||||
:left 0
|
||||
:top 0}
|
||||
:on-click #(when (:close-click-outside overlay)
|
||||
(close-overlay (:frame overlay)))}])
|
||||
[:div.viewport-container.viewer-overlay
|
||||
{:id (str "overlay-" (str (:id (:frame overlay))))
|
||||
:style {:width (:width size-over)
|
||||
:height (:height size-over)
|
||||
:left (* (:x (:position overlay)) zoom)
|
||||
:top (* (:y (:position overlay)) zoom)}}
|
||||
[:& interactions/viewport
|
||||
{:frame (:frame overlay)
|
||||
:base-frame frame
|
||||
:frame-offset (:position overlay)
|
||||
:size size-over
|
||||
:page page
|
||||
:file file
|
||||
:users users
|
||||
:interactions-mode interactions-mode}]]]))]]]))]]]))
|
||||
|
||||
;; --- Component: Viewer Page
|
||||
|
||||
|
|
|
@ -169,3 +169,338 @@
|
|||
[:span.icon i/tick]
|
||||
[:span.label (tr "viewer.header.show-interactions-on-click")]]]]]))
|
||||
|
||||
|
||||
(defn animate-go-to-frame
|
||||
[animation current-viewport orig-viewport current-size orig-size wrapper-size]
|
||||
(case (:animation-type animation)
|
||||
|
||||
:dissolve
|
||||
(do (dom/animate! orig-viewport
|
||||
[#js {:opacity "100"}
|
||||
#js {:opacity "0"}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))}
|
||||
#(st/emit! (dv/complete-animation)))
|
||||
(dom/animate! current-viewport
|
||||
[#js {:opacity "0"}
|
||||
#js {:opacity "100"}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))}))
|
||||
|
||||
:slide
|
||||
(case (:way animation)
|
||||
|
||||
:in
|
||||
(case (:direction animation)
|
||||
|
||||
:right
|
||||
(let [offset (+ (:width current-size)
|
||||
(/ (- (:width wrapper-size) (:width current-size)) 2))]
|
||||
(dom/animate! current-viewport
|
||||
[#js {:left (str "-" offset "px")}
|
||||
#js {:left "0"}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))}
|
||||
#(st/emit! (dv/complete-animation)))
|
||||
(when (:offset-effect animation)
|
||||
(dom/animate! orig-viewport
|
||||
[#js {:left "0"
|
||||
:opacity "100%"}
|
||||
#js {:left (str (* offset 0.2) "px")
|
||||
:opacity "0"}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))})))
|
||||
|
||||
:left
|
||||
(let [offset (+ (:width current-size)
|
||||
(/ (- (:width wrapper-size) (:width current-size)) 2))]
|
||||
(dom/animate! current-viewport
|
||||
[#js {:right (str "-" offset "px")}
|
||||
#js {:right "0"}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))}
|
||||
#(st/emit! (dv/complete-animation)))
|
||||
(when (:offset-effect animation)
|
||||
(dom/animate! orig-viewport
|
||||
[#js {:right "0"
|
||||
:opacity "100%"}
|
||||
#js {:right (str (* offset 0.2) "px")
|
||||
:opacity "0"}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))})))
|
||||
|
||||
:up
|
||||
(let [offset (+ (:height current-size)
|
||||
(/ (- (:height wrapper-size) (:height current-size)) 2))]
|
||||
(dom/animate! current-viewport
|
||||
[#js {:bottom (str "-" offset "px")}
|
||||
#js {:bottom "0"}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))}
|
||||
#(st/emit! (dv/complete-animation)))
|
||||
(when (:offset-effect animation)
|
||||
(dom/animate! orig-viewport
|
||||
[#js {:bottom "0"
|
||||
:opacity "100%"}
|
||||
#js {:bottom (str (* offset 0.2) "px")
|
||||
:opacity "0"}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))})))
|
||||
|
||||
:down
|
||||
(let [offset (+ (:height current-size)
|
||||
(/ (- (:height wrapper-size) (:height current-size)) 2))]
|
||||
(dom/animate! current-viewport
|
||||
[#js {:top (str "-" offset "px")}
|
||||
#js {:top "0"}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))}
|
||||
#(st/emit! (dv/complete-animation)))
|
||||
(when (:offset-effect animation)
|
||||
(dom/animate! orig-viewport
|
||||
[#js {:top "0"
|
||||
:opacity "100%"}
|
||||
#js {:top (str (* offset 0.2) "px")
|
||||
:opacity "0"}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))}))))
|
||||
|
||||
:out
|
||||
(case (:direction animation)
|
||||
|
||||
:right
|
||||
(let [offset (+ (:width orig-size)
|
||||
(/ (- (:width wrapper-size) (:width orig-size)) 2))]
|
||||
(dom/set-css-property! orig-viewport "z-index" 10000)
|
||||
(dom/animate! orig-viewport
|
||||
[#js {:right "0"}
|
||||
#js {:right (str "-" offset "px")}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))}
|
||||
#(st/emit! (dv/complete-animation)))
|
||||
(when (:offset-effect animation)
|
||||
(dom/animate! current-viewport
|
||||
[#js {:right (str (* offset 0.2) "px")
|
||||
:opacity "0"}
|
||||
#js {:right "0"
|
||||
:opacity "100%"}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))})))
|
||||
|
||||
:left
|
||||
(let [offset (+ (:width orig-size)
|
||||
(/ (- (:width wrapper-size) (:width orig-size)) 2))]
|
||||
(dom/set-css-property! orig-viewport "z-index" 10000)
|
||||
(dom/animate! orig-viewport
|
||||
[#js {:left "0"}
|
||||
#js {:left (str "-" offset "px")}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))}
|
||||
#(st/emit! (dv/complete-animation)))
|
||||
(when (:offset-effect animation)
|
||||
(dom/animate! current-viewport
|
||||
[#js {:left (str (* offset 0.2) "px")
|
||||
:opacity "0"}
|
||||
#js {:left "0"
|
||||
:opacity "100%"}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))})))
|
||||
|
||||
:up
|
||||
(let [offset (+ (:height orig-size)
|
||||
(/ (- (:height wrapper-size) (:height orig-size)) 2))]
|
||||
(dom/set-css-property! orig-viewport "z-index" 10000)
|
||||
(dom/animate! orig-viewport
|
||||
[#js {:top "0"}
|
||||
#js {:top (str "-" offset "px")}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))}
|
||||
#(st/emit! (dv/complete-animation)))
|
||||
(when (:offset-effect animation)
|
||||
(dom/animate! current-viewport
|
||||
[#js {:top (str (* offset 0.2) "px")
|
||||
:opacity "0"}
|
||||
#js {:top "0"
|
||||
:opacity "100%"}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))})))
|
||||
|
||||
:down
|
||||
(let [offset (+ (:height orig-size)
|
||||
(/ (- (:height wrapper-size) (:height orig-size)) 2))]
|
||||
(dom/set-css-property! orig-viewport "z-index" 10000)
|
||||
(dom/animate! orig-viewport
|
||||
[#js {:bottom "0"}
|
||||
#js {:bottom (str "-" offset "px")}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))}
|
||||
#(st/emit! (dv/complete-animation)))
|
||||
(when (:offset-effect animation)
|
||||
(dom/animate! current-viewport
|
||||
[#js {:bottom (str (* offset 0.2) "px")
|
||||
:opacity "0"}
|
||||
#js {:bottom "0"
|
||||
:opacity "100%"}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))})))))
|
||||
|
||||
:push
|
||||
(case (:direction animation)
|
||||
|
||||
:right
|
||||
(let [offset (:width wrapper-size)]
|
||||
(dom/animate! current-viewport
|
||||
[#js {:left (str "-" offset "px")}
|
||||
#js {:left "0"}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))}
|
||||
#(st/emit! (dv/complete-animation)))
|
||||
(dom/animate! orig-viewport
|
||||
[#js {:left "0"}
|
||||
#js {:left (str offset "px")}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))}))
|
||||
|
||||
:left
|
||||
(let [offset (:width wrapper-size)]
|
||||
(dom/animate! current-viewport
|
||||
[#js {:right (str "-" offset "px")}
|
||||
#js {:right "0"}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))}
|
||||
#(st/emit! (dv/complete-animation)))
|
||||
(dom/animate! orig-viewport
|
||||
[#js {:right "0"}
|
||||
#js {:right (str offset "px")}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))}))
|
||||
|
||||
:up
|
||||
(let [offset (:height wrapper-size)]
|
||||
(dom/animate! current-viewport
|
||||
[#js {:bottom (str "-" offset "px")}
|
||||
#js {:bottom "0"}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))}
|
||||
#(st/emit! (dv/complete-animation)))
|
||||
(dom/animate! orig-viewport
|
||||
[#js {:bottom "0"}
|
||||
#js {:bottom (str offset "px")}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))}))
|
||||
|
||||
:down
|
||||
(let [offset (:height wrapper-size)]
|
||||
(dom/animate! current-viewport
|
||||
[#js {:top (str "-" offset "px")}
|
||||
#js {:top "0"}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))}
|
||||
#(st/emit! (dv/complete-animation)))
|
||||
(dom/animate! orig-viewport
|
||||
[#js {:top "0"}
|
||||
#js {:top (str offset "px")}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))})))))
|
||||
|
||||
(defn animate-open-overlay
|
||||
[animation overlay-viewport
|
||||
wrapper-size overlay-size overlay-position]
|
||||
(case (:animation-type animation)
|
||||
|
||||
:dissolve
|
||||
(dom/animate! overlay-viewport
|
||||
[#js {:opacity "0"}
|
||||
#js {:opacity "100"}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))}
|
||||
#(st/emit! (dv/complete-animation)))
|
||||
|
||||
:slide
|
||||
(case (:direction animation) ;; way and offset-effect are ignored
|
||||
|
||||
:right
|
||||
(dom/animate! overlay-viewport
|
||||
[#js {:left (str "-" (:width overlay-size) "px")}
|
||||
#js {:left (str (:x overlay-position) "px")}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))}
|
||||
#(st/emit! (dv/complete-animation)))
|
||||
|
||||
:left
|
||||
(dom/animate! overlay-viewport
|
||||
[#js {:left (str (:width wrapper-size) "px")}
|
||||
#js {:left (str (:x overlay-position) "px")}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))}
|
||||
#(st/emit! (dv/complete-animation)))
|
||||
|
||||
:up
|
||||
(dom/animate! overlay-viewport
|
||||
[#js {:top (str (:height wrapper-size) "px")}
|
||||
#js {:top (str (:y overlay-position) "px")}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))}
|
||||
#(st/emit! (dv/complete-animation)))
|
||||
|
||||
:down
|
||||
(dom/animate! overlay-viewport
|
||||
[#js {:top (str "-" (:height overlay-size) "px")}
|
||||
#js {:top (str (:y overlay-position) "px")}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))}
|
||||
#(st/emit! (dv/complete-animation))))))
|
||||
|
||||
(defn animate-close-overlay
|
||||
[animation overlay-viewport
|
||||
wrapper-size overlay-size overlay-position overlay-id]
|
||||
(case (:animation-type animation)
|
||||
|
||||
:dissolve
|
||||
(dom/animate! overlay-viewport
|
||||
[#js {:opacity "100"}
|
||||
#js {:opacity "0"}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))}
|
||||
#(st/emit! (dv/complete-animation)
|
||||
(dv/close-overlay overlay-id)))
|
||||
|
||||
:slide
|
||||
(case (:direction animation) ;; way and offset-effect are ignored
|
||||
|
||||
:right
|
||||
(dom/animate! overlay-viewport
|
||||
[#js {:left (str (:x overlay-position) "px")}
|
||||
#js {:left (str (:width wrapper-size) "px")}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))}
|
||||
#(st/emit! (dv/complete-animation)
|
||||
(dv/close-overlay overlay-id)))
|
||||
|
||||
:left
|
||||
(dom/animate! overlay-viewport
|
||||
[#js {:left (str (:x overlay-position) "px")}
|
||||
#js {:left (str "-" (:width overlay-size) "px")}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))}
|
||||
#(st/emit! (dv/complete-animation)
|
||||
(dv/close-overlay overlay-id)))
|
||||
|
||||
:up
|
||||
(dom/animate! overlay-viewport
|
||||
[#js {:top (str (:y overlay-position) "px")}
|
||||
#js {:top (str "-" (:height overlay-size) "px")}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))}
|
||||
#(st/emit! (dv/complete-animation)
|
||||
(dv/close-overlay overlay-id)))
|
||||
|
||||
:down
|
||||
(dom/animate! overlay-viewport
|
||||
[#js {:top (str (:y overlay-position) "px")}
|
||||
#js {:top (str (:height wrapper-size) "px")}]
|
||||
#js {:duration (:duration animation)
|
||||
:easing (name (:easing animation))}
|
||||
#(st/emit! (dv/complete-animation)
|
||||
(dv/close-overlay overlay-id))))))
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@
|
|||
(def viewer-interactions-show?
|
||||
(l/derived :interactions-show? refs/viewer-local))
|
||||
|
||||
(defn activate-interaction
|
||||
(defn- activate-interaction
|
||||
[interaction shape base-frame frame-offset objects]
|
||||
(case (:action-type interaction)
|
||||
:navigate
|
||||
|
@ -48,7 +48,7 @@
|
|||
(dom/get-scroll-pos viewer-section)
|
||||
0)]
|
||||
(st/emit! (dv/set-nav-scroll scroll)
|
||||
(dv/go-to-frame frame-id))))
|
||||
(dv/go-to-frame frame-id (:animation interaction)))))
|
||||
|
||||
:open-overlay
|
||||
(let [dest-frame-id (:destination interaction)
|
||||
|
@ -64,7 +64,8 @@
|
|||
(st/emit! (dv/open-overlay dest-frame-id
|
||||
position
|
||||
close-click-outside
|
||||
background-overlay))))
|
||||
background-overlay
|
||||
(:animation interaction)))))
|
||||
|
||||
:toggle-overlay
|
||||
(let [frame-id (:destination interaction)
|
||||
|
@ -75,14 +76,15 @@
|
|||
(st/emit! (dv/toggle-overlay frame-id
|
||||
position
|
||||
close-click-outside
|
||||
background-overlay))))
|
||||
background-overlay
|
||||
(:animation interaction)))))
|
||||
|
||||
:close-overlay
|
||||
(let [frame-id (or (:destination interaction)
|
||||
(if (= (:type shape) :frame)
|
||||
(:id shape)
|
||||
(:frame-id shape)))]
|
||||
(st/emit! (dv/close-overlay frame-id)))
|
||||
(st/emit! (dv/close-overlay frame-id (:animation interaction))))
|
||||
|
||||
:prev-screen
|
||||
(st/emit! (rt/nav-back-local))
|
||||
|
@ -93,7 +95,7 @@
|
|||
nil))
|
||||
|
||||
;; Perform the opposite action of an interaction, if possible
|
||||
(defn deactivate-interaction
|
||||
(defn- deactivate-interaction
|
||||
[interaction shape base-frame frame-offset objects]
|
||||
(case (:action-type interaction)
|
||||
:open-overlay
|
||||
|
@ -112,7 +114,8 @@
|
|||
(st/emit! (dv/toggle-overlay frame-id
|
||||
position
|
||||
close-click-outside
|
||||
background-overlay))))
|
||||
background-overlay
|
||||
(:animation interaction)))))
|
||||
|
||||
:close-overlay
|
||||
(let [dest-frame-id (:destination interaction)
|
||||
|
@ -128,10 +131,11 @@
|
|||
(st/emit! (dv/open-overlay dest-frame-id
|
||||
position
|
||||
close-click-outside
|
||||
background-overlay))))
|
||||
background-overlay
|
||||
(:animation interaction)))))
|
||||
nil))
|
||||
|
||||
(defn on-mouse-down
|
||||
(defn- on-mouse-down
|
||||
[event shape base-frame frame-offset objects]
|
||||
(let [interactions (->> (:interactions shape)
|
||||
(filter #(or (= (:event-type %) :click)
|
||||
|
@ -141,7 +145,7 @@
|
|||
(doseq [interaction interactions]
|
||||
(activate-interaction interaction shape base-frame frame-offset objects)))))
|
||||
|
||||
(defn on-mouse-up
|
||||
(defn- on-mouse-up
|
||||
[event shape base-frame frame-offset objects]
|
||||
(let [interactions (->> (:interactions shape)
|
||||
(filter #(= (:event-type %) :mouse-press)))]
|
||||
|
@ -150,7 +154,7 @@
|
|||
(doseq [interaction interactions]
|
||||
(deactivate-interaction interaction shape base-frame frame-offset objects)))))
|
||||
|
||||
(defn on-mouse-enter
|
||||
(defn- on-mouse-enter
|
||||
[event shape base-frame frame-offset objects]
|
||||
(let [interactions (->> (:interactions shape)
|
||||
(filter #(or (= (:event-type %) :mouse-enter)
|
||||
|
@ -160,7 +164,7 @@
|
|||
(doseq [interaction interactions]
|
||||
(activate-interaction interaction shape base-frame frame-offset objects)))))
|
||||
|
||||
(defn on-mouse-leave
|
||||
(defn- on-mouse-leave
|
||||
[event shape base-frame frame-offset objects]
|
||||
(let [interactions (->> (:interactions shape)
|
||||
(filter #(= (:event-type %) :mouse-leave)))
|
||||
|
@ -173,7 +177,7 @@
|
|||
(doseq [interaction interactions-inv]
|
||||
(deactivate-interaction interaction shape base-frame frame-offset objects)))))
|
||||
|
||||
(defn on-load
|
||||
(defn- on-load
|
||||
[shape base-frame frame-offset objects]
|
||||
(let [interactions (->> (:interactions shape)
|
||||
(filter #(= (:event-type %) :after-delay)))]
|
||||
|
|
|
@ -73,6 +73,23 @@
|
|||
:bottom-right (tr "workspace.options.interaction-pos-bottom-right")
|
||||
:bottom-center (tr "workspace.options.interaction-pos-bottom-center")})
|
||||
|
||||
(defn- animation-type-names
|
||||
[interaction]
|
||||
(cond->
|
||||
{:dissolve (tr "workspace.options.interaction-animation-dissolve")
|
||||
:slide (tr "workspace.options.interaction-animation-slide")}
|
||||
|
||||
(cti/allow-push? (:action-type interaction))
|
||||
(assoc :push (tr "workspace.options.interaction-animation-push"))))
|
||||
|
||||
(defn- easing-names
|
||||
[]
|
||||
{:linear (tr "workspace.options.interaction-easing-linear")
|
||||
:ease (tr "workspace.options.interaction-easing-ease")
|
||||
:ease-in (tr "workspace.options.interaction-easing-ease-in")
|
||||
:ease-out (tr "workspace.options.interaction-easing-ease-out")
|
||||
:ease-in-out (tr "workspace.options.interaction-easing-ease-in-out")})
|
||||
|
||||
(def flow-for-rename-ref
|
||||
(l/derived (l/in [:workspace-local :flow-for-rename]) st/state))
|
||||
|
||||
|
@ -170,10 +187,13 @@
|
|||
close-click-outside? (:close-click-outside interaction false)
|
||||
background-overlay? (:background-overlay interaction false)
|
||||
preserve-scroll? (:preserve-scroll interaction false)
|
||||
way (-> interaction :animation :way)
|
||||
direction (-> interaction :animation :direction)
|
||||
|
||||
extended-open? (mf/use-state false)
|
||||
|
||||
ext-delay-ref (mf/use-ref nil)
|
||||
ext-duration-ref (mf/use-ref nil)
|
||||
|
||||
select-text
|
||||
(fn [ref] (fn [_] (dom/select-text! (mf/ref-val ref))))
|
||||
|
@ -237,7 +257,36 @@
|
|||
change-background-overlay
|
||||
(fn [event]
|
||||
(let [value (-> event dom/get-target dom/checked?)]
|
||||
(update-interaction index #(cti/set-background-overlay % value))))]
|
||||
(update-interaction index #(cti/set-background-overlay % value))))
|
||||
|
||||
change-animation-type
|
||||
(fn [event]
|
||||
(let [value (-> event dom/get-target dom/get-value d/read-string)]
|
||||
(update-interaction index #(cti/set-animation-type % value))))
|
||||
|
||||
change-duration
|
||||
(fn [value]
|
||||
(update-interaction index #(cti/set-duration % value)))
|
||||
|
||||
change-easing
|
||||
(fn [event]
|
||||
(let [value (-> event dom/get-target dom/get-value d/read-string)]
|
||||
(update-interaction index #(cti/set-easing % value))))
|
||||
|
||||
change-way
|
||||
(fn [event]
|
||||
(let [value (-> event dom/get-target dom/get-value d/read-string)]
|
||||
(update-interaction index #(cti/set-way % value))))
|
||||
|
||||
change-direction
|
||||
(fn [value]
|
||||
(update-interaction index #(cti/set-direction % value)))
|
||||
|
||||
change-offset-effect
|
||||
(fn [event]
|
||||
(let [value (-> event dom/get-target dom/checked?)]
|
||||
(update-interaction index #(cti/set-offset-effect % value))))
|
||||
]
|
||||
|
||||
[:*
|
||||
[:div.element-set-options-group {:class (dom/classnames
|
||||
|
@ -382,7 +431,97 @@
|
|||
:checked background-overlay?
|
||||
:on-change change-background-overlay}]
|
||||
[:label {:for (str "background-" index)}
|
||||
(tr "workspace.options.interaction-background")]]]])])]]))
|
||||
(tr "workspace.options.interaction-background")]]]])
|
||||
|
||||
; Animation select
|
||||
[:div.interactions-element.separator
|
||||
[:span.element-set-subtitle.wide (tr "workspace.options.interaction-animation")]
|
||||
[:select.input-select
|
||||
{:value (str (-> interaction :animation :animation-type))
|
||||
:on-change change-animation-type}
|
||||
[:option {:value ""} (tr "workspace.options.interaction-animation-none")]
|
||||
(for [[value name] (animation-type-names interaction)]
|
||||
[:option {:value (str value)} name])]]
|
||||
|
||||
; Direction
|
||||
(when (cti/has-way? interaction)
|
||||
[:div.interactions-element.interactions-way-buttons
|
||||
[:div.input-radio
|
||||
[:input {:type "radio"
|
||||
:id "way-in"
|
||||
:checked (= :in way)
|
||||
:name "animation-way"
|
||||
:value ":in"
|
||||
:on-change change-way}]
|
||||
[:label {:for "way-in"} (tr "workspace.options.interaction-in")]]
|
||||
[:div.input-radio
|
||||
[:input {:type "radio"
|
||||
:id "way-out"
|
||||
:checked (= :out way)
|
||||
:name "animation-way"
|
||||
:value ":out"
|
||||
:on-change change-way}]
|
||||
[:label {:for "way-out"} (tr "workspace.options.interaction-out")]]])
|
||||
|
||||
; Direction
|
||||
(when (cti/has-direction? interaction)
|
||||
[:div.interactions-element.interactions-direction-buttons
|
||||
[:div.element-set-actions-button
|
||||
{:class (dom/classnames :active (= direction :right))
|
||||
:on-click #(change-direction :right)}
|
||||
i/animate-right]
|
||||
[:div.element-set-actions-button
|
||||
{:class (dom/classnames :active (= direction :down))
|
||||
:on-click #(change-direction :down)}
|
||||
i/animate-down]
|
||||
[:div.element-set-actions-button
|
||||
{:class (dom/classnames :active (= direction :left))
|
||||
:on-click #(change-direction :left)}
|
||||
i/animate-left]
|
||||
[:div.element-set-actions-button
|
||||
{:class (dom/classnames :active (= direction :up))
|
||||
:on-click #(change-direction :up)}
|
||||
i/animate-up]])
|
||||
|
||||
; Duration
|
||||
(when (cti/has-duration? interaction)
|
||||
[:div.interactions-element
|
||||
[:span.element-set-subtitle.wide (tr "workspace.options.interaction-duration")]
|
||||
[:div.input-element {:title (tr "workspace.options.interaction-ms")}
|
||||
[:> numeric-input {:ref ext-duration-ref
|
||||
:on-click (select-text ext-duration-ref)
|
||||
:on-change change-duration
|
||||
:value (-> interaction :animation :duration)
|
||||
:title (tr "workspace.options.interaction-ms")}]
|
||||
[:span.after (tr "workspace.options.interaction-ms")]]])
|
||||
|
||||
; Easing
|
||||
(when (cti/has-easing? interaction)
|
||||
[:div.interactions-element
|
||||
[:span.element-set-subtitle.wide (tr "workspace.options.interaction-easing")]
|
||||
[:select.input-select
|
||||
{:value (str (-> interaction :animation :easing))
|
||||
:on-change change-easing}
|
||||
(for [[value name] (easing-names)]
|
||||
[:option {:value (str value)} name])]
|
||||
[:div.interactions-easing-icon
|
||||
(case (-> interaction :animation :easing)
|
||||
:linear i/easing-linear
|
||||
:ease i/easing-ease
|
||||
:ease-in i/easing-ease-in
|
||||
:ease-out i/easing-ease-out
|
||||
:ease-in-out i/easing-ease-in-out)]])
|
||||
|
||||
; Offset effect
|
||||
(when (cti/has-offset-effect? interaction)
|
||||
[:div.interactions-element
|
||||
[:div.input-checkbox
|
||||
[:input {:type "checkbox"
|
||||
:id (str "offset-effect-" index)
|
||||
:checked (-> interaction :animation :offset-effect)
|
||||
:on-change change-offset-effect}]
|
||||
[:label {:for (str "offset-effect-" index)}
|
||||
(tr "workspace.options.interaction-offset-effect")]]])])]]))
|
||||
|
||||
(mf/defc interactions-menu
|
||||
[{:keys [shape] :as props}]
|
||||
|
|
|
@ -425,3 +425,10 @@
|
|||
[]
|
||||
(.back (.-history js/window)))
|
||||
|
||||
(defn animate!
|
||||
([item keyframes duration] (animate! item keyframes duration nil))
|
||||
([item keyframes duration onfinish]
|
||||
(let [animation (.animate item keyframes duration)]
|
||||
(when onfinish
|
||||
(set! (.-onfinish animation) onfinish)))))
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue