Improved performance on moving and zooming

This commit is contained in:
alonso.torres 2020-10-05 08:52:06 +02:00 committed by Andrey Antukh
parent 96fbc83a0a
commit 12b4951994
10 changed files with 142 additions and 64 deletions

View file

@ -12,6 +12,7 @@
[beicon.core :as rx]
[cljs.spec.alpha :as s]
[potok.core :as ptk]
[linked.set :as lks]
[app.common.data :as d]
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as geom]
@ -133,13 +134,18 @@
ptk/WatchEvent
(watch [_ state stream]
(let [page-id (:current-page-id state)
selrect (get-in state [:workspace-local :selrect])]
selrect (get-in state [:workspace-local :selrect])
is-not-blocked (fn [shape-id] (not (get-in state [:workspace-data
:pages-index page-id
:objects shape-id
:blocked] false)))]
(rx/merge
(rx/of (update-selrect nil))
(when selrect
(->> (uw/ask! {:cmd :selection/query
:page-id page-id
:rect selrect})
(rx/map #(into lks/empty-linked-set (filter is-not-blocked) %))
(rx/map select-shapes))))))))
(defn select-inside-group

View file

@ -257,9 +257,11 @@
(rx/switch-map #(snap/closest-snap-move page-id shapes objects layout %))
(rx/map #(gpt/round % 0))
(rx/map gmt/translate-matrix)
(rx/map #(set-modifiers ids {:displacement %})))
(rx/map #(fn [state] (assoc-in state [:workspace-local :modifiers] {:displacement %}))))
(rx/of (apply-modifiers ids)
(rx/of (set-modifiers ids)
(apply-modifiers ids)
(fn [state] (update state :workspace-local dissoc :modifiers))
finish-transform)))))))
(defn- get-displacement-with-grid
@ -339,13 +341,15 @@
;; -- Apply modifiers
(defn set-modifiers
([ids] (set-modifiers ids nil true))
([ids modifiers] (set-modifiers ids modifiers true))
([ids modifiers recurse-frames?]
(us/verify (s/coll-of uuid?) ids)
(ptk/reify ::set-modifiers
ptk/UpdateEvent
(update [_ state]
(let [page-id (:current-page-id state)
(let [modifiers (or modifiers (get-in state [:workspace-local :modifiers] {}))
page-id (:current-page-id state)
objects (dwc/lookup-page-objects state page-id)
not-frame-id?

View file

@ -49,6 +49,10 @@
(def selected-shapes
(l/derived :selected workspace-local))
(defn make-selected-ref
[id]
(l/derived #(contains? % id) selected-shapes))
(def selected-zoom
(l/derived :zoom workspace-local))

View file

@ -11,6 +11,7 @@
"A workspace specific shapes wrappers."
(:require
[rumext.alpha :as mf]
[okulary.core :as l]
[beicon.core :as rx]
[app.main.streams :as ms]
[app.main.ui.hooks :as hooks]
@ -21,6 +22,7 @@
[app.main.ui.shapes.image :as image]
[app.main.data.workspace.selection :as dws]
[app.main.store :as st]
[app.main.refs :as refs]
;; Shapes that has some peculiarities are defined in its own
;; namespace under app.ui.workspace.shapes.* prefix, all the
@ -68,18 +70,30 @@
(fn []
(st/emit! (dws/change-hover-state id false)))))
(defn make-is-moving-ref
[id]
(let [check-moving (fn [local]
(and (= :move (:transform local))
(contains? (:selected local) id)))]
(l/derived check-moving refs/workspace-local)))
(mf/defc shape-wrapper
{::mf/wrap [#(mf/memo' % shape-wrapper-memo-equals?)]
::mf/wrap-props false}
[props]
(let [shape (unchecked-get props "shape")
frame (unchecked-get props "frame")
ghost? (unchecked-get props "ghost?")
shape (geom/transform-shape frame shape)
opts #js {:shape shape
:frame frame}
alt? (mf/use-state false)
on-mouse-enter (use-mouse-enter shape)
on-mouse-leave (use-mouse-leave shape)]
on-mouse-leave (use-mouse-leave shape)
moving-iref (mf/use-memo (mf/deps (:id shape))
#(make-is-moving-ref (:id shape)))
moving? (mf/deref moving-iref)]
(hooks/use-stream ms/keyboard-alt #(reset! alt? %))
@ -88,7 +102,9 @@
(fn []
(on-mouse-leave))))
(when (and shape (not (:hidden shape)))
(when (and shape
(or ghost? (not moving?))
(not (:hidden shape)))
[:g.shape-wrapper {:on-mouse-enter on-mouse-enter
:on-mouse-leave on-mouse-leave
:style {:cursor (if @alt? cur/duplicate nil)}}

View file

@ -45,9 +45,30 @@
(recur (first ids) (rest ids))
false))))))
(defn make-selected-ref
[id]
(l/derived #(contains? % id) refs/selected-shapes))
(mf/defc frame-title
[{:keys [frame on-double-click on-mouse-over on-mouse-out]}]
(let [zoom (mf/deref refs/selected-zoom)
inv-zoom (/ 1 zoom)
{:keys [width x y]} frame
label-pos (gpt/point x (- y (/ 10 zoom)))]
[:text {:x 0
:y 0
:width width
:height 20
:class "workspace-frame-label"
;; Ensure that the label has always the same font
;; size, regardless of zoom
;; https://css-tricks.com/transforms-on-svg-elements/
:transform (str
"scale(" inv-zoom ", " inv-zoom ") "
"translate(" (* zoom (:x label-pos)) ", "
(* zoom (:y label-pos))
")")
;; User may also select the frame with single click in the label
:on-click on-double-click
:on-mouse-over on-mouse-over
:on-mouse-out on-mouse-out}
(:name frame)]))
(defn frame-wrapper-factory
[shape-wrapper]
@ -61,9 +82,8 @@
objects (unchecked-get props "objects")
selected-iref (mf/use-memo (mf/deps (:id shape))
#(make-selected-ref (:id shape)))
#(refs/make-selected-ref (:id shape)))
selected? (mf/deref selected-iref)
zoom (mf/deref refs/selected-zoom)
on-mouse-down (mf/use-callback (mf/deps shape)
#(common/on-mouse-down % shape))
@ -71,14 +91,9 @@
#(common/on-context-menu % shape))
shape (geom/transform-shape shape)
{:keys [x y width height]} shape
inv-zoom (/ 1 zoom)
children (mapv #(get objects %) (:shapes shape))
ds-modifier (get-in shape [:modifiers :displacement])
label-pos (gpt/point x (- y (/ 10 zoom)))
on-double-click
(mf/use-callback
(mf/deps (:id shape))
@ -106,24 +121,11 @@
:on-context-menu on-context-menu
:on-double-click on-double-click
:on-mouse-down on-mouse-down}
[:text {:x 0
:y 0
:width width
:height 20
:class "workspace-frame-label"
;; Ensure that the label has always the same font
;; size, regardless of zoom
;; https://css-tricks.com/transforms-on-svg-elements/
:transform (str
"scale(" inv-zoom ", " inv-zoom ") "
"translate(" (* zoom (:x label-pos)) ", "
(* zoom (:y label-pos))
")")
;; User may also select the frame with single click in the label
:on-click on-double-click
:on-mouse-over on-mouse-over
:on-mouse-out on-mouse-out}
(:name shape)]
[:& frame-title {:frame shape
:on-context-menu on-context-menu
:on-double-click on-double-click
:on-mouse-down on-mouse-down}]
[:g.frame {:filter (filters/filter-str filter-id shape)}
[:& filters/filters {:filter-id filter-id :shape shape}]
[:& frame-shape

View file

@ -58,13 +58,15 @@
{::mf/wrap-props false}
[props]
(let [{:keys [id x1 y1 content group grow-type width height ] :as shape} (unchecked-get props "shape")
selected (mf/deref refs/selected-shapes)
selected-iref (mf/use-memo (mf/deps (:id shape))
#(refs/make-selected-ref (:id shape)))
selected? (mf/deref selected-iref)
edition (mf/deref refs/selected-edition)
zoom (mf/deref refs/selected-zoom)
current-transform (mf/deref refs/current-transform)
render-editor (mf/use-state false)
edition? (= edition id)
selected? (and (contains? selected id)
(= (count selected) 1))
embed-resources? (mf/use-ctx muc/embed-ctx)
@ -80,23 +82,30 @@
filter-id (mf/use-memo filters/get-filter-id)]
(mf/use-effect
(mf/deps shape edition selected? current-transform)
(fn [] (let [check? (and (#{:auto-width :auto-height} (:grow-type shape))
selected?
(not edition?)
(not embed-resources?)
(nil? current-transform))]
(timers/schedule #(reset! render-editor check?)))))
[:g.shape {:on-double-click on-double-click
:on-mouse-down on-mouse-down
:on-context-menu on-context-menu
:filter (filters/filter-str filter-id shape)}
[:& filters/filters {:filter-id filter-id :shape shape}]
[:*
(when (and (not edition?) (not embed-resources?))
(when @render-editor
[:g {:opacity 0
:style {:pointer-events "none"}}
;; We only render the component for its side-effect
[:& text-shape-edit {:shape shape
:zoom zoom
:read-only? true}]])
(if edition?
[:& text-shape-edit {:shape shape
:zoom zoom}]
[:& text-shape-edit {:shape shape}]
[:& text/text-shape {:shape shape
:selected? selected?}])]]))
@ -277,9 +286,9 @@
(mf/defc text-shape-edit
{::mf/wrap [mf/memo]}
[{:keys [shape zoom read-only?] :or {read-only? false} :as props}]
[{:keys [shape read-only?] :or {read-only? false} :as props}]
(let [{:keys [id x y width height content grow-type]} shape
zoom (mf/deref refs/selected-zoom)
state (mf/use-state #(parse-content content))
editor (mf/use-memo #(dwt/create-editor))
self-ref (mf/use-ref)

View file

@ -186,9 +186,9 @@
:name (:name item)})]
(mf/use-effect
(mf/deps selected?)
(mf/deps selected)
(fn []
(when selected?
(when (and (= (count selected) 1) selected?)
(.scrollIntoView (mf/ref-val dref) false))))
[:li {:on-context-menu on-context-menu

View file

@ -247,12 +247,16 @@
transform (unchecked-get props "transform")
selected-shapes (mf/deref (refs/objects-by-id selected))
frame-id (-> selected-shapes first :frame-id)
frame (mf/deref (refs/object-by-id frame-id))]
frame (mf/deref (refs/object-by-id frame-id))
local (mf/deref refs/workspace-local)
update-shape (fn [shape] (-> shape
(update :modifiers merge (:modifiers local))
gsh/transform-shape))]
(when (and (contains? layout :dynamic-alignment)
(= transform :move)
(not (empty? selected)))
(let [shapes (map gsh/transform-shape selected-shapes)
selrect (gsh/selection-rect shapes)
(let [selrect (->> selected-shapes (map update-shape) gsh/selection-rect)
key (->> selected (map str) (str/join "-"))]
[:g.distance
[:& shape-distance

View file

@ -56,11 +56,15 @@
:opacity line-opacity}])
(defn get-snap
[coord {:keys [shapes page-id filter-shapes]}]
[coord {:keys [shapes page-id filter-shapes local]}]
(let [shape (if (> (count shapes) 1)
(->> shapes (map gsh/transform-shape) gsh/selection-rect)
(->> shapes (first)))
shape (if (:modifiers local)
(-> shape (assoc :modifiers (:modifiers local)) gsh/transform-shape)
shape)
frame-id (snap/snap-frame-id shapes)]
(->> (rx/of shape)
@ -104,7 +108,7 @@
(hash-map coord fixedv (flip coord) maxv)]))))
(mf/defc snap-feedback
[{:keys [shapes page-id filter-shapes zoom] :as props}]
[{:keys [shapes page-id filter-shapes zoom local] :as props}]
(let [state (mf/use-state [])
subject (mf/use-memo #(rx/subject))
@ -129,7 +133,7 @@
#(rx/dispose! sub))))
(mf/use-effect
(mf/deps shapes)
(mf/deps shapes local)
(fn []
(rx/push! subject props)))
@ -150,7 +154,7 @@
(mf/defc snap-points
{::mf/wrap [mf/memo]}
[{:keys [layout zoom selected page-id drawing transform] :as props}]
[{:keys [layout zoom selected page-id drawing transform local] :as props}]
(let [shapes (mf/deref (refs/objects-by-id selected))
filter-shapes (mf/deref refs/selected-shapes-with-children)
filter-shapes (fn [id]
@ -166,5 +170,6 @@
[:& snap-feedback {:shapes shapes
:page-id page-id
:filter-shapes filter-shapes
:zoom zoom}])))
:zoom zoom
:local local}])))

View file

@ -148,26 +148,35 @@
{::mf/wrap [mf/memo]
::mf/wrap-props false}
[props]
(let [data (mf/deref refs/workspace-page)
hover (unchecked-get props "hover")
(let [hover (unchecked-get props "hover")
selected (unchecked-get props "selected")
ids (unchecked-get props "ids")
ghost? (unchecked-get props "ghost?")
data (mf/deref refs/workspace-page)
objects (:objects data)
root (get objects uuid/zero)
shapes (->> (:shapes root)
(map #(get objects %)))]
(map #(get objects %)))
shapes (if ids
(->> ids (map #(get objects %)))
shapes)]
[:*
[:g.shapes
(for [item shapes]
(if (= (:type item) :frame)
[:& frame-wrapper {:shape item
:key (:id item)
:objects objects}]
:objects objects
:ghost? ghost?}]
[:& shape-wrapper {:shape item
:key (:id item)}]))]
:key (:id item)
:ghost? ghost?}]))]
[:& shape-outlines {:objects objects
:selected selected
:hover hover}]]))
(when (not ghost?)
[:& shape-outlines {:objects objects
:selected selected
:hover hover}])]))
(defn format-viewbox [vbox]
(str/join " " [(+ (:x vbox 0) (:left-offset vbox 0))
@ -286,6 +295,12 @@
panning
picking-color?]} local
selrect-orig (->> (mf/deref refs/selected-objects)
gsh/selection-rect)
selrect (-> selrect-orig
(assoc :modifiers (:modifiers local))
(gsh/transform-shape))
file (mf/deref refs/workspace-file)
viewport-ref (mf/use-ref nil)
zoom-view-ref (mf/use-ref nil)
@ -610,6 +625,18 @@
:hover (:hover local)
:selected (:selected selected)}]
(when (= :move (:transform local))
[:svg.ghost
{:x (:x selrect)
:y (:y selrect)
:width (:width selrect)
:height (:height selrect)
:style {:pointer-events "none"}}
[:g {:transform (str/fmt "translate(%s,%s)" (- (:x selrect-orig)) (- (:y selrect-orig)))}
[:& frames {:ids selected
:ghost? true}]]])
(when (seq selected)
[:& selection-handlers {:selected selected
:zoom zoom
@ -628,7 +655,8 @@
:drawing drawing-obj
:zoom zoom
:page-id page-id
:selected selected}]
:selected selected
:local local}]
[:& snap-distances {:layout layout
:zoom zoom