From d5ab0eea1a0c528446df180d39fb6cda4b3a7269 Mon Sep 17 00:00:00 2001 From: "alonso.torres" Date: Tue, 3 Jan 2023 16:57:01 +0100 Subject: [PATCH] :zap: Removed reflow in viewport --- .../src/app/main/ui/workspace/viewport.cljs | 16 ++--- .../main/ui/workspace/viewport/actions.cljs | 43 +++++------- .../app/main/ui/workspace/viewport/hooks.cljs | 20 ++++-- .../ui/workspace/viewport/scroll_bars.cljs | 14 ++-- .../app/main/ui/workspace/viewport/utils.cljs | 15 +---- .../ui/workspace/viewport/viewport_ref.cljs | 65 +++++++++++++++++++ 6 files changed, 111 insertions(+), 62 deletions(-) create mode 100644 frontend/src/app/main/ui/workspace/viewport/viewport_ref.cljs diff --git a/frontend/src/app/main/ui/workspace/viewport.cljs b/frontend/src/app/main/ui/workspace/viewport.cljs index a44943058..81b2ecec9 100644 --- a/frontend/src/app/main/ui/workspace/viewport.cljs +++ b/frontend/src/app/main/ui/workspace/viewport.cljs @@ -40,6 +40,7 @@ [app.main.ui.workspace.viewport.snap-distances :as snap-distances] [app.main.ui.workspace.viewport.snap-points :as snap-points] [app.main.ui.workspace.viewport.utils :as utils] + [app.main.ui.workspace.viewport.viewport-ref :refer [create-viewport-ref]] [app.main.ui.workspace.viewport.widgets :as widgets] [beicon.core :as rx] [debug :refer [debug?]] @@ -98,7 +99,8 @@ active-frames (mf/use-state #{}) ;; REFS - viewport-ref (mf/use-ref nil) + [viewport-ref + on-viewport-ref] (create-viewport-ref) ;; VARS disable-paste (mf/use-var false) @@ -140,15 +142,14 @@ on-double-click (actions/on-double-click hover hover-ids drawing-path? base-objects edition workspace-read-only?) on-drag-enter (actions/on-drag-enter) on-drag-over (actions/on-drag-over) - on-drop (actions/on-drop file viewport-ref zoom) + on-drop (actions/on-drop file) on-mouse-down (actions/on-mouse-down @hover selected edition drawing-tool text-editing? node-editing? - drawing-path? create-comment? space? viewport-ref zoom panning - workspace-read-only?) + drawing-path? create-comment? space? panning workspace-read-only?) on-mouse-up (actions/on-mouse-up disable-paste) on-pointer-down (actions/on-pointer-down) on-pointer-enter (actions/on-pointer-enter in-viewport?) on-pointer-leave (actions/on-pointer-leave in-viewport?) - on-pointer-move (actions/on-pointer-move viewport-ref zoom move-stream) + on-pointer-move (actions/on-pointer-move move-stream) on-pointer-up (actions/on-pointer-up) on-move-selected (actions/on-move-selected hover hover-ids selected space? workspace-read-only?) on-menu-selected (actions/on-menu-selected hover hover-ids selected workspace-read-only?) @@ -269,7 +270,7 @@ :preserveAspectRatio "xMidYMid meet" :key (str "viewport" page-id) :view-box (utils/format-viewbox vbox) - :ref viewport-ref + :ref on-viewport-ref :class (when drawing-tool "drawing") :style {:cursor @cursor} :fill "none" @@ -423,8 +424,7 @@ [:& scroll-bars/viewport-scrollbars {:objects base-objects :zoom zoom - :vbox vbox - :viewport-ref viewport-ref}] + :vbox vbox}] (when show-rules? [:& rules/rules diff --git a/frontend/src/app/main/ui/workspace/viewport/actions.cljs b/frontend/src/app/main/ui/workspace/viewport/actions.cljs index 0bf2505fd..1a85587f2 100644 --- a/frontend/src/app/main/ui/workspace/viewport/actions.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/actions.cljs @@ -19,7 +19,7 @@ [app.main.refs :as refs] [app.main.store :as st] [app.main.streams :as ms] - [app.main.ui.workspace.viewport.utils :as utils] + [app.main.ui.workspace.viewport.viewport-ref :as uwvv] [app.util.dom :as dom] [app.util.dom.dnd :as dnd] [app.util.dom.normalize-wheel :as nw] @@ -34,11 +34,11 @@ (defn on-mouse-down [{:keys [id blocked hidden type]} selected edition drawing-tool text-editing? - node-editing? drawing-path? create-comment? space? viewport-ref zoom panning + node-editing? drawing-path? create-comment? space? panning workspace-read-only?] (mf/use-callback (mf/deps id blocked hidden type selected edition drawing-tool text-editing? - node-editing? drawing-path? create-comment? @space? viewport-ref zoom + node-editing? drawing-path? create-comment? @space? panning workspace-read-only?) (fn [bevent] (when (or (dom/class? (dom/get-target bevent) "viewport-controls") @@ -61,8 +61,7 @@ (dom/prevent-default bevent) (if mod? (let [raw-pt (dom/get-client-position event) - viewport (mf/ref-val viewport-ref) - pt (utils/translate-point-to-viewport viewport zoom raw-pt)] + pt (uwvv/point->viewport raw-pt)] (st/emit! (dw/start-zooming pt))) (st/emit! (dw/start-panning)))) @@ -322,15 +321,12 @@ (= "TEXTAREA" (obj/get target "tagName")))] (st/emit! (ms/->KeyboardEvent :up key shift? ctrl? alt? meta? editing?)))))) -(defn on-mouse-move [viewport-ref zoom] +(defn on-mouse-move [] (let [last-position (mf/use-var nil)] (mf/use-callback - (mf/deps zoom) (fn [event] - (let [event (.getBrowserEvent ^js event) - raw-pt (dom/get-client-position event) - viewport (mf/ref-val viewport-ref) - pt (utils/translate-point-to-viewport viewport zoom raw-pt) + (let [raw-pt (dom/get-client-position event) + pt (uwvv/point->viewport raw-pt) ;; We calculate the delta because Safari's MouseEvent.movementX/Y drop ;; events @@ -350,30 +346,27 @@ (kbd/alt? event) (kbd/meta? event)))))))) -(defn on-pointer-move [viewport-ref zoom move-stream] +(defn on-pointer-move [move-stream] (mf/use-callback - (mf/deps zoom move-stream) + (mf/deps move-stream) (fn [event] (let [raw-pt (dom/get-client-position event) - viewport (mf/ref-val viewport-ref) - pt (utils/translate-point-to-viewport viewport zoom raw-pt)] + pt (uwvv/point->viewport raw-pt)] (rx/push! move-stream pt))))) -(defn on-mouse-wheel [viewport-ref zoom] +(defn on-mouse-wheel [zoom] (mf/use-callback (mf/deps zoom) (fn [event] - (let [viewport (mf/ref-val viewport-ref) - event (.getBrowserEvent ^js event) + (let [event (.getBrowserEvent ^js event) target (dom/get-target event) mod? (kbd/mod? event)] - (when (dom/is-child? viewport target) + (when (uwvv/inside-viewport? target) (dom/prevent-default event) (dom/stop-propagation event) - (let [pt (->> (dom/get-client-position event) - (utils/translate-point-to-viewport viewport zoom)) - + (let [raw-pt (dom/get-client-position event) + pt (uwvv/point->viewport raw-pt) norm-event ^js (nw/normalize-wheel event) ctrl? (kbd/ctrl? event) delta-y (.-pixelY norm-event) @@ -413,14 +406,12 @@ (dom/prevent-default e))))) (defn on-drop - [file viewport-ref zoom] + [file] (mf/use-fn - (mf/deps zoom) (fn [event] (dom/prevent-default event) (let [point (gpt/point (.-clientX event) (.-clientY event)) - viewport (mf/ref-val viewport-ref) - viewport-coord (utils/translate-point-to-viewport viewport zoom point) + viewport-coord (uwvv/point->viewport point) asset-id (-> (dnd/get-data event "text/asset-id") uuid/uuid) asset-name (dnd/get-data event "text/asset-name") asset-type (dnd/get-data event "text/asset-type")] diff --git a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs index fb20a66fb..5076cb86f 100644 --- a/frontend/src/app/main/ui/workspace/viewport/hooks.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/hooks.cljs @@ -35,21 +35,29 @@ (defn setup-dom-events [viewport-ref zoom disable-paste in-viewport? workspace-read-only?] (let [on-key-down (actions/on-key-down) on-key-up (actions/on-key-up) - on-mouse-move (actions/on-mouse-move viewport-ref zoom) - on-mouse-wheel (actions/on-mouse-wheel viewport-ref zoom) + on-mouse-move (actions/on-mouse-move) + on-mouse-wheel (actions/on-mouse-wheel zoom) on-paste (actions/on-paste disable-paste in-viewport? workspace-read-only?)] + + ;; We use the DOM listener because the goog.closure one forces reflow to generate its internal + ;; structure. As we don't need currently nothing from BrowserEvent we optimize by using the basic event + (mf/use-layout-effect + (mf/deps on-mouse-move) + (fn [] + (let [node (mf/ref-val viewport-ref)] + (.addEventListener node "mousemove" on-mouse-move) + (fn [] + (.removeEventListener node "mousemove" on-mouse-move))))) + (mf/use-layout-effect (mf/deps on-key-down on-key-up on-mouse-move on-mouse-wheel on-paste workspace-read-only?) (fn [] - (let [node (mf/ref-val viewport-ref) - keys [(events/listen js/document EventType.KEYDOWN on-key-down) + (let [keys [(events/listen js/document EventType.KEYDOWN on-key-down) (events/listen js/document EventType.KEYUP on-key-up) - (events/listen node EventType.MOUSEMOVE on-mouse-move) ;; bind with passive=false to allow the event to be cancelled ;; https://stackoverflow.com/a/57582286/3219895 (events/listen js/window EventType.WHEEL on-mouse-wheel #js {:passive false}) (events/listen js/window EventType.PASTE on-paste)]] - (fn [] (doseq [key keys] (events/unlistenByKey key)))))))) diff --git a/frontend/src/app/main/ui/workspace/viewport/scroll_bars.cljs b/frontend/src/app/main/ui/workspace/viewport/scroll_bars.cljs index 9e6a823b7..75166092a 100644 --- a/frontend/src/app/main/ui/workspace/viewport/scroll_bars.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/scroll_bars.cljs @@ -11,7 +11,7 @@ [app.common.pages.helpers :as cph] [app.main.data.workspace :as dw] [app.main.store :as st] - [app.main.ui.workspace.viewport.utils :as utils] + [app.main.ui.workspace.viewport.viewport-ref :refer [point->viewport]] [app.util.dom :as dom] [rumext.v2 :as mf])) @@ -27,7 +27,7 @@ (mf/defc viewport-scrollbars {::mf/wrap [mf/memo]} - [{:keys [objects viewport-ref zoom vbox]}] + [{:keys [objects zoom vbox]}] (let [v-scrolling? (mf/use-state false) h-scrolling? (mf/use-state false) @@ -126,10 +126,9 @@ on-mouse-move (fn [event axis] (when-let [_ (or @v-scrolling? @h-scrolling?)] - (let [viewport (mf/ref-val viewport-ref) - start-pt (mf/ref-val start-ref) + (let [start-pt (mf/ref-val start-ref) current-pt (dom/get-client-position event) - current-pt-viewport (utils/translate-point-to-viewport viewport zoom current-pt) + current-pt-viewport (point->viewport current-pt) y-delta (/ (* (mf/ref-val height-factor-ref) (- (:y current-pt) (:y start-pt))) zoom) x-delta (/ (* (mf/ref-val width-factor-ref) (- (:x current-pt) (:x start-pt))) zoom) new-v-scrollbar-y (-> current-pt-viewport @@ -150,9 +149,8 @@ on-mouse-down (fn [event axis] - (let [viewport (mf/ref-val viewport-ref) - start-pt (dom/get-client-position event) - viewport-point (utils/translate-point-to-viewport viewport zoom start-pt) + (let [start-pt (dom/get-client-position event) + viewport-point (point->viewport start-pt) new-h-scrollbar-x (:x viewport-point) new-v-scrollbar-y (:y viewport-point) v-scrollbar-y-padding (- v-scrollbar-y new-v-scrollbar-y) diff --git a/frontend/src/app/main/ui/workspace/viewport/utils.cljs b/frontend/src/app/main/ui/workspace/viewport/utils.cljs index 40b54f43c..59c411b4a 100644 --- a/frontend/src/app/main/ui/workspace/viewport/utils.cljs +++ b/frontend/src/app/main/ui/workspace/viewport/utils.cljs @@ -6,13 +6,11 @@ (ns app.main.ui.workspace.viewport.utils (:require - [app.common.data :as d] [app.common.data.macros :as dm] [app.common.geom.point :as gpt] [app.common.geom.shapes :as gsh] [app.main.ui.cursors :as cur] - [app.main.ui.formats :refer [format-number]] - [app.util.dom :as dom])) + [app.main.ui.formats :refer [format-number]])) (defn format-viewbox [vbox] (dm/str (format-number(:x vbox 0)) " " @@ -20,17 +18,6 @@ (format-number (:width vbox 0)) " " (format-number (:height vbox 0)))) -(defn translate-point-to-viewport [viewport zoom pt] - (let [vbox (.. ^js viewport -viewBox -baseVal) - brect (dom/get-bounding-rect viewport) - brect (gpt/point (d/parse-integer (:left brect)) - (d/parse-integer (:top brect))) - box (gpt/point (.-x vbox) (.-y vbox)) - zoom (gpt/point zoom)] - (-> (gpt/subtract pt brect) - (gpt/divide zoom) - (gpt/add box)))) - (defn get-cursor [cursor] (case cursor :hand cur/hand diff --git a/frontend/src/app/main/ui/workspace/viewport/viewport_ref.cljs b/frontend/src/app/main/ui/workspace/viewport/viewport_ref.cljs new file mode 100644 index 000000000..aa293f191 --- /dev/null +++ b/frontend/src/app/main/ui/workspace/viewport/viewport_ref.cljs @@ -0,0 +1,65 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns app.main.ui.workspace.viewport.viewport-ref + (:require + [app.common.data :as d] + [app.common.data.macros :as dm] + [app.common.geom.point :as gpt] + [app.main.store :as st] + [app.util.dom :as dom] + [rumext.v2 :as mf])) + +(defonce viewport-ref (atom nil)) +(defonce current-observer (atom nil)) +(defonce viewport-brect (atom nil)) + +(defn init-observer + [node on-change-bounds] + + (let [observer (js/ResizeObserver. on-change-bounds)] + (when (some? @current-observer) + (.disconnect @current-observer)) + + (reset! current-observer observer) + + (when (some? node) + (.observe observer node)))) + +(defn on-change-bounds + [_] + (when @viewport-ref + (let [brect (dom/get-bounding-rect @viewport-ref) + brect (gpt/point (d/parse-integer (:left brect)) + (d/parse-integer (:top brect)))] + (reset! viewport-brect brect)))) + +(defn create-viewport-ref + [] + + (let [ref (mf/use-ref nil)] + [ref + (mf/use-memo + #(fn [node] + (mf/set-ref-val! ref node) + (reset! viewport-ref node) + (init-observer node on-change-bounds)))])) + +(defn point->viewport + [pt] + (let [zoom (dm/get-in @st/state [:workspace-local :zoom])] + (when (some? @viewport-ref) + (let [vbox (.. ^js @viewport-ref -viewBox -baseVal) + brect @viewport-brect + box (gpt/point (.-x vbox) (.-y vbox)) + zoom (gpt/point zoom)] + (-> (gpt/subtract pt brect) + (gpt/divide zoom) + (gpt/add box)))))) + +(defn inside-viewport? + [target] + (dom/is-child? @viewport-ref target))