mirror of
https://github.com/penpot/penpot.git
synced 2025-06-07 04:21:39 +02:00
⚡ Change resize to use DOM transformations
This commit is contained in:
parent
fa09fff2b5
commit
b2211aec59
38 changed files with 839 additions and 717 deletions
|
@ -6,9 +6,6 @@
|
|||
|
||||
(ns app.main.ui.shapes.bool
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.geom.shapes.path :as gsp]
|
||||
[app.common.path.bool :as pb]
|
||||
[app.common.path.shapes-to-path :as stp]
|
||||
[app.main.ui.hooks :refer [use-equal-memo]]
|
||||
|
@ -17,87 +14,26 @@
|
|||
[app.util.object :as obj]
|
||||
[rumext.alpha :as mf]))
|
||||
|
||||
(mf/defc debug-bool
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
|
||||
(let [frame (obj/get props "frame")
|
||||
shape (obj/get props "shape")
|
||||
childs (obj/get props "childs")
|
||||
|
||||
[content-a content-b]
|
||||
(mf/use-memo
|
||||
(mf/deps shape childs)
|
||||
(fn []
|
||||
(let [childs (d/mapm #(-> %2 (gsh/translate-to-frame frame) gsh/transform-shape) childs)
|
||||
[content-a content-b]
|
||||
(->> (:shapes shape)
|
||||
(map #(get childs %))
|
||||
(filter #(not (:hidden %)))
|
||||
(map #(stp/convert-to-path % childs))
|
||||
(map :content)
|
||||
(map pb/close-paths)
|
||||
(map pb/add-previous))]
|
||||
(pb/content-intersect-split content-a content-b))))]
|
||||
[:g.debug-bool
|
||||
[:g.shape-a
|
||||
[:& path-shape {:shape (-> shape
|
||||
(assoc :type :path)
|
||||
(assoc :stroke-color "blue")
|
||||
(assoc :stroke-opacity 1)
|
||||
(assoc :stroke-width 1)
|
||||
(assoc :stroke-style :solid)
|
||||
(dissoc :fill-color :fill-opacity)
|
||||
(assoc :content content-b))
|
||||
:frame frame}]
|
||||
(for [{:keys [x y]} (gsp/content->points (pb/close-paths content-b))]
|
||||
[:circle {:cx x
|
||||
:cy y
|
||||
:r 2.5
|
||||
:style {:fill "blue"}}])]
|
||||
|
||||
[:g.shape-b
|
||||
[:& path-shape {:shape (-> shape
|
||||
(assoc :type :path)
|
||||
(assoc :stroke-color "red")
|
||||
(assoc :stroke-opacity 1)
|
||||
(assoc :stroke-width 0.5)
|
||||
(assoc :stroke-style :solid)
|
||||
(dissoc :fill-color :fill-opacity)
|
||||
(assoc :content content-a))
|
||||
:frame frame}]
|
||||
(for [{:keys [x y]} (gsp/content->points (pb/close-paths content-a))]
|
||||
[:circle {:cx x
|
||||
:cy y
|
||||
:r 1.25
|
||||
:style {:fill "red"}}])]])
|
||||
)
|
||||
|
||||
|
||||
(defn bool-shape
|
||||
[shape-wrapper]
|
||||
(mf/fnc bool-shape
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [frame (obj/get props "frame")
|
||||
shape (obj/get props "shape")
|
||||
(let [shape (obj/get props "shape")
|
||||
childs (obj/get props "childs")
|
||||
|
||||
childs (use-equal-memo childs)
|
||||
|
||||
include-metadata? (mf/use-ctx use/include-metadata-ctx)
|
||||
|
||||
bool-content
|
||||
(mf/use-memo
|
||||
(mf/deps shape childs)
|
||||
(fn []
|
||||
(let [childs (d/mapm #(-> %2 gsh/transform-shape (gsh/translate-to-frame frame)) childs)]
|
||||
(->> (:shapes shape)
|
||||
(map #(get childs %))
|
||||
(filter #(not (:hidden %)))
|
||||
(map #(stp/convert-to-path % childs))
|
||||
(mapv :content)
|
||||
(pb/content-bool (:bool-type shape))))))]
|
||||
(->> (:shapes shape)
|
||||
(map #(get childs %))
|
||||
(filter #(not (:hidden %)))
|
||||
(map #(stp/convert-to-path % childs))
|
||||
(mapv :content)
|
||||
(pb/content-bool (:bool-type shape)))))]
|
||||
|
||||
[:*
|
||||
[:& path-shape {:shape (assoc shape :content bool-content)}]
|
||||
|
@ -105,10 +41,5 @@
|
|||
(when include-metadata?
|
||||
[:> "penpot:bool" {}
|
||||
(for [item (->> (:shapes shape) (mapv #(get childs %)))]
|
||||
[:& shape-wrapper {:frame frame
|
||||
:shape item
|
||||
:key (:id item)}])])
|
||||
|
||||
#_[:& debug-bool {:frame frame
|
||||
:shape shape
|
||||
:childs childs}]])))
|
||||
[:& shape-wrapper {:shape item
|
||||
:key (:id item)}])])])))
|
||||
|
|
|
@ -25,7 +25,12 @@
|
|||
(let [;; When not active the embedding we return the URI
|
||||
url-mapping (fn [obs]
|
||||
(if embed?
|
||||
(rx/merge-map http/fetch-data-uri obs)
|
||||
(->> obs
|
||||
(rx/merge-map
|
||||
(fn [uri]
|
||||
(->> (http/fetch-data-uri uri true)
|
||||
;; If fetching give an error we store the URI as its `data-uri`
|
||||
(rx/catch #(rx/of (hash-map uri uri)))))))
|
||||
(rx/map identity obs)))
|
||||
|
||||
sub (->> (rx/from urls)
|
||||
|
|
|
@ -113,7 +113,7 @@
|
|||
filter-x (min x (+ x offset-x (- spread) (- blur) -5))
|
||||
filter-y (min y (+ y offset-y (- spread) (- blur) -5))
|
||||
filter-width (+ width (mth/abs offset-x) (* spread 2) (* blur 2) 10)
|
||||
filter-height (+ height (mth/abs offset-x) (* spread 2) (* blur 2) 10)]
|
||||
filter-height (+ height (mth/abs offset-y) (* spread 2) (* blur 2) 10)]
|
||||
{:x1 filter-x
|
||||
:y1 filter-y
|
||||
:x2 (+ filter-x filter-width)
|
||||
|
@ -208,26 +208,31 @@
|
|||
margin (gsh/shape-stroke-margin shape stroke-width)]
|
||||
(+ stroke-width margin)))
|
||||
|
||||
(defn change-filter-in
|
||||
"Adds the previous filter as `filter-in` parameter"
|
||||
[filters]
|
||||
(map #(assoc %1 :filter-in %2) filters (cons nil (map :id filters))))
|
||||
|
||||
(mf/defc filters
|
||||
[{:keys [filter-id shape]}]
|
||||
|
||||
(let [filters (shape->filters shape)
|
||||
|
||||
;; Adds the previous filter as `filter-in` parameter
|
||||
filters (map #(assoc %1 :filter-in %2) filters (cons nil (map :id filters)))
|
||||
bounds (get-filters-bounds shape filters (or (-> shape :blur :value) 0))
|
||||
padding (calculate-padding shape)]
|
||||
|
||||
(let [filters (-> shape shape->filters change-filter-in)
|
||||
bounds (get-filters-bounds shape filters (or (-> shape :blur :value) 0))
|
||||
padding (calculate-padding shape)
|
||||
selrect (:selrect shape)
|
||||
filter-x (/ (- (:x bounds) (:x selrect) padding) (:width selrect))
|
||||
filter-y (/ (- (:y bounds) (:y selrect) padding) (:height selrect))
|
||||
filter-width (/ (+ (:width bounds) (* 2 padding)) (:width selrect))
|
||||
filter-height (/ (+ (:height bounds) (* 2 padding)) (:height selrect))]
|
||||
[:*
|
||||
(when (> (count filters) 2)
|
||||
[:filter {:id filter-id
|
||||
:x (- (:x bounds) padding)
|
||||
:y (- (:y bounds) padding)
|
||||
:width (+ (:width bounds) (* 2 padding))
|
||||
:height (+ (:height bounds) (* 2 padding))
|
||||
:filterUnits "userSpaceOnUse"
|
||||
[:filter {:id filter-id
|
||||
:x filter-x
|
||||
:y filter-y
|
||||
:width filter-width
|
||||
:height filter-height
|
||||
:filterUnits "objectBoundingBox"
|
||||
:color-interpolation-filters "sRGB"}
|
||||
|
||||
(for [entry filters]
|
||||
[:& filter-entry {:entry entry}])])]))
|
||||
|
||||
|
|
|
@ -10,6 +10,22 @@
|
|||
[app.util.object :as obj]
|
||||
[rumext.alpha :as mf]))
|
||||
|
||||
(defn frame-clip-id
|
||||
[shape render-id]
|
||||
(str "frame-clip-" (:id shape) "-" render-id))
|
||||
|
||||
(defn frame-clip-url
|
||||
[shape render-id]
|
||||
(when (= :frame (:type shape))
|
||||
(str "url(#" (frame-clip-id shape render-id) ")")))
|
||||
|
||||
(mf/defc frame-clip-def
|
||||
[{:keys [shape render-id]}]
|
||||
(when (= :frame (:type shape))
|
||||
(let [{:keys [x y width height]} shape]
|
||||
[:clipPath {:id (frame-clip-id shape render-id) :class "frame-clip"}
|
||||
[:rect {:x x :y y :width width :height height}]])))
|
||||
|
||||
(defn frame-shape
|
||||
[shape-wrapper]
|
||||
(mf/fnc frame-shape
|
||||
|
@ -17,7 +33,7 @@
|
|||
[props]
|
||||
(let [childs (unchecked-get props "childs")
|
||||
shape (unchecked-get props "shape")
|
||||
{:keys [width height]} shape
|
||||
{:keys [x y width height]} shape
|
||||
|
||||
has-background? (or (some? (:fill-color shape))
|
||||
(some? (:fill-color-gradient shape)))
|
||||
|
@ -25,8 +41,8 @@
|
|||
|
||||
props (-> (attrs/extract-style-attrs shape)
|
||||
(obj/merge!
|
||||
#js {:x 0
|
||||
:y 0
|
||||
#js {:x x
|
||||
:y y
|
||||
:width width
|
||||
:height height
|
||||
:className "frame-background"}))]
|
||||
|
@ -34,7 +50,6 @@
|
|||
(when (or has-background? has-stroke?)
|
||||
[:> :rect props])
|
||||
(for [item childs]
|
||||
[:& shape-wrapper {:frame shape
|
||||
:shape item
|
||||
[:& shape-wrapper {:shape item
|
||||
:key (:id item)}])])))
|
||||
|
||||
|
|
|
@ -17,8 +17,7 @@
|
|||
(mf/fnc group-shape
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [frame (unchecked-get props "frame")
|
||||
shape (unchecked-get props "shape")
|
||||
(let [shape (unchecked-get props "shape")
|
||||
childs (unchecked-get props "childs")
|
||||
render-id (mf/use-ctx muc/render-ctx)
|
||||
masked-group? (:masked-group? shape)
|
||||
|
@ -46,11 +45,10 @@
|
|||
[:> clip-wrapper clip-props
|
||||
[:> mask-wrapper mask-props
|
||||
(when masked-group?
|
||||
[:> render-mask #js {:frame frame :mask mask}])
|
||||
[:> render-mask #js {:mask mask}])
|
||||
|
||||
(for [item childs]
|
||||
[:& shape-wrapper {:frame frame
|
||||
:shape item
|
||||
[:& shape-wrapper {:shape item
|
||||
:key (:id item)}])]]))))
|
||||
|
||||
|
||||
|
|
|
@ -34,13 +34,9 @@
|
|||
(mf/fnc mask-shape
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [frame (unchecked-get props "frame")
|
||||
mask (unchecked-get props "mask")
|
||||
(let [mask (unchecked-get props "mask")
|
||||
render-id (mf/use-ctx muc/render-ctx)
|
||||
|
||||
mask' (-> mask
|
||||
(gsh/transform-shape)
|
||||
(gsh/translate-to-frame frame))]
|
||||
mask' (gsh/transform-shape mask)]
|
||||
[:defs
|
||||
[:filter {:id (filter-id render-id mask)}
|
||||
[:feFlood {:flood-color "white"
|
||||
|
@ -52,13 +48,13 @@
|
|||
;; Clip path is necessary so the elements inside the mask won't affect
|
||||
;; the events outside. Clip hides the elements but mask doesn't (like display vs visibility)
|
||||
;; we cannot use clips instead of mask because clips can only be simple shapes
|
||||
[:clipPath {:id (clip-id render-id mask)}
|
||||
[:clipPath {:class "mask-clip-path"
|
||||
:id (clip-id render-id mask)}
|
||||
[:polyline {:points (->> (:points mask')
|
||||
(map #(str (:x %) "," (:y %)))
|
||||
(str/join " "))}]]
|
||||
[:mask {:id (mask-id render-id mask)}
|
||||
[:mask {:class "mask-shape"
|
||||
:id (mask-id render-id mask)}
|
||||
[:g {:filter (filter-url render-id mask)}
|
||||
[:& shape-wrapper {:frame frame
|
||||
:shape (-> mask
|
||||
(dissoc :shadow :blur))}]]]])))
|
||||
[:& shape-wrapper {:shape (dissoc mask :shadow :blur)}]]]])))
|
||||
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
[app.main.ui.shapes.export :as ed]
|
||||
[app.main.ui.shapes.fill-image :as fim]
|
||||
[app.main.ui.shapes.filters :as filters]
|
||||
[app.main.ui.shapes.frame :as frame]
|
||||
[app.main.ui.shapes.gradients :as grad]
|
||||
[app.main.ui.shapes.svg-defs :as defs]
|
||||
[app.util.object :as obj]
|
||||
|
@ -26,6 +27,8 @@
|
|||
(let [shape (obj/get props "shape")
|
||||
children (obj/get props "children")
|
||||
pointer-events (obj/get props "pointer-events")
|
||||
|
||||
type (:type shape)
|
||||
render-id (mf/use-memo #(str (uuid/next)))
|
||||
filter-id (str "filter_" render-id)
|
||||
styles (-> (obj/new)
|
||||
|
@ -34,10 +37,6 @@
|
|||
(cond-> (and (:blend-mode shape) (not= (:blend-mode shape) :normal))
|
||||
(obj/set! "mixBlendMode" (d/name (:blend-mode shape)))))
|
||||
|
||||
{:keys [x y width height type]} shape
|
||||
frame? (= :frame type)
|
||||
group? (= :group type)
|
||||
|
||||
include-metadata? (mf/use-ctx ed/include-metadata-ctx)
|
||||
|
||||
wrapper-props
|
||||
|
@ -50,26 +49,14 @@
|
|||
|
||||
wrapper-props
|
||||
(cond-> wrapper-props
|
||||
frame?
|
||||
(-> (obj/set! "x" x)
|
||||
(obj/set! "y" y)
|
||||
(obj/set! "width" width)
|
||||
(obj/set! "height" height)
|
||||
(obj/set! "xmlns" "http://www.w3.org/2000/svg")
|
||||
(obj/set! "xmlnsXlink" "http://www.w3.org/1999/xlink")
|
||||
(cond->
|
||||
include-metadata?
|
||||
(obj/set! "xmlns:penpot" "https://penpot.app/xmlns"))))
|
||||
(= :frame type)
|
||||
(obj/set! "clipPath" (frame/frame-clip-url shape render-id))
|
||||
|
||||
wrapper-props
|
||||
(cond-> wrapper-props
|
||||
group?
|
||||
(attrs/add-style-attrs shape))
|
||||
|
||||
wrapper-tag (if frame? "svg" "g")]
|
||||
(= :group type)
|
||||
(attrs/add-style-attrs shape))]
|
||||
|
||||
[:& (mf/provider muc/render-ctx) {:value render-id}
|
||||
[:> wrapper-tag wrapper-props
|
||||
[:> :g wrapper-props
|
||||
(when include-metadata?
|
||||
[:& ed/export-data {:shape shape}])
|
||||
|
||||
|
@ -79,5 +66,6 @@
|
|||
[:& grad/gradient {:shape shape :attr :fill-color-gradient}]
|
||||
[:& grad/gradient {:shape shape :attr :stroke-color-gradient}]
|
||||
[:& fim/fill-image-pattern {:shape shape :render-id render-id}]
|
||||
[:& cs/stroke-defs {:shape shape :render-id render-id}]]
|
||||
[:& cs/stroke-defs {:shape shape :render-id render-id}]
|
||||
[:& frame/frame-clip-def {:shape shape :render-id render-id}]]
|
||||
children]]))
|
||||
|
|
|
@ -88,8 +88,7 @@
|
|||
{::mf/wrap-props false}
|
||||
[props]
|
||||
|
||||
(let [frame (unchecked-get props "frame")
|
||||
shape (unchecked-get props "shape")
|
||||
(let [shape (unchecked-get props "shape")
|
||||
childs (unchecked-get props "childs")
|
||||
|
||||
{:keys [content]} shape
|
||||
|
@ -103,12 +102,12 @@
|
|||
svg-root?
|
||||
[:& svg-root {:shape shape}
|
||||
(for [item childs]
|
||||
[:& shape-wrapper {:frame frame :shape item :key (:id item)}])]
|
||||
[:& shape-wrapper {:shape item :key (:id item)}])]
|
||||
|
||||
svg-tag?
|
||||
[:& svg-element {:shape shape}
|
||||
(for [item childs]
|
||||
[:& shape-wrapper {:frame frame :shape item :key (:id item)}])]
|
||||
[:& shape-wrapper {:shape item :key (:id item)}])]
|
||||
|
||||
svg-leaf?
|
||||
content
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue