mirror of
https://github.com/penpot/penpot.git
synced 2025-04-15 08:21:40 -05:00
♻️ Refactor custom stroke
This commit is contained in:
parent
bf5f845789
commit
6cbbfa6499
7 changed files with 166 additions and 137 deletions
|
@ -32,6 +32,5 @@
|
|||
:ry ry
|
||||
:transform transform}))]
|
||||
|
||||
[:& shape-custom-stroke {:shape shape
|
||||
:base-props props
|
||||
:elem-name "ellipse"}]))
|
||||
[:& shape-custom-stroke {:shape shape}
|
||||
[:> :ellipse props]]))
|
||||
|
|
|
@ -9,7 +9,116 @@
|
|||
[rumext.alpha :as mf]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.common.geom.shapes :as geom]
|
||||
[app.util.object :as obj]))
|
||||
[app.util.object :as obj]
|
||||
[app.main.ui.context :as muc]))
|
||||
|
||||
(defn add-props
|
||||
[props new-props]
|
||||
(-> props
|
||||
(obj/merge (clj->js new-props))))
|
||||
|
||||
(defn add-style
|
||||
[props new-style]
|
||||
(let [old-style (obj/get props "style")
|
||||
style (obj/merge old-style (clj->js new-style))]
|
||||
(-> props (obj/merge #js {:style style}))))
|
||||
|
||||
(mf/defc inner-stroke-clip-path
|
||||
[{:keys [shape render-id]}]
|
||||
(let [clip-id (str "inner-stroke-" render-id)
|
||||
shape-id (str "stroke-shape-" render-id)]
|
||||
[:> "clipPath" #js {:id clip-id}
|
||||
[:use {:href (str "#" shape-id)}]]))
|
||||
|
||||
(mf/defc outer-stroke-mask
|
||||
[{:keys [shape render-id]}]
|
||||
(let [stroke-mask-id (str "outer-stroke-" render-id)
|
||||
shape-id (str "stroke-shape-" render-id)
|
||||
stroke-width (:stroke-width shape 0)]
|
||||
[:mask {:id stroke-mask-id}
|
||||
[:use {:href (str "#" shape-id)
|
||||
:style #js {:fill "none" :stroke "white" :strokeWidth (* stroke-width 2)}}]
|
||||
|
||||
[:use {:href (str "#" shape-id)
|
||||
:style #js {:fill "black"}}]]))
|
||||
|
||||
(mf/defc stroke-defs
|
||||
[{:keys [shape render-id]}]
|
||||
(cond
|
||||
(and (= :inner (:stroke-alignment shape :center))
|
||||
(> (:stroke-width shape 0) 0))
|
||||
[:& inner-stroke-clip-path {:shape shape
|
||||
:render-id render-id}]
|
||||
|
||||
(and (= :outer (:stroke-alignment shape :center))
|
||||
(> (:stroke-width shape 0) 0))
|
||||
[:& outer-stroke-mask {:shape shape
|
||||
:render-id render-id}]))
|
||||
|
||||
;; Outer alingmnent: display the shape in two layers. One
|
||||
;; without stroke (only fill), and another one only with stroke
|
||||
;; at double width (transparent fill) and passed through a mask
|
||||
;; that shows the whole shape, but hides the original shape
|
||||
;; without stroke
|
||||
(mf/defc outer-stroke
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
|
||||
(let [render-id (mf/use-ctx muc/render-ctx)
|
||||
child (obj/get props "children")
|
||||
base-props (obj/get child "props")
|
||||
elem-name (obj/get child "type")
|
||||
shape (obj/get props "shape")
|
||||
stroke-width (:stroke-width shape 0)
|
||||
stroke-mask-id (str "outer-stroke-" render-id)
|
||||
shape-id (str "stroke-shape-" render-id)]
|
||||
|
||||
[:g.outer-stroke-shape
|
||||
[:symbol
|
||||
[:> elem-name (-> (obj/clone base-props)
|
||||
(obj/set! "id" shape-id)
|
||||
(obj/without ["style"]))]]
|
||||
|
||||
[:use {:href (str "#" shape-id)
|
||||
:mask (str "url(#" stroke-mask-id ")")
|
||||
:style (-> (obj/get base-props "style")
|
||||
(obj/clone)
|
||||
(obj/update! "strokeWidth" * 2)
|
||||
(obj/without ["fill" "fillOpacity"])
|
||||
(obj/set! "fill" "none"))}]
|
||||
|
||||
[:use {:href (str "#" shape-id)
|
||||
:style (-> (obj/get base-props "style")
|
||||
(obj/clone)
|
||||
(obj/without ["stroke" "strokeWidth" "strokeOpacity" "strokeStyle" "strokeDasharray"]))}]]))
|
||||
|
||||
|
||||
;; Inner alignment: display the shape with double width stroke,
|
||||
;; and clip the result with the original shape without stroke.
|
||||
(mf/defc inner-stroke
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [render-id (mf/use-ctx muc/render-ctx)
|
||||
child (obj/get props "children")
|
||||
base-props (obj/get child "props")
|
||||
elem-name (obj/get child "type")
|
||||
shape (obj/get props "shape")
|
||||
transform (obj/get base-props "transform")
|
||||
|
||||
stroke-width (:stroke-width shape 0)
|
||||
|
||||
clip-id (str "inner-stroke-" render-id)
|
||||
shape-id (str "stroke-shape-" render-id)
|
||||
|
||||
shape-props (-> base-props
|
||||
(add-props {:id shape-id
|
||||
:transform nil
|
||||
:clipPath (str "url('#" clip-id "')")})
|
||||
(add-style {:strokeWidth (* stroke-width 2)}))]
|
||||
|
||||
[:g.inner-stroke-shape {:transform transform}
|
||||
[:> elem-name shape-props]]))
|
||||
|
||||
|
||||
; The SVG standard does not implement yet the 'stroke-alignment'
|
||||
; attribute, to define the position of the stroke relative to the
|
||||
|
@ -19,100 +128,25 @@
|
|||
(mf/defc shape-custom-stroke
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [shape (unchecked-get props "shape")
|
||||
base-props (unchecked-get props "base-props")
|
||||
elem-name (unchecked-get props "elem-name")
|
||||
base-style (obj/get base-props "style")
|
||||
{:keys [x y width height]} (:selrect shape)
|
||||
stroke-id (mf/use-var (uuid/next))
|
||||
(let [child (obj/get props "children")
|
||||
shape (obj/get props "shape")
|
||||
stroke-width (:stroke-width shape 0)
|
||||
stroke-style (:stroke-style shape :none)
|
||||
stroke-position (:stroke-alignment shape :center)]
|
||||
stroke-position (:stroke-alignment shape :center)
|
||||
has-stroke? (and (and (> stroke-width 0)
|
||||
(not= stroke-style :none)))
|
||||
inner? (= :inner stroke-position)
|
||||
outer? (= :outer stroke-position)]
|
||||
|
||||
(cond
|
||||
;; Center alignment (or no stroke): the default in SVG
|
||||
(or (= stroke-style :none) (= stroke-position :center))
|
||||
[:> elem-name (obj/merge! #js {} base-props)]
|
||||
(and has-stroke? inner?)
|
||||
[:& inner-stroke {:shape shape}
|
||||
child]
|
||||
|
||||
;; Inner alignment: display the shape with double width stroke,
|
||||
;; and clip the result with the original shape without stroke.
|
||||
(= stroke-position :inner)
|
||||
(let [clip-id (str "clip-" @stroke-id)
|
||||
(and has-stroke? outer?)
|
||||
[:& outer-stroke {:shape shape}
|
||||
child]
|
||||
|
||||
clip-props (obj/merge
|
||||
base-props
|
||||
#js {:transform nil
|
||||
:style (obj/merge
|
||||
base-style
|
||||
#js {:stroke nil
|
||||
:strokeWidth nil
|
||||
:strokeOpacity nil
|
||||
:strokeDasharray nil
|
||||
:fill "white"
|
||||
:fillOpacity 1})})
|
||||
|
||||
stroke-width (obj/get base-style "strokeWidth" 0)
|
||||
shape-props (obj/merge
|
||||
base-props
|
||||
#js {:clipPath (str "url('#" clip-id "')")
|
||||
:style (obj/merge
|
||||
base-style
|
||||
#js {:strokeWidth (* stroke-width 2)})})]
|
||||
[:*
|
||||
[:> "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
|
||||
;; at double width (transparent fill) and passed through a mask
|
||||
;; that shows the whole shape, but hides the original shape
|
||||
;; without stroke
|
||||
|
||||
(= stroke-position :outer)
|
||||
(let [stroke-mask-id (str "mask-" @stroke-id)
|
||||
stroke-width (obj/get base-style "strokeWidth" 0)
|
||||
mask-props1 (obj/merge
|
||||
base-props
|
||||
#js {:transform nil
|
||||
:style (obj/merge
|
||||
base-style
|
||||
#js {:stroke "white"
|
||||
:strokeWidth (* stroke-width 2)
|
||||
:strokeOpacity 1
|
||||
:strokeDasharray nil
|
||||
:fill "white"
|
||||
:fillOpacity 1})})
|
||||
mask-props2 (obj/merge
|
||||
base-props
|
||||
#js {:transform nil
|
||||
:style (obj/merge
|
||||
base-style
|
||||
#js {:stroke nil
|
||||
:strokeWidth nil
|
||||
:strokeOpacity nil
|
||||
:strokeDasharray nil
|
||||
:fill "black"
|
||||
:fillOpacity 1})})
|
||||
|
||||
shape-props1 (obj/merge
|
||||
base-props
|
||||
#js {:style (obj/merge
|
||||
base-style
|
||||
#js {:stroke nil
|
||||
:strokeWidth nil
|
||||
:strokeOpacity nil
|
||||
:strokeDasharray nil})})
|
||||
shape-props2 (obj/merge
|
||||
base-props
|
||||
#js {:mask (str "url('#" stroke-mask-id "')")
|
||||
:style (obj/merge
|
||||
base-style
|
||||
#js {:strokeWidth (* stroke-width 2)
|
||||
:fill "none"
|
||||
:fillOpacity 0})})]
|
||||
[:*
|
||||
[:mask {:id stroke-mask-id}
|
||||
[:> elem-name mask-props1]
|
||||
[:> elem-name mask-props2]]
|
||||
[:> elem-name shape-props1]
|
||||
[:> elem-name shape-props2]]))))
|
||||
:else
|
||||
child)))
|
||||
|
||||
|
|
|
@ -26,16 +26,6 @@
|
|||
props (-> (attrs/extract-style-attrs shape)
|
||||
(obj/merge!
|
||||
#js {:d pdata}))]
|
||||
(if background?
|
||||
[:g
|
||||
[:path {:stroke "none"
|
||||
:fill "none"
|
||||
:stroke-width "20px"
|
||||
:d pdata}]
|
||||
[:& shape-custom-stroke {:shape shape
|
||||
:base-props props
|
||||
:elem-name "path"}]]
|
||||
[:& shape-custom-stroke {:shape shape
|
||||
:base-props props
|
||||
:elem-name "path"}])))
|
||||
[:& shape-custom-stroke {:shape shape}
|
||||
[:> :path props]]))
|
||||
|
||||
|
|
|
@ -6,23 +6,19 @@
|
|||
|
||||
(ns app.main.ui.shapes.rect
|
||||
(:require
|
||||
[rumext.alpha :as mf]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.main.ui.shapes.attrs :as attrs]
|
||||
[app.main.ui.shapes.custom-stroke :refer [shape-custom-stroke]]
|
||||
[app.common.geom.shapes :as geom]
|
||||
[app.util.object :as obj]
|
||||
[app.main.ui.shapes.gradients :refer [gradient]]
|
||||
|
||||
[cuerdas.core :as str]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.common.geom.point :as gpt]))
|
||||
[app.util.object :as obj]
|
||||
[rumext.alpha :as mf]))
|
||||
|
||||
(mf/defc rect-shape
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [shape (unchecked-get props "shape")
|
||||
{:keys [id x y width height]} shape
|
||||
transform (geom/transform-matrix shape)
|
||||
transform (gsh/transform-matrix shape)
|
||||
|
||||
props (-> (attrs/extract-style-attrs shape)
|
||||
(obj/merge!
|
||||
|
@ -30,11 +26,11 @@
|
|||
:y y
|
||||
:transform transform
|
||||
:width width
|
||||
:height height}))]
|
||||
:height height}))
|
||||
|
||||
[:& shape-custom-stroke {:shape shape
|
||||
:base-props props
|
||||
:elem-name
|
||||
(if (.-d props)
|
||||
"path"
|
||||
"rect")}]))
|
||||
path? (some? (.-d props))]
|
||||
|
||||
[:& shape-custom-stroke {:shape shape}
|
||||
(if path?
|
||||
[:> :path props]
|
||||
[:> :rect props])]))
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.main.ui.context :as muc]
|
||||
[app.main.ui.shapes.custom-stroke :as cs]
|
||||
[app.main.ui.shapes.fill-image :as fim]
|
||||
[app.main.ui.shapes.filters :as filters]
|
||||
[app.main.ui.shapes.gradients :as grad]
|
||||
|
@ -56,5 +57,6 @@
|
|||
[:& filters/filters {:shape shape :filter-id filter-id}]
|
||||
[:& 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}]]
|
||||
[:& fim/fill-image-pattern {:shape shape :render-id render-id}]
|
||||
[:& cs/stroke-defs {:shape shape :render-id render-id}]]
|
||||
children]]))
|
||||
|
|
|
@ -28,5 +28,4 @@
|
|||
|
||||
[:> shape-container {:shape shape
|
||||
:pointer-events (when editing? "none")}
|
||||
[:& path/path-shape {:shape shape
|
||||
:background? true}]]))
|
||||
[:& path/path-shape {:shape shape}]]))
|
||||
|
|
|
@ -27,17 +27,18 @@
|
|||
(js/Object.keys ^js obj))
|
||||
|
||||
(defn get-in
|
||||
[obj keys]
|
||||
(loop [key (first keys)
|
||||
keys (rest keys)
|
||||
res obj]
|
||||
(if (nil? key)
|
||||
res
|
||||
(if (nil? res)
|
||||
res
|
||||
(recur (first keys)
|
||||
(rest keys)
|
||||
(unchecked-get res key))))))
|
||||
([obj keys]
|
||||
(get-in obj keys nil))
|
||||
|
||||
([obj keys default]
|
||||
(loop [key (first keys)
|
||||
keys (rest keys)
|
||||
res obj]
|
||||
(if (or (nil? key) (nil? res))
|
||||
(or res default)
|
||||
(recur (first keys)
|
||||
(rest keys)
|
||||
(unchecked-get res key))))))
|
||||
|
||||
(defn without
|
||||
[obj keys]
|
||||
|
@ -68,6 +69,14 @@
|
|||
(unchecked-set obj key value)
|
||||
obj)
|
||||
|
||||
(defn update!
|
||||
[obj key f & args]
|
||||
(let [found (get obj key ::not-found)]
|
||||
(if-not (identical? ::not-found found)
|
||||
(do (unchecked-set obj key (apply f found args))
|
||||
obj)
|
||||
obj)))
|
||||
|
||||
(defn- props-key-fn
|
||||
[key]
|
||||
(if (or (= key :class) (= key :class-name))
|
||||
|
|
Loading…
Add table
Reference in a new issue