mirror of
https://github.com/penpot/penpot.git
synced 2025-02-13 02:28:18 -05:00
Merge pull request #491 from penpot/fix/cursor-responsiveness
Cursor responsiveness
This commit is contained in:
commit
f72a09b698
8 changed files with 154 additions and 54 deletions
Before Width: | Height: | Size: 442 B After Width: | Height: | Size: 442 B |
|
@ -130,7 +130,7 @@
|
|||
grid-template-columns: 20px 1fr;
|
||||
|
||||
.viewport {
|
||||
cursor: var(--cursor);
|
||||
cursor: none;
|
||||
grid-column: 1 / span 2;
|
||||
grid-row: 1 / span 2;
|
||||
overflow: hidden;
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
;; --- Auxiliar Functions
|
||||
|
||||
(s/def ::platform #{:windows :linux :macos :other})
|
||||
(s/def ::browser #{:chrome :mozilla :safari :edge :other})
|
||||
(s/def ::browser #{:chrome :firefox :safari :edge :other})
|
||||
|
||||
(defn- parse-browser
|
||||
[]
|
||||
|
|
|
@ -54,6 +54,23 @@
|
|||
[id]
|
||||
(l/derived #(contains? % id) selected-shapes))
|
||||
|
||||
(def viewport-data
|
||||
(l/derived #(select-keys % [:options-mode
|
||||
:zoom
|
||||
:vport
|
||||
:vbox
|
||||
:edition
|
||||
:edit-path
|
||||
:tooltip
|
||||
:selected
|
||||
:panning
|
||||
:picking-color?
|
||||
:transform
|
||||
:hover
|
||||
:modifiers
|
||||
:selrect])
|
||||
workspace-local =))
|
||||
|
||||
(def selected-zoom
|
||||
(l/derived :zoom workspace-local))
|
||||
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
(def create-artboard (cursor-ref :create-artboard))
|
||||
(def create-ellipse (cursor-ref :create-ellipse))
|
||||
(def create-polygon (cursor-ref :create-polygon))
|
||||
(def create-rectangle (cursor-ref :create-reclangle))
|
||||
(def create-rectangle (cursor-ref :create-rectangle))
|
||||
(def create-shape (cursor-ref :create-shape))
|
||||
(def duplicate (cursor-ref :duplicate 0 0 0))
|
||||
(def hand (cursor-ref :hand))
|
||||
|
|
|
@ -60,7 +60,8 @@
|
|||
(mf/defc workspace-content
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [local (mf/deref refs/workspace-local)
|
||||
(let [local (mf/deref refs/viewport-data)
|
||||
{:keys [zoom vbox vport options-mode selected]} local
|
||||
file (obj/get props "file")
|
||||
layout (obj/get props "layout")]
|
||||
[:*
|
||||
|
@ -72,9 +73,9 @@
|
|||
[:section.workspace-content
|
||||
[:section.workspace-viewport
|
||||
(when (contains? layout :rules)
|
||||
[:& workspace-rules {:zoom (:zoom local)
|
||||
:vbox (:vbox local)
|
||||
:vport (:vport local)}])
|
||||
[:& workspace-rules {:zoom zoom
|
||||
:vbox vbox
|
||||
:vport vport}])
|
||||
|
||||
[:& viewport-actions]
|
||||
[:& viewport {:file file
|
||||
|
@ -85,8 +86,8 @@
|
|||
|
||||
;; Aside
|
||||
[:& left-sidebar {:layout layout}]
|
||||
[:& right-sidebar {:section (:options-mode local)
|
||||
:selected (:selected local)}]]))
|
||||
[:& right-sidebar {:section options-mode
|
||||
:selected selected}]]))
|
||||
|
||||
(def trimmed-page-ref (l/derived :trimmed-page st/state =))
|
||||
|
||||
|
|
|
@ -56,13 +56,13 @@
|
|||
:opacity line-opacity}])
|
||||
|
||||
(defn get-snap
|
||||
[coord {:keys [shapes page-id filter-shapes local]}]
|
||||
[coord {:keys [shapes page-id filter-shapes modifiers]}]
|
||||
(let [shape (if (> (count shapes) 1)
|
||||
(->> shapes (map gsh/transform-shape) gsh/selection-rect (gsh/setup {:type :rect}))
|
||||
(->> shapes (first)))
|
||||
|
||||
shape (if (:modifiers local)
|
||||
(-> shape (assoc :modifiers (:modifiers local)) gsh/transform-shape)
|
||||
shape (if modifiers
|
||||
(-> shape (assoc :modifiers modifiers) gsh/transform-shape)
|
||||
shape)
|
||||
|
||||
frame-id (snap/snap-frame-id shapes)]
|
||||
|
@ -108,7 +108,7 @@
|
|||
(hash-map coord fixedv (flip coord) maxv)]))))
|
||||
|
||||
(mf/defc snap-feedback
|
||||
[{:keys [shapes page-id filter-shapes zoom local] :as props}]
|
||||
[{:keys [shapes page-id filter-shapes zoom modifiers] :as props}]
|
||||
(let [state (mf/use-state [])
|
||||
subject (mf/use-memo #(rx/subject))
|
||||
|
||||
|
@ -134,7 +134,7 @@
|
|||
#(rx/dispose! sub))))
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps shapes local)
|
||||
(mf/deps shapes modifiers)
|
||||
(fn []
|
||||
(rx/push! subject props)))
|
||||
|
||||
|
@ -155,7 +155,7 @@
|
|||
|
||||
(mf/defc snap-points
|
||||
{::mf/wrap [mf/memo]}
|
||||
[{:keys [layout zoom selected page-id drawing transform local] :as props}]
|
||||
[{:keys [layout zoom selected page-id drawing transform modifiers] :as props}]
|
||||
(let [shapes (mf/deref (refs/objects-by-id selected))
|
||||
filter-shapes (mf/deref refs/selected-shapes-with-children)
|
||||
filter-shapes (fn [id]
|
||||
|
@ -164,13 +164,11 @@
|
|||
(not (contains? layout :snap-grid)))
|
||||
(or (filter-shapes id)
|
||||
(not (contains? layout :dynamic-alignment)))))
|
||||
;; current-transform (mf/deref refs/current-transform)
|
||||
;; snap-data (mf/deref refs/workspace-snap-data)
|
||||
shapes (if drawing [drawing] shapes)]
|
||||
(when (or drawing transform)
|
||||
[:& snap-feedback {:shapes shapes
|
||||
:page-id page-id
|
||||
:filter-shapes filter-shapes
|
||||
:zoom zoom
|
||||
:local local}])))
|
||||
:modifiers modifiers}])))
|
||||
|
||||
|
|
|
@ -59,6 +59,25 @@
|
|||
(:import goog.events.EventType
|
||||
goog.events.WheelEvent))
|
||||
|
||||
(defonce css-mouse?
|
||||
(cfg/check-browser? :firefox))
|
||||
|
||||
(defn get-cursor [cursor]
|
||||
(if-not css-mouse?
|
||||
(name cursor)
|
||||
|
||||
(case cursor
|
||||
:hand cur/hand
|
||||
:comments cur/comments
|
||||
:create-artboard cur/create-artboard
|
||||
:create-rectangle cur/create-rectangle
|
||||
:create-ellipse cur/create-ellipse
|
||||
:pen cur/pen
|
||||
:pencil cur/pencil
|
||||
:create-shape cur/create-shape
|
||||
:duplicate cur/duplicate
|
||||
cur/pointer-inner)))
|
||||
|
||||
;; --- Coordinates Widget
|
||||
|
||||
(mf/defc coordinates
|
||||
|
@ -128,6 +147,65 @@
|
|||
|
||||
(declare remote-user-cursors)
|
||||
|
||||
(mf/defc render-cursor
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [cursor (unchecked-get props "cursor")
|
||||
viewport-ref (unchecked-get props "viewport")
|
||||
|
||||
visible? (mf/use-state true)
|
||||
in-viewport? (mf/use-state true)
|
||||
|
||||
cursor-ref (mf/use-ref nil)
|
||||
viewport (mf/ref-val viewport-ref)
|
||||
|
||||
node (mf/ref-val cursor-ref)
|
||||
|
||||
on-mouse-move
|
||||
(mf/use-callback
|
||||
(mf/deps node @visible?)
|
||||
(fn [left top event]
|
||||
|
||||
(let [target (dom/get-target event)
|
||||
style (.getComputedStyle js/window target)
|
||||
cursor (.getPropertyValue style "cursor")
|
||||
|
||||
x (- (.-clientX event) left)
|
||||
y (- (.-clientY event) top)]
|
||||
|
||||
(cond
|
||||
(and (= cursor "none") (not @visible?))
|
||||
(reset! visible? true)
|
||||
|
||||
(and (not= cursor "none") @visible?)
|
||||
(reset! visible? false))
|
||||
|
||||
(timers/raf
|
||||
#(let [style (obj/get node "style")]
|
||||
(obj/set! style "transform" (str "translate(" x "px, " y "px)")))))))]
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps viewport on-mouse-move)
|
||||
(fn []
|
||||
(when viewport
|
||||
(let [{:keys [left top]} (dom/get-bounding-rect viewport)
|
||||
keys [(events/listen (dom/get-root) EventType.MOUSEMOVE (partial on-mouse-move left top))
|
||||
(events/listen viewport EventType.POINTERENTER #(reset! in-viewport? true))
|
||||
(events/listen viewport EventType.POINTERLEAVE #(reset! in-viewport? false))]]
|
||||
|
||||
(fn []
|
||||
(doseq [key keys]
|
||||
(events/unlistenByKey key)))))))
|
||||
|
||||
[:svg {:ref cursor-ref
|
||||
:width 20
|
||||
:height 20
|
||||
:viewBox "0 0 16 18"
|
||||
:style {:position "absolute"
|
||||
:pointer-events "none"
|
||||
:will-change "transform"
|
||||
:display (when-not (and @in-viewport? @visible?) "none")}}
|
||||
[:use {:xlinkHref (str "#cursor-" cursor)}]]))
|
||||
|
||||
;; TODO: revisit the refs usage (vs props)
|
||||
(mf/defc shape-outlines
|
||||
|
@ -227,9 +305,10 @@
|
|||
|
||||
(mf/defc viewport
|
||||
[{:keys [local layout file] :as props}]
|
||||
(let [{:keys [options-mode
|
||||
(let [;; When adding data from workspace-local revisit `app.main.ui.workspace` to check
|
||||
;; that the new parameter is sent
|
||||
{:keys [options-mode
|
||||
zoom
|
||||
flags
|
||||
vport
|
||||
vbox
|
||||
edition
|
||||
|
@ -237,14 +316,18 @@
|
|||
tooltip
|
||||
selected
|
||||
panning
|
||||
picking-color?]} local
|
||||
picking-color?
|
||||
transform
|
||||
hover
|
||||
modifiers
|
||||
selrect]} local
|
||||
|
||||
page-id (mf/use-ctx ctx/current-page-id)
|
||||
|
||||
selected-objects (mf/deref refs/selected-objects)
|
||||
|
||||
alt? (mf/use-state false)
|
||||
cursor (mf/use-state cur/pointer-inner)
|
||||
cursor (mf/use-state (get-cursor :pointer-inner))
|
||||
viewport-ref (mf/use-ref nil)
|
||||
zoom-view-ref (mf/use-ref nil)
|
||||
last-position (mf/use-var nil)
|
||||
|
@ -258,9 +341,9 @@
|
|||
|
||||
show-grids? (contains? layout :display-grid)
|
||||
show-snap-points? (and (contains? layout :dynamic-alignment)
|
||||
(or drawing-obj (:transform local)))
|
||||
(or drawing-obj transform))
|
||||
show-snap-distance? (and (contains? layout :dynamic-alignment)
|
||||
(= (:transform local) :move)
|
||||
(= transform :move)
|
||||
(not (empty? selected)))
|
||||
|
||||
on-mouse-down
|
||||
|
@ -593,29 +676,24 @@
|
|||
;; We schedule the event so it fires after `initialize-page` event
|
||||
(timers/schedule #(st/emit! (dw/initialize-viewport size))))))
|
||||
|
||||
;; This change is in an effect to minimize the sideffects of the cursor chaning
|
||||
;; Changing a cursor will produce a "reflow" so we defer it until the component is rendered
|
||||
(mf/use-layout-effect
|
||||
(mf/use-effect
|
||||
(mf/deps @cursor @alt? panning drawing-tool drawing-path?)
|
||||
(fn []
|
||||
(let [new-cursor
|
||||
(cond
|
||||
panning cur/hand
|
||||
(= drawing-tool :comments) cur/comments
|
||||
(= drawing-tool :frame) cur/create-artboard
|
||||
(= drawing-tool :rect) cur/create-rectangle
|
||||
(= drawing-tool :circle) cur/create-ellipse
|
||||
(or (= drawing-tool :path) drawing-path?) cur/pen
|
||||
(= drawing-tool :curve) cur/pencil
|
||||
drawing-tool cur/create-shape
|
||||
@alt? cur/duplicate
|
||||
:else cur/pointer-inner)]
|
||||
panning (get-cursor :hand)
|
||||
(= drawing-tool :comments) (get-cursor :comments)
|
||||
(= drawing-tool :frame) (get-cursor :create-artboard)
|
||||
(= drawing-tool :rect) (get-cursor :create-rectangle)
|
||||
(= drawing-tool :circle) (get-cursor :create-ellipse)
|
||||
(or (= drawing-tool :path)
|
||||
drawing-path?) (get-cursor :pen)
|
||||
(= drawing-tool :curve) (get-cursor :pencil)
|
||||
drawing-tool (get-cursor :create-shape)
|
||||
@alt? (get-cursor :duplicate)
|
||||
:else (get-cursor :pointer-inner))]
|
||||
|
||||
;; Chrome BUG: https://bugs.chromium.org/p/chromium/issues/detail?id=664066
|
||||
;; Right now this is a performance concern but cannot find a better alternative
|
||||
(when (not= @cursor new-cursor)
|
||||
(timers/raf
|
||||
#(dom/set-css-property (dom/get-root) "--cursor" new-cursor))
|
||||
(reset! cursor new-cursor)))))
|
||||
|
||||
(mf/use-layout-effect (mf/deps layout) on-resize)
|
||||
|
@ -630,13 +708,17 @@
|
|||
:layout layout}])
|
||||
|
||||
(when (= drawing-tool :comments)
|
||||
[:& comments-layer {:vbox (:vbox local)
|
||||
:vport (:vport local)
|
||||
:zoom (:zoom local)
|
||||
[:& comments-layer {:vbox vbox
|
||||
:vport vport
|
||||
:zoom zoom
|
||||
:drawing drawing
|
||||
:page-id page-id
|
||||
:file-id (:id file)}])
|
||||
|
||||
(when-not css-mouse?
|
||||
[:& render-cursor {:viewport viewport-ref
|
||||
:cursor @cursor}])
|
||||
|
||||
[:svg.viewport
|
||||
{:xmlns "http://www.w3.org/2000/svg"
|
||||
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
||||
|
@ -647,7 +729,8 @@
|
|||
:view-box (format-viewbox vbox)
|
||||
:ref viewport-ref
|
||||
:class (when drawing-tool "drawing")
|
||||
:style {:background-color (get options :background "#E8E9EA")}
|
||||
:style {:cursor (when css-mouse? @cursor)
|
||||
:background-color (get options :background "#E8E9EA")}
|
||||
:on-context-menu on-context-menu
|
||||
:on-click on-click
|
||||
:on-double-click on-double-click
|
||||
|
@ -665,19 +748,19 @@
|
|||
"none"
|
||||
"auto")}}
|
||||
[:& frames {:key page-id
|
||||
:hover (:hover local)
|
||||
:hover hover
|
||||
:selected selected
|
||||
:edition edition}]
|
||||
|
||||
[:g {:style {:display (when (not= :move (:transform local)) "none")}}
|
||||
[:& ghost-frames {:modifiers (:modifiers local)
|
||||
[:g {:style {:display (when (not= :move transform) "none")}}
|
||||
[:& ghost-frames {:modifiers modifiers
|
||||
:selected selected}]]
|
||||
|
||||
(when (seq selected)
|
||||
[:& selection-handlers {:selected selected
|
||||
:zoom zoom
|
||||
:edition edition
|
||||
:show-distances (and (not (:transform local)) @alt?)}])
|
||||
:show-distances (and (not transform) @alt?)}])
|
||||
|
||||
(when (= (count selected) 1)
|
||||
[:& gradient-handlers {:id (first selected)
|
||||
|
@ -687,24 +770,24 @@
|
|||
[:& draw-area {:shape drawing-obj
|
||||
:zoom zoom
|
||||
:tool drawing-tool
|
||||
:modifiers (:modifiers local)}])
|
||||
:modifiers modifiers}])
|
||||
|
||||
(when show-grids?
|
||||
[:& frame-grid {:zoom zoom}])
|
||||
|
||||
(when show-snap-points?
|
||||
[:& snap-points {:layout layout
|
||||
:transform (:transform local)
|
||||
:transform transform
|
||||
:drawing drawing-obj
|
||||
:zoom zoom
|
||||
:page-id page-id
|
||||
:selected selected
|
||||
:local local}])
|
||||
:modifiers modifiers}])
|
||||
|
||||
(when show-snap-distance?
|
||||
[:& snap-distances {:layout layout
|
||||
:zoom zoom
|
||||
:transform (:transform local)
|
||||
:transform transform
|
||||
:selected selected
|
||||
:page-id page-id}])
|
||||
|
||||
|
@ -712,7 +795,8 @@
|
|||
[:& cursor-tooltip {:zoom zoom :tooltip tooltip}])]
|
||||
|
||||
[:& presence/active-cursors {:page-id page-id}]
|
||||
[:& selection-rect {:data (:selrect local)}]
|
||||
[:& selection-rect {:data selrect}]
|
||||
|
||||
(when (= options-mode :prototype)
|
||||
[:& interactions {:selected selected}])]]))
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue