0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-01-10 00:40:30 -05:00

🚧 Start working on shape rotation (with mouse handlers).

This commit is contained in:
Andrey Antukh 2020-01-15 00:15:23 +01:00
parent 747213dea3
commit 338a0b97ac
4 changed files with 89 additions and 36 deletions

View file

@ -89,6 +89,7 @@
g.controls {
rect.main { pointer-events: none; }
circle.rotate { cursor: ns-resize; } // TODO
circle.top-left { cursor: nwse-resize; }
circle.bottom-right { cursor: nwse-resize; }
circle.top-right { cursor: nesw-resize; }

View file

@ -49,17 +49,21 @@
{:keys [x y width height]} shape
transform (when (pos? rotation)
(str (rotate (gmt/matrix) shape)))
;; transform (when (pos? rotation)
;; (str (rotate (gmt/matrix) shape)))
moving? (boolean modifier-mtx)
transform (str/format "rotate(%s %s %s)"
rotation
(+ x (/ width 2))
(+ y (/ height 2)))
props (-> (attrs/extract-style-attrs shape)
(assoc :x x
:y y
;; :transform transform
:id (str "shape-" id)
:className (classnames :move-cursor moving?)
:width width
:height height
:transform transform))]
;; :transform transform
))]
[:& "rect" props]))

View file

@ -10,6 +10,7 @@
(:require
[beicon.core :as rx]
[lentes.core :as l]
[cuerdas.core :as str]
[potok.core :as ptk]
[rumext.alpha :as mf]
[uxbox.main.data.workspace :as dw]
@ -21,16 +22,6 @@
[uxbox.util.dom :as dom]
[uxbox.util.geom.point :as gpt]))
;; --- Refs & Constants
(def ^:private +circle-props+
{:r 6
:style {:fillOpacity "1"
:strokeWidth "2px"
:vectorEffect "non-scaling-stroke"}
:fill "rgba(49,239,184,.7)"
:stroke "#31EFB8"})
;; --- Resize Implementation
(defn- start-resize
@ -75,6 +66,37 @@
(rx/of (dw/materialize-temporal-modifier-in-bulk ids)
::dw/page-data-update)))))))
(defn start-rotate
[shape]
(ptk/reify ::start-rotate
ptk/WatchEvent
(watch [_ state stream]
(let [shape (geom/shape->rect-shape shape)
stoper (rx/filter ms/mouse-up? stream)
center (gpt/point (+ (:x shape) (/ (:width shape) 2))
(+ (:y shape) (/ (:height shape) 2)))]
(rx/concat
(->> ms/mouse-position
;; (rx/map apply-zoom)
;; (rx/mapcat apply-grid-alignment)
(rx/with-latest vector ms/mouse-position-ctrl)
;; (rx/map normalize-proportion-lock)
;; (rx/mapcat (partial resize shape))
(rx/map (fn [[pos ctrl?]]
(let [angle (+ (gpt/angle pos center) 90)
angle (if (neg? angle)
(+ 360 angle)
angle)
modval (mod angle 90)
angle (if ctrl?
(if (< 50 modval)
(+ angle (- 90 modval))
(- angle modval))
angle)]
(dw/update-shape (:id shape) {:rotation angle}))))
(rx/take-until stoper)))))))
;; --- Controls (Component)
(def ^:private handler-size-threshold
@ -98,52 +120,73 @@
:cy cy}])
(mf/defc controls
[{:keys [shape zoom on-click] :as props}]
[{:keys [shape zoom on-resize on-rotate] :as props}]
(let [{:keys [x y width height]} shape
radius (if (> (max width height) handler-size-threshold) 6.0 4.0)]
[:g.controls
radius (if (> (max width height) handler-size-threshold) 6.0 4.0)
transform (str/format "rotate(%s %s %s)"
(:rotation shape 0)
(+ (:x shape) (/ (:width shape) 2))
(+ (:y shape) (/ (:height shape) 2)))]
[:g.controls #_{:transform transform}
[:rect.main {:x x :y y
:width width
:height height
:stroke-dasharray (str (/ 8.0 zoom) "," (/ 5 zoom))
:style {:stroke "#31EFB8" :fill "transparent"
:stroke-opacity "1"}}]
[:path {:stroke "#31EFB8"
:stroke-opacity "1"
:stroke-dasharray (str (/ 8.0 zoom) "," (/ 5 zoom))
:fill "transparent"
:d (str/format "M %s %s L %s %s"
(+ x (/ width 2))
y
(+ x (/ width 2))
(- y 30))}]
[:& control-item {:class "rotate"
:r (/ radius zoom)
:cx (+ x (/ width 2))
:on-click on-rotate
:cy (- y 30)}]
[:& control-item {:class "top"
:on-click #(on-click :top %)
:on-click #(on-resize :top %)
:r (/ radius zoom)
:cx (+ x (/ width 2))
:cy (- y 2)}]
[:& control-item {:on-click #(on-click :right %)
[:& control-item {:on-click #(on-resize :right %)
:r (/ radius zoom)
:cy (+ y (/ height 2))
:cx (+ x width 1)
:class "right"}]
[:& control-item {:on-click #(on-click :bottom %)
[:& control-item {:on-click #(on-resize :bottom %)
:r (/ radius zoom)
:cx (+ x (/ width 2))
:cy (+ y height 2)
:class "bottom"}]
[:& control-item {:on-click #(on-click :left %)
[:& control-item {:on-click #(on-resize :left %)
:r (/ radius zoom)
:cy (+ y (/ height 2))
:cx (- x 3)
:class "left"}]
[:& control-item {:on-click #(on-click :top-left %)
[:& control-item {:on-click #(on-resize :top-left %)
:r (/ radius zoom)
:cx x
:cy y
:class "top-left"}]
[:& control-item {:on-click #(on-click :top-right %)
[:& control-item {:on-click #(on-resize :top-right %)
:r (/ radius zoom)
:cx (+ x width)
:cy y
:class "top-right"}]
[:& control-item {:on-click #(on-click :bottom-left %)
[:& control-item {:on-click #(on-resize :bottom-left %)
:r (/ radius zoom)
:cx x
:cy (+ y height)
:class "bottom-left"}]
[:& control-item {:on-click #(on-click :bottom-right %)
[:& control-item {:on-click #(on-resize :bottom-right %)
:r (/ radius zoom)
:cx (+ x width)
:cy (+ y height)
@ -195,11 +238,11 @@
(map #(geom/selection-rect %))
(geom/shapes->rect-shape)
(geom/selection-rect))
on-click #(do (dom/stop-propagation %2)
on-resize #(do (dom/stop-propagation %2)
(st/emit! (start-resize %1 selected shape)))]
[:& controls {:shape shape
:zoom zoom
:on-click on-click}]))
:on-resize on-resize}]))
(mf/defc text-edition-selection-handlers
[{:keys [shape zoom] :as props}]
@ -216,10 +259,15 @@
(mf/defc single-selection-handlers
[{:keys [shape zoom] :as props}]
(let [on-click #(do (dom/stop-propagation %2)
(let [on-resize #(do (dom/stop-propagation %2)
(st/emit! (start-resize %1 #{(:id shape)} shape)))
on-rotate #(do (dom/stop-propagation %)
#_(st/emit! (start-rotate shape)))
shape (geom/selection-rect shape)]
[:& controls {:shape shape :zoom zoom :on-click on-click}]))
[:& controls {:shape shape
:zoom zoom
:on-rotate on-rotate
:on-resize on-resize}]))
(mf/defc selection-handlers
[{:keys [selected edition zoom] :as props}]

View file

@ -116,7 +116,7 @@
If the second vector is not provided, the angle
will be measured from x-axis."
([p]
{:pre [(point? p)]}
(-> (mth/atan2 (:y p) (:x p))
(mth/degrees)))
([p center]