🎉 Allow to position interaction overlays

This commit is contained in:
Andrés Moya 2021-09-15 12:49:04 +02:00
parent 4708af3b91
commit 157e8413fb
9 changed files with 372 additions and 67 deletions

View file

@ -328,8 +328,9 @@
;; --- Overlays
(defn open-overlay
[frame-id]
[frame-id position]
(us/verify ::us/uuid frame-id)
(us/verify ::us/point position)
(ptk/reify ::open-overlay
ptk/UpdateEvent
(update [_ state]
@ -340,7 +341,9 @@
frame (d/seek #(= (:id %) frame-id) frames)
overlays (get-in state [:viewer-local :overlays])]
(if-not (some #(= % frame) overlays)
(update-in state [:viewer-local :overlays] conj frame)
(update-in state [:viewer-local :overlays] conj
{:frame frame
:position position})
state)))))
(defn close-overlay
@ -350,7 +353,7 @@
(update [_ state]
(update-in state [:viewer-local :overlays]
(fn [overlays]
(remove #(= (:id %) frame-id) overlays))))))
(remove #(= (:id (:frame %)) frame-id) overlays))))))
;; --- Objects selection

View file

@ -18,6 +18,7 @@
[app.common.pages.spec :as spec]
[app.common.spec :as us]
[app.common.transit :as t]
[app.common.types.interactions :as cti]
[app.common.uuid :as uuid]
[app.config :as cfg]
[app.main.data.events :as ev]
@ -1826,10 +1827,93 @@
frame)]
;; Update or create interaction
(if index
(assoc-in interactions [index :destination] (:id frame))
(update-in interactions [index]
#(cti/set-destination % (:id frame) shape objects))
(conj (or interactions [])
(assoc spec/default-interaction
:destination (:id frame))))))))))))))))
(cti/set-destination cti/default-interaction
(:id frame)
shape
objects)))))))))))))))
(declare move-overlay-pos)
(declare finish-move-overlay-pos)
(defn start-move-overlay-pos
[index]
(ptk/reify ::start-move-overlay-pos
ptk/UpdateEvent
(update [_ state]
(-> state
(assoc-in [:workspace-local :move-overlay-to] nil)
(assoc-in [:workspace-local :move-overlay-index] index)))
ptk/WatchEvent
(watch [_ state stream]
(let [initial-pos @ms/mouse-position
selected (wsh/lookup-selected state)
stopper (rx/filter ms/mouse-up? stream)]
(when (= 1 (count selected))
(let [page-id (:current-page-id state)
objects (wsh/lookup-page-objects state page-id)
shape (->> state
wsh/lookup-selected
first
(get objects))
overlay-pos (-> shape
(get-in [:interactions index])
:overlay-position)
orig-frame (cph/get-frame shape objects)
frame-pos (gpt/point (:x orig-frame) (:y orig-frame))
offset (-> initial-pos
(gpt/subtract overlay-pos)
(gpt/subtract frame-pos))]
(rx/concat
(->> ms/mouse-position
(rx/take-until stopper)
(rx/map #(move-overlay-pos % overlay-pos frame-pos offset)))
(rx/of (finish-move-overlay-pos index overlay-pos frame-pos offset)))))))))
(defn move-overlay-pos
[pos overlay-pos frame-pos offset]
(ptk/reify ::move-overlay-pos
ptk/UpdateEvent
(update [_ state]
(let [pos (-> pos
(gpt/subtract frame-pos)
(gpt/subtract offset))]
(assoc-in state [:workspace-local :move-overlay-to] pos)))))
(defn finish-move-overlay-pos
[index overlay-pos frame-pos offset]
(ptk/reify ::finish-move-overlay-pos
ptk/UpdateEvent
(update [_ state]
(-> state
(d/dissoc-in [:workspace-local :move-overlay-to])
(d/dissoc-in [:workspace-local :move-overlay-index])))
ptk/WatchEvent
(watch [_ state _]
(let [pos @ms/mouse-position
overlay-pos (-> pos
(gpt/subtract frame-pos)
(gpt/subtract offset))
page-id (:current-page-id state)
objects (wsh/lookup-page-objects state page-id)
shape (->> state
wsh/lookup-selected
first
(get objects))
interactions (:interactions shape)
new-interactions
(update interactions index
#(cti/set-overlay-position % overlay-pos))]
(rx/of (update-shape (:id shape) {:interactions new-interactions}))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; CANVAS OPTIONS

View file

@ -33,11 +33,6 @@
:height (* height zoom)
:vbox (str "0 0 " width " " height)}))
(defn- position-overlay
[size size-over]
{:x (/ (- (:width size) (:width size-over)) 2)
:y (/ (- (:height size) (:height size-over)) 2)})
(mf/defc viewer
[{:keys [params data]}]
@ -125,7 +120,6 @@
:section section
:local local}]
[:div.viewport-container
{:style {:width (:width size)
:height (:height size)
@ -147,16 +141,15 @@
:local local}]
(for [overlay (:overlays local)]
(let [size-over (calculate-size overlay zoom)
pos-over (position-overlay size size-over)]
(let [size-over (calculate-size (:frame overlay) zoom)]
[:div.viewport-container
{:style {:width (:width size-over)
:height (:height size-over)
:position "absolute"
:left (:x pos-over)
:top (:y pos-over)}}
:left (:x (:position overlay))
:top (:y (:position overlay))}}
[:& interactions/viewport
{:frame overlay
{:frame (:frame overlay)
:size size-over
:page page
:file file

View file

@ -39,9 +39,10 @@
(st/emit! (dv/go-to-frame frame-id)))
:open-overlay
(let [frame-id (:destination interaction)]
(let [frame-id (:destination interaction)
position (:overlay-position interaction)]
(dom/stop-propagation event)
(st/emit! (dv/open-overlay frame-id)))
(st/emit! (dv/open-overlay frame-id position)))
:close-overlay
(let [frame-id (or (:destination interaction)

View file

@ -8,7 +8,7 @@
(:require
[app.common.data :as d]
[app.common.pages :as cp]
[app.common.pages.spec :as spec]
[app.common.types.interactions :as cti]
[app.common.uuid :as uuid]
[app.main.data.workspace :as dw]
[app.main.refs :as refs]
@ -21,7 +21,7 @@
(defn- event-type-names
[]
{:click (tr "workspace.options.interaction-on-click")
:hover (tr "workspace.options.interaction-while-hovering")})
:mouse-over (tr "workspace.options.interaction-while-hovering")})
(defn- event-type-name
[interaction]
@ -56,18 +56,18 @@
change-event-type
(fn [event]
(let [value (-> event dom/get-target dom/get-value d/read-string)]
(update-interaction index #(assoc % :event-type value))))
(update-interaction index #(cti/set-event-type % value))))
change-action-type
(fn [event]
(let [value (-> event dom/get-target dom/get-value d/read-string)]
(update-interaction index #(assoc % :action-type value))))
(update-interaction index #(cti/set-action-type % value))))
change-destination
(fn [event]
(let [value (-> event dom/get-target dom/get-value)
value (when (not= value "") (uuid/uuid value))]
(update-interaction index #(assoc % :destination value))))]
(update-interaction index #(cti/set-destination % value shape objects))))]
[:*
[:div.element-set-options-group
@ -84,21 +84,21 @@
[:div.interactions-element
[:span.element-set-subtitle.wide (tr "workspace.options.interaction-trigger")]
[:select.input-select
{:default-value (str (:event-type interaction))
{:value (str (:event-type interaction))
:on-change change-event-type}
(for [[value name] (event-type-names)]
[:option {:value (str value)} name])]]
[:div.interactions-element
[:span.element-set-subtitle.wide (tr "workspace.options.interaction-action")]
[:select.input-select
{:default-value (str (:action-type interaction))
{:value (str (:action-type interaction))
:on-change change-action-type}
(for [[value name] (action-type-names)]
[:option {:value (str value)} name])]]
[:div.interactions-element
[:span.element-set-subtitle.wide (tr "workspace.options.interaction-destination")]
[:select.input-select
{:default-value (str (:destination interaction))
{:value (str (:destination interaction))
:on-change change-destination}
[:option {:value ""} (tr "workspace.options.interaction-none")]
(for [frame frames]
@ -112,8 +112,7 @@
add-interaction
(fn [_]
(let [new-interactions
(conj interactions (update spec/default-interaction :event-type identity))]
(let [new-interactions (conj interactions cti/default-interaction)]
(st/emit! (dw/update-shape (:id shape) {:interactions new-interactions}))))
remove-interaction

View file

@ -8,6 +8,7 @@
"Visually show shape interactions in workspace"
(:require
[app.common.data :as d]
[app.common.pages.helpers :as cph]
[app.main.data.workspace :as dw]
[app.main.refs :as refs]
[app.main.store :as st]
@ -86,11 +87,8 @@
:right "M -5 0 l 8 0 l -4 -4 m 4 4 l -4 4"
:left "M 5 0 l -8 0 l 4 -4 m -4 4 l 4 4"
nil)
:open-overlay (case arrow-dir
;; TODO: have a different icon for open overlay?
:right "M -5 0 l 8 0 l -4 -4 m 4 4 l -4 4"
:left "M 5 0 l -8 0 l 4 -4 m -4 4 l 4 4"
nil)
:open-overlay "M-4 -4 h6 v6 h-6 z M2 -2 h2.5 v6.5 h-6.5 v-2.5"
:close-overlay "M -4 -4 L 4 4 M -4 4 L 4 -4"
@ -162,6 +160,11 @@
:pointer-events "visible"
:stroke-width (/ 2 zoom)
:d pdata}]
(when dest-shape
[:& outline {:shape dest-shape
:color "#31EFB8"}])
[:& interaction-marker {:index index
:x orig-x
:y orig-y
@ -173,11 +176,7 @@
:stroke "#31EFB8"
:action-type action-type
:arrow-dir arrow-dir
:zoom zoom}]
(when dest-shape
[:& outline {:shape dest-shape
:color "#31EFB8"}])])))
:zoom zoom}]])))
(mf/defc interaction-handle
@ -194,6 +193,37 @@
:zoom zoom}]]))
(mf/defc overlay-marker
[{:keys [index orig-shape dest-shape position objects zoom] :as props}]
(let [start-move-position
(fn [event]
(st/emit! (dw/start-move-overlay-pos index)))]
(when dest-shape
(let [orig-frame (cph/get-frame orig-shape objects)
marker-x (+ (:x orig-frame) (:x position))
marker-y (+ (:y orig-frame) (:y position))
width (:width dest-shape)
height (:height dest-shape)]
[:g {:on-mouse-down start-move-position}
[:path {:stroke "#31EFB8"
:fill "#000000"
:fill-opacity 0.3
:stroke-width 1
:d (str "M" marker-x " " marker-y " "
"h " width " "
"v " height " "
"h -" width " z"
"M" marker-x " " marker-y " "
"l " width " " height " "
"M" marker-x " " (+ marker-y height) " "
"l " width " -" height " ")}]
[:circle {:cx (+ marker-x (/ width 2))
:cy (+ marker-y (/ height 2))
:r 8
:fill "#31EFB8"}]
]))))
(mf/defc interactions
[{:keys [selected] :as props}]
(let [local (mf/deref refs/workspace-local)
@ -205,6 +235,8 @@
editing-interaction-index (:editing-interaction-index local)
draw-interaction-to (:draw-interaction-to local)
draw-interaction-to-frame (:draw-interaction-to-frame local)
move-overlay-to (:move-overlay-to local)
move-overlay-index (:move-overlay-index local)
first-selected (first selected-shapes)]
[:g.interactions
@ -238,14 +270,32 @@
(for [[index interaction] (d/enumerate (:interactions shape))]
(when-not (= index editing-interaction-index)
(let [dest-shape (get objects (:destination interaction))]
[:& interaction-path {:key (str (:id shape) "-" index)
:index index
:orig-shape shape
:dest-shape dest-shape
:selected selected
:selected? true
:action-type (:action-type interaction)
:zoom zoom}])))
[:*
[:& interaction-path {:key (str (:id shape) "-" index)
:index index
:orig-shape shape
:dest-shape dest-shape
:selected selected
:selected? true
:action-type (:action-type interaction)
:zoom zoom}]
(when (= (:action-type interaction) :open-overlay)
(if (and (some? move-overlay-to)
(= move-overlay-index index))
[:& overlay-marker {:key (str "pos" (:id shape) "-" index)
:index index
:orig-shape shape
:dest-shape dest-shape
:position move-overlay-to
:objects objects
:zoom zoom}]
[:& overlay-marker {:key (str "pos" (:id shape) "-" index)
:index index
:orig-shape shape
:dest-shape dest-shape
:position (:overlay-position interaction)
:objects objects
:zoom zoom}]))])))
(when (not (#{:move :rotate} current-transform))
[:& interaction-handle {:key (:id shape)
:index nil