mirror of
https://github.com/penpot/penpot.git
synced 2025-06-06 03:11:39 +02:00
🐛 Fixes problems with masks rotation and some clipping problems
This commit is contained in:
parent
34af5e4563
commit
3a8a212432
10 changed files with 95 additions and 104 deletions
|
@ -1146,8 +1146,11 @@
|
|||
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(rx/of (dws/deselect-all)
|
||||
(dws/select-shape (:id shape))))))
|
||||
(let [selected (get-in state [:workspace-local :selected])]
|
||||
(if (selected (:id shape))
|
||||
(rx/empty)
|
||||
(rx/of (dws/deselect-all)
|
||||
(dws/select-shape (:id shape))))))))
|
||||
|
||||
(def hide-context-menu
|
||||
(ptk/reify ::hide-context-menu
|
||||
|
@ -1478,14 +1481,20 @@
|
|||
:id (:id group)
|
||||
:operations [{:type :set
|
||||
:attr :masked-group?
|
||||
:val nil}]}]
|
||||
:val nil}]}
|
||||
{:type :reg-objects
|
||||
:page-id page-id
|
||||
:shapes [(:id group)]}]
|
||||
|
||||
uchanges [{:type :mod-obj
|
||||
:page-id page-id
|
||||
:id (:id group)
|
||||
:operations [{:type :set
|
||||
:attr :masked-group?
|
||||
:val (:masked-group? group)}]}]]
|
||||
:val (:masked-group? group)}]}
|
||||
{:type :reg-objects
|
||||
:page-id page-id
|
||||
:shapes [(:id group)]}]]
|
||||
|
||||
(rx/of (dwc/commit-changes rchanges uchanges {:commit-local? true})
|
||||
(dwc/select-shapes (d/ordered-set (:id group))))))))))
|
||||
|
|
|
@ -77,7 +77,10 @@
|
|||
(fn []
|
||||
(let [keys [(events/listen js/window EventType.POPSTATE on-pop-state)
|
||||
(events/listen js/document EventType.KEYDOWN handle-keydown)
|
||||
(events/listen js/document EventType.CLICK handle-click-outside)
|
||||
|
||||
;; Changing to js/document breaks the color picker
|
||||
(events/listen (dom/get-root) EventType.CLICK handle-click-outside)
|
||||
|
||||
(events/listen js/document EventType.CONTEXTMENU handle-click-outside)]]
|
||||
#(doseq [key keys]
|
||||
(events/unlistenByKey key)))))
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
[rumext.alpha :as mf]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.common.geom.shapes :as geom]
|
||||
[app.main.ui.shapes.group :refer [mask-id-ctx]]
|
||||
[app.util.object :as obj]))
|
||||
|
||||
; The SVG standard does not implement yet the 'stroke-alignment'
|
||||
|
@ -25,16 +24,13 @@
|
|||
elem-name (unchecked-get props "elem-name")
|
||||
;; {:keys [x y width height]} (geom/shape->rect-shape shape)
|
||||
{:keys [x y width height]} (:selrect shape)
|
||||
mask-id (mf/use-ctx mask-id-ctx)
|
||||
stroke-id (mf/use-var (uuid/next))
|
||||
stroke-style (:stroke-style shape :none)
|
||||
stroke-position (:stroke-alignment shape :center)]
|
||||
(cond
|
||||
;; Center alignment (or no stroke): the default in SVG
|
||||
(or (= stroke-style :none) (= stroke-position :center))
|
||||
[:> elem-name (cond-> (obj/merge! #js {} base-props)
|
||||
(some? mask-id)
|
||||
(obj/merge! #js {:mask mask-id}))]
|
||||
[:> elem-name (obj/merge! #js {} base-props)]
|
||||
|
||||
;; Inner alignment: display the shape with double width stroke,
|
||||
;; and clip the result with the original shape without stroke.
|
||||
|
@ -54,15 +50,10 @@
|
|||
shape-props (-> (obj/merge! #js {} base-props)
|
||||
(obj/merge! #js {:strokeWidth (* stroke-width 2)
|
||||
:clipPath (str "url('#" clip-id "')")}))]
|
||||
(if (nil? mask-id)
|
||||
[:*
|
||||
[:> "clipPath" #js {:id clip-id}
|
||||
[:> elem-name clip-props]]
|
||||
[:> elem-name shape-props]]
|
||||
[:g {:mask mask-id}
|
||||
[:> "clipPath" #js {:id clip-id}
|
||||
[:> elem-name clip-props]]
|
||||
[:> elem-name shape-props]]))
|
||||
[:*
|
||||
[:> "clipPath" #js {:id clip-id}
|
||||
[:> elem-name clip-props]]
|
||||
[:> elem-name shape-props]])
|
||||
|
||||
;; Outer alingmnent: display the shape in two layers. One
|
||||
;; without stroke (only fill), and another one only with stroke
|
||||
|
@ -100,17 +91,10 @@
|
|||
:fill "none"
|
||||
:fillOpacity 0
|
||||
:mask (str "url('#" stroke-mask-id "')")}))]
|
||||
(if (nil? mask-id)
|
||||
[:*
|
||||
[:mask {:id mask-id}
|
||||
[:> elem-name mask-props1]
|
||||
[:> elem-name mask-props2]]
|
||||
[:> elem-name shape-props1]
|
||||
[:> elem-name shape-props2]]
|
||||
[:g {:mask mask-id}
|
||||
[:mask {:id stroke-mask-id}
|
||||
[:> elem-name mask-props1]
|
||||
[:> elem-name mask-props2]]
|
||||
[:> elem-name shape-props1]
|
||||
[:> elem-name shape-props2]])))))
|
||||
[:*
|
||||
[:mask {:id stroke-mask-id}
|
||||
[:> elem-name mask-props1]
|
||||
[:> elem-name mask-props2]]
|
||||
[:> elem-name shape-props1]
|
||||
[:> elem-name shape-props2]]))))
|
||||
|
||||
|
|
|
@ -18,56 +18,54 @@
|
|||
|
||||
(mf/defc linear-gradient [{:keys [id gradient shape]}]
|
||||
(let [{:keys [x y width height]} shape]
|
||||
[:defs
|
||||
[:linearGradient {:id id
|
||||
:x1 (:start-x gradient)
|
||||
:y1 (:start-y gradient)
|
||||
:x2 (:end-x gradient)
|
||||
:y2 (:end-y gradient)}
|
||||
(for [{:keys [offset color opacity]} (:stops gradient)]
|
||||
[:stop {:key (str id "-stop-" offset)
|
||||
:offset (or offset 0)
|
||||
:stop-color color
|
||||
:stop-opacity opacity}])]]))
|
||||
[:linearGradient {:id id
|
||||
:x1 (:start-x gradient)
|
||||
:y1 (:start-y gradient)
|
||||
:x2 (:end-x gradient)
|
||||
:y2 (:end-y gradient)}
|
||||
(for [{:keys [offset color opacity]} (:stops gradient)]
|
||||
[:stop {:key (str id "-stop-" offset)
|
||||
:offset (or offset 0)
|
||||
:stop-color color
|
||||
:stop-opacity opacity}])]))
|
||||
|
||||
(mf/defc radial-gradient [{:keys [id gradient shape]}]
|
||||
(let [{:keys [x y width height]} shape]
|
||||
[:defs
|
||||
(let [[x y] (if (= (:type shape) :frame) [0 0] [x y])
|
||||
translate-vec (gpt/point (+ x (* width (:start-x gradient)))
|
||||
(+ y (* height (:start-y gradient))))
|
||||
|
||||
gradient-vec (gpt/to-vec (gpt/point (* width (:start-x gradient))
|
||||
(* height (:start-y gradient)))
|
||||
(gpt/point (* width (:end-x gradient))
|
||||
(* height (:end-y gradient))))
|
||||
(let [[x y] (if (= (:type shape) :frame) [0 0] [x y])
|
||||
translate-vec (gpt/point (+ x (* width (:start-x gradient)))
|
||||
(+ y (* height (:start-y gradient))))
|
||||
|
||||
angle (gpt/angle gradient-vec
|
||||
(gpt/point 1 0))
|
||||
gradient-vec (gpt/to-vec (gpt/point (* width (:start-x gradient))
|
||||
(* height (:start-y gradient)))
|
||||
(gpt/point (* width (:end-x gradient))
|
||||
(* height (:end-y gradient))))
|
||||
|
||||
shape-height-vec (gpt/point 0 (/ height 2))
|
||||
angle (gpt/angle gradient-vec
|
||||
(gpt/point 1 0))
|
||||
|
||||
scale-factor-y (/ (gpt/length gradient-vec) (/ height 2))
|
||||
scale-factor-x (* scale-factor-y (:width gradient))
|
||||
shape-height-vec (gpt/point 0 (/ height 2))
|
||||
|
||||
scale-vec (gpt/point (* scale-factor-y (/ height 2))
|
||||
(* scale-factor-x (/ width 2)))
|
||||
scale-factor-y (/ (gpt/length gradient-vec) (/ height 2))
|
||||
scale-factor-x (* scale-factor-y (:width gradient))
|
||||
|
||||
tr-translate (str/fmt "translate(%s, %s)" (:x translate-vec) (:y translate-vec))
|
||||
tr-rotate (str/fmt "rotate(%s)" angle)
|
||||
tr-scale (str/fmt "scale(%s, %s)" (:x scale-vec) (:y scale-vec))
|
||||
transform (str/fmt "%s %s %s" tr-translate tr-rotate tr-scale)]
|
||||
[:radialGradient {:id id
|
||||
:cx 0
|
||||
:cy 0
|
||||
:r 1
|
||||
:gradientUnits "userSpaceOnUse"
|
||||
:gradientTransform transform}
|
||||
(for [{:keys [offset color opacity]} (:stops gradient)]
|
||||
[:stop {:key (str id "-stop-" offset)
|
||||
:offset (or offset 0)
|
||||
:stop-color color
|
||||
:stop-opacity opacity}])])]))
|
||||
scale-vec (gpt/point (* scale-factor-y (/ height 2))
|
||||
(* scale-factor-x (/ width 2)))
|
||||
|
||||
tr-translate (str/fmt "translate(%s, %s)" (:x translate-vec) (:y translate-vec))
|
||||
tr-rotate (str/fmt "rotate(%s)" angle)
|
||||
tr-scale (str/fmt "scale(%s, %s)" (:x scale-vec) (:y scale-vec))
|
||||
transform (str/fmt "%s %s %s" tr-translate tr-rotate tr-scale)]
|
||||
[:radialGradient {:id id
|
||||
:cx 0
|
||||
:cy 0
|
||||
:r 1
|
||||
:gradientUnits "userSpaceOnUse"
|
||||
:gradientTransform transform}
|
||||
(for [{:keys [offset color opacity]} (:stops gradient)]
|
||||
[:stop {:key (str id "-stop-" offset)
|
||||
:offset (or offset 0)
|
||||
:stop-color color
|
||||
:stop-opacity opacity}])])))
|
||||
|
||||
(mf/defc gradient
|
||||
{::mf/wrap-props false}
|
||||
|
|
|
@ -14,17 +14,16 @@
|
|||
[app.main.ui.shapes.attrs :as attrs]
|
||||
[app.common.geom.shapes :as geom]))
|
||||
|
||||
(def mask-id-ctx (mf/create-context nil))
|
||||
|
||||
(defn group-shape
|
||||
[shape-wrapper]
|
||||
(mf/fnc group-shape
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [frame (unchecked-get props "frame")
|
||||
shape (unchecked-get props "shape")
|
||||
childs (unchecked-get props "childs")
|
||||
expand-mask (unchecked-get props "expand-mask")
|
||||
(let [frame (unchecked-get props "frame")
|
||||
shape (unchecked-get props "shape")
|
||||
childs (unchecked-get props "childs")
|
||||
expand-mask (unchecked-get props "expand-mask")
|
||||
pointer-events (unchecked-get props "pointer-events")
|
||||
mask (if (and (:masked-group? shape) (not expand-mask))
|
||||
(first childs)
|
||||
nil)
|
||||
|
@ -33,7 +32,9 @@
|
|||
childs)
|
||||
{:keys [id x y width height]} shape
|
||||
transform (geom/transform-matrix shape)]
|
||||
[:g
|
||||
[:g.group {:pointer-events pointer-events
|
||||
:mask (when (and mask (not expand-mask))
|
||||
(str/fmt "url(#%s)" (:id mask)))}
|
||||
(when mask
|
||||
[:defs
|
||||
[:mask {:id (:id mask)
|
||||
|
@ -41,11 +42,10 @@
|
|||
:height height}
|
||||
[:& shape-wrapper {:frame frame
|
||||
:shape mask}]]])
|
||||
[:& (mf/provider mask-id-ctx) {:value (str/fmt "url(#%s)" (:id mask))}
|
||||
(for [item childs]
|
||||
[:& shape-wrapper {:frame frame
|
||||
:shape item
|
||||
:key (:id item)}])]
|
||||
])))
|
||||
(for [item childs]
|
||||
[:& shape-wrapper {:frame frame
|
||||
:shape item
|
||||
:key (:id item)}])])))
|
||||
|
||||
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
[app.config :as cfg]
|
||||
[app.common.geom.shapes :as geom]
|
||||
[app.main.ui.shapes.attrs :as attrs]
|
||||
[app.main.ui.shapes.group :refer [mask-id-ctx]]
|
||||
[app.util.object :as obj]
|
||||
[app.main.ui.context :as muc]
|
||||
[app.main.data.fetch :as df]
|
||||
|
@ -27,7 +26,6 @@
|
|||
{:keys [id x y width height rotation metadata]} shape
|
||||
uri (cfg/resolve-media-path (:path metadata))
|
||||
embed-resources? (mf/use-ctx muc/embed-ctx)
|
||||
mask-id (mf/use-ctx mask-id-ctx)
|
||||
data-uri (mf/use-state (when (not embed-resources?) uri))]
|
||||
|
||||
(mf/use-effect
|
||||
|
@ -45,8 +43,7 @@
|
|||
:transform transform
|
||||
:width width
|
||||
:height height
|
||||
:preserveAspectRatio "none"
|
||||
:mask mask-id}))]
|
||||
:preserveAspectRatio "none"}))]
|
||||
(if (nil? @data-uri)
|
||||
[:> "rect" (obj/merge!
|
||||
props
|
||||
|
|
|
@ -13,7 +13,6 @@
|
|||
[rumext.alpha :as mf]
|
||||
[app.main.ui.shapes.attrs :as attrs]
|
||||
[app.main.ui.shapes.custom-stroke :refer [shape-custom-stroke]]
|
||||
[app.main.ui.shapes.group :refer [mask-id-ctx]]
|
||||
[app.common.geom.shapes :as geom]
|
||||
[app.util.object :as obj]
|
||||
[app.util.geom.path :as ugp]))
|
||||
|
@ -27,7 +26,6 @@
|
|||
background? (unchecked-get props "background?")
|
||||
;; {:keys [id x y width height]} (geom/shape->rect-shape shape)
|
||||
{:keys [id x y width height]} (:selrect shape)
|
||||
mask-id (mf/use-ctx mask-id-ctx)
|
||||
transform (geom/transform-matrix shape)
|
||||
pdata (ugp/content->path (:content shape))
|
||||
props (-> (attrs/extract-style-attrs shape)
|
||||
|
@ -35,7 +33,7 @@
|
|||
#js {:transform transform
|
||||
:d pdata}))]
|
||||
(if background?
|
||||
[:g {:mask mask-id}
|
||||
[:g
|
||||
[:path {:stroke "transparent"
|
||||
:fill "transparent"
|
||||
:stroke-width "20px"
|
||||
|
@ -45,6 +43,5 @@
|
|||
:elem-name "path"}]]
|
||||
[:& shape-custom-stroke {:shape shape
|
||||
:base-props props
|
||||
:mask mask-id
|
||||
:elem-name "path"}])))
|
||||
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
[cuerdas.core :as str]
|
||||
[rumext.alpha :as mf]
|
||||
[app.main.ui.context :as muc]
|
||||
[app.main.ui.shapes.group :refer [mask-id-ctx]]
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.shapes :as geom]
|
||||
[app.common.geom.matrix :as gmt]
|
||||
|
@ -90,7 +89,6 @@
|
|||
(let [shape (unchecked-get props "shape")
|
||||
selected? (unchecked-get props "selected?")
|
||||
grow-type (:grow-type shape)
|
||||
mask-id (mf/use-ctx mask-id-ctx)
|
||||
{:keys [id x y width height content]} shape]
|
||||
[:foreignObject {:x x
|
||||
:y y
|
||||
|
@ -99,7 +97,6 @@
|
|||
:transform (geom/transform-matrix shape)
|
||||
:width (if (#{:auto-width} grow-type) 10000 width)
|
||||
:height (if (#{:auto-height :auto-width} grow-type) 10000 height)
|
||||
:mask mask-id
|
||||
:ref ref
|
||||
:pointer-events "none"}
|
||||
[:& text-content {:shape shape
|
||||
|
|
|
@ -80,7 +80,8 @@
|
|||
{:frame frame
|
||||
:shape shape
|
||||
:childs childs
|
||||
:expand-mask is-mask-selected?}]
|
||||
:expand-mask is-mask-selected?
|
||||
:pointer-events (when (not is-child-selected?) "none")}]
|
||||
|
||||
(when-not is-child-selected?
|
||||
[:rect.group-actions
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue