Import paths as native shapes

This commit is contained in:
alonso.torres 2021-02-22 21:40:15 +01:00 committed by Andrey Antukh
parent 741d67c30b
commit 19febde547
28 changed files with 921 additions and 209 deletions

View file

@ -12,7 +12,8 @@
[rumext.alpha :as mf]
[cuerdas.core :as str]
[app.util.object :as obj]
[app.main.ui.context :as muc]))
[app.main.ui.context :as muc]
[app.util.svg :as usvg]))
(defn- stroke-type->dasharray
[style]
@ -74,45 +75,77 @@
attrs)))
(defn add-fill [attrs shape render-id]
(let [fill-color-gradient-id (str "fill-color-gradient_" render-id)]
(cond
(:fill-color-gradient shape)
(obj/merge! attrs #js {:fill (str/format "url(#%s)" fill-color-gradient-id)})
(let [fill-attrs (cond
(contains? shape :fill-color-gradient)
(let [fill-color-gradient-id (str "fill-color-gradient_" render-id)]
{:fill (str/format "url(#%s)" fill-color-gradient-id)})
(and (not= :svg-raw (:type shape))
(not (:fill-color-gradient shape)))
(obj/merge! attrs #js {:fill (or (:fill-color shape) "transparent")
:fillOpacity (:fill-opacity shape nil)})
(contains? shape :fill-color)
{:fill (:fill-color shape)}
(and (= :svg-raw (:type shape))
(or (:fill-opacity shape) (:fill-color shape)))
(obj/merge! attrs #js {:fill (:fill-color shape)
:fillOpacity (:fill-opacity shape nil)})
;; If contains svg-attrs the origin is svg. If it's not svg origin
;; we setup the default fill as transparent (instead of black)
(not (contains? shape :svg-attrs))
{:fill "transparent"}
:else attrs)))
:else
{})
fill-attrs (cond-> fill-attrs
(contains? shape :fill-opacity)
(assoc :fillOpacity (:fill-opacity shape)))]
(obj/merge! attrs (clj->js fill-attrs))))
(defn add-stroke [attrs shape render-id]
(let [stroke-style (:stroke-style shape :none)
stroke-color-gradient-id (str "stroke-color-gradient_" render-id)]
(if (not= stroke-style :none)
(if (:stroke-color-gradient shape)
(obj/merge! attrs
#js {:stroke (str/format "url(#%s)" stroke-color-gradient-id)
:strokeWidth (:stroke-width shape 1)
:strokeDasharray (stroke-type->dasharray stroke-style)})
(obj/merge! attrs
#js {:stroke (:stroke-color shape nil)
:strokeWidth (:stroke-width shape 1)
:strokeOpacity (:stroke-opacity shape nil)
:strokeDasharray (stroke-type->dasharray stroke-style)}))))
attrs)
(let [stroke-attrs
(cond-> {:strokeWidth (:stroke-width shape 1)}
(:stroke-color-gradient shape)
(assoc :stroke (str/format "url(#%s)" stroke-color-gradient-id))
(not (:stroke-color-gradient shape))
(assoc :stroke (:stroke-color shape nil)
:strokeOpacity (:stroke-opacity shape nil))
(not= stroke-style :svg)
(assoc :strokeDasharray (stroke-type->dasharray stroke-style)))]
(obj/merge! attrs (clj->js stroke-attrs)))
attrs)))
(defn extract-svg-attrs
[render-id svg-defs svg-attrs]
(let [replace-id (fn [id]
(if (contains? svg-defs id)
(str render-id "-" id)
id))
svg-attrs (-> svg-attrs
(usvg/update-attr-ids replace-id)
(usvg/clean-attrs))
attrs (-> svg-attrs (dissoc :style) (clj->js))
styles (-> svg-attrs (:style {}) (clj->js))]
[attrs styles]))
(defn extract-style-attrs
([shape]
(let [render-id (mf/use-ctx muc/render-ctx)
svg-defs (:svg-defs shape {})
svg-attrs (:svg-attrs shape {})
[svg-attrs svg-styles] (mf/use-memo
(mf/deps render-id svg-defs svg-attrs)
#(extract-svg-attrs render-id svg-defs svg-attrs))
styles (-> (obj/new)
(obj/merge! svg-styles)
(add-fill shape render-id)
(add-stroke shape render-id))]
(-> (obj/new)
(obj/merge! svg-attrs)
(add-border-radius shape)
(obj/set! "style" styles)))))

View file

@ -9,7 +9,9 @@
(ns app.main.ui.shapes.group
(:require
[app.util.object :as obj]
[rumext.alpha :as mf]
[app.main.ui.shapes.attrs :as attrs]
[app.main.ui.shapes.mask :refer [mask-str mask-factory]]))
(defn group-shape
@ -28,12 +30,15 @@
show-mask? (and (:masked-group? shape) (not expand-mask))
mask (when show-mask? (first childs))
childs (if show-mask? (rest childs) childs)]
childs (if show-mask? (rest childs) childs)
[:g.group
{:pointer-events pointer-events
:mask (when (and mask (not expand-mask)) (mask-str mask))}
props (-> (attrs/extract-style-attrs shape)
(obj/merge!
#js {:className "group"
:pointerEvents pointer-events
:mask (when (and mask (not expand-mask)) (mask-str mask))}))]
[:> :g props
(when mask
[:> render-mask #js {:frame frame :mask mask}])

View file

@ -9,13 +9,12 @@
(ns app.main.ui.shapes.shape
(:require
[app.common.geom.shapes :as geom]
[app.common.uuid :as uuid]
[app.main.ui.context :as muc]
[app.main.ui.shapes.filters :as filters]
[app.main.ui.shapes.gradients :as grad]
[app.main.ui.shapes.svg-defs :as defs]
[app.util.object :as obj]
[cuerdas.core :as str]
[rumext.alpha :as mf]))
(mf/defc shape-container
@ -48,6 +47,7 @@
[:& (mf/provider muc/render-ctx) {:value render-id}
[:> wrapper-tag group-props
[:defs
[:& defs/svg-defs {:shape shape :render-id render-id}]
[:& filters/filters {:shape shape :filter-id filter-id}]
[:& grad/gradient {:shape shape :attr :fill-color-gradient}]
[:& grad/gradient {:shape shape :attr :stroke-color-gradient}]]

View file

@ -0,0 +1,103 @@
;; This Source Code Form is subject to the terms of the Mozilla Public
;; License, v. 2.0. If a copy of the MPL was not distributed with this
;; file, You can obtain one at http://mozilla.org/MPL/2.0/.
;;
;; This Source Code Form is "Incompatible With Secondary Licenses", as
;; defined by the Mozilla Public License, v. 2.0.
;;
;; Copyright (c) 2020-2021 UXBOX Labs SL
(ns app.main.ui.shapes.svg-defs
(:require
[app.common.data :as d]
[app.common.geom.matrix :as gmt]
[app.common.geom.point :as gpt]
[app.util.object :as obj]
[app.util.svg :as usvg]
[rumext.alpha :as mf]))
(defn add-matrix [attrs transform-key transform-matrix]
(update attrs
transform-key
(fn [val]
(if val
(str transform-matrix " " val)
(str transform-matrix)))))
(defn transform-region [attrs transform]
(let [{x-str :x y-str :y width-str :width height-str :height} attrs
data (map d/parse-double [x-str y-str width-str height-str])]
(if (every? (comp not nil?) data)
(let [[x y width height] data
p1 (-> (gpt/point x y)
(gpt/transform transform))
p2 (-> (gpt/point (+ x width) (+ y height))
(gpt/transform transform))]
(assoc attrs
:x (:x p1)
:y (:y p1)
:width (- (:x p2) (:x p1))
:height (- (:y p2) (:y p1))))
attrs)))
(mf/defc svg-node [{:keys [node prefix-id transform]}]
(cond
(string? node) node
:else
(let [{:keys [tag attrs content]} node
transform-gradient? (and (#{:linearGradient :radialGradient} tag)
(= "userSpaceOnUse" (get attrs :gradientUnits "userSpaceOnUse")))
transform-pattern? (and (= :pattern tag)
(every? d/num-string? [(:x attrs "0") (:y attrs "0") (:width attrs "0") (:height attrs "0")])
(= "userSpaceOnUse" (get attrs :patternUnits "userSpaceOnUse")))
transform-filter? (and (= #{:filter
;; Filter primitives. We need to remap subregions
:feBlend :feColorMatrix :feComponentTransfer :feComposite :feConvolveMatrix
:feDiffuseLighting :feDisplacementMap :feFlood :feGaussianBlur
:feImage :feMerge :feMorphology :feOffset
:feSpecularLighting :feTile :feTurbulence} tag)
(= "userSpaceOnUse" (get attrs :filterUnits "userSpaceOnUse")))
attrs (-> attrs
(usvg/update-attr-ids prefix-id)
(usvg/clean-attrs)
(cond->
transform-gradient? (add-matrix :gradientTransform transform)
transform-pattern? (add-matrix :patternTransform transform)
transform-filter? (transform-region transform)))
[wrapper wrapper-props] (if (= tag :mask)
["g" #js {:transform (str transform)}]
[mf/Fragment (obj/new)])]
[:> (name tag) (clj->js attrs)
[:> wrapper wrapper-props
(for [node content] [:& svg-node {:node node
:prefix-id prefix-id
:transform transform}])]])))
(mf/defc svg-defs [{:keys [shape render-id]}]
(let [svg-defs (:svg-defs shape)
transform (mf/use-memo
(mf/deps shape)
#(if (= :svg-raw (:type shape))
(gmt/matrix)
(usvg/svg-transform-matrix shape)))
prefix-id
(fn [id]
(cond->> id
(contains? svg-defs id) (str render-id "-")))]
(when (and svg-defs (not (empty? svg-defs)))
(for [svg-def (vals svg-defs)]
[:& svg-node {:node svg-def
:prefix-id prefix-id
:transform transform}]))))

View file

@ -21,7 +21,7 @@
[rumext.alpha :as mf]))
;; Graphic tags
(defonce graphic-element? #{ :circle :ellipse :image :line :path :polygon :polyline :rect :text #_"use"})
(defonce graphic-element? #{:circle :ellipse :image :line :path :polygon :polyline :rect :text :use})
;; Context to store a re-mapping of the ids
(def svg-ids-ctx (mf/create-context nil))
@ -37,17 +37,11 @@
(obj/set! "style" style))))
(defn translate-shape [attrs shape]
(let [{svg-width :width svg-height :height :as root-shape} (:root-attrs shape)
{:keys [x y width height]} (:selrect shape)
transform (->> (:transform attrs "")
(str (gmt/multiply
(gmt/matrix)
(gsh/transform-matrix shape)
(gmt/translate-matrix (gpt/point x y))
(gmt/scale-matrix (gpt/point (/ width svg-width) (/ height svg-height))))
" "))]
(let [transform (str (usvg/svg-transform-matrix shape)
" "
(:transform attrs ""))]
(cond-> attrs
(and root-shape (graphic-element? (-> shape :content :tag)))
(and (:svg-viewbox shape) (graphic-element? (-> shape :content :tag)))
(assoc :transform transform))))
(mf/defc svg-root