0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-02-10 00:58:26 -05:00

Gradients support in shapes

This commit is contained in:
alonso.torres 2020-10-09 12:29:35 +02:00
parent 381aef77ee
commit c266f78d1e
14 changed files with 176 additions and 79 deletions

View file

@ -202,3 +202,10 @@
:type :colorpicker
:props {:on-change handle-change-color}
:allow-click-outside true}))))))
(defn select-gradient-stop [spot]
(ptk/reify ::start-picker
ptk/UpdateEvent
(update [_ state]
(-> state
(assoc-in [:workspace-local :editing-stop] spot)))))

View file

@ -29,6 +29,14 @@
:type type
:props props
:allow-click-outside false})))))
(defn update-props
([type props]
(ptk/reify ::show-modal
ptk/UpdateEvent
(update [_ state]
(cond-> state
(::modal state)
(update-in [::modal :props] merge props))))))
(defn hide
[]
@ -48,6 +56,10 @@
[type props]
(st/emit! (show type props)))
(defn update-props!
[type props]
(st/emit! (update-props type props)))
(defn allow-click-outside!
[]
(st/emit! (update {:allow-click-outside true})))

View file

@ -20,21 +20,36 @@
:dashed "10,10"
nil))
(defn add-border-radius [attrs shape]
(obj/merge! attrs #js {:rx (:rx shape)
:ry (:ry shape)}))
(defn add-fill [attrs shape]
(let [fill-color-gradient-id (str "fill-color-gradient_" (:id shape))]
(if (:fill-color-gradient shape)
(obj/merge! attrs #js {:fill (str/format "url(#%s)" fill-color-gradient-id)})
(obj/merge! attrs #js {:fill (or (:fill-color shape) "transparent")
:fillOpacity (:fill-opacity shape nil)}))))
(defn add-stroke [attrs shape]
(let [stroke-style (:stroke-style shape :none)
stroke-color-gradient-id (str "stroke-color-gradient_" (:id shape))]
(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)
(defn extract-style-attrs
([shape] (extract-style-attrs shape nil))
([shape gradient-id]
(let [stroke-style (:stroke-style shape :none)
attrs #js {:rx (:rx shape nil)
:ry (:ry shape nil)}
attrs (obj/merge! attrs
(if gradient-id
#js {:fill (str/format "url(#%s)" gradient-id)}
#js {:fill (or (:fill-color shape) "transparent")
:fillOpacity (:fill-opacity shape nil)}))]
(when (not= stroke-style :none)
(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)))
([shape]
(-> (obj/new)
(add-border-radius shape)
(add-fill shape)
(add-stroke shape))))

View file

@ -11,11 +11,11 @@
(:require
[rumext.alpha :as mf]
[cuerdas.core :as str]
[goog.object :as gobj]
[app.util.object :as obj]
[app.common.uuid :as uuid]
[app.common.geom.point :as gpt]))
(mf/defc linear-gradient [{:keys [id shape gradient]}]
(mf/defc linear-gradient [{:keys [id gradient shape]}]
(let [{:keys [x y width height]} shape]
[:defs
[:linearGradient {:id id
@ -29,12 +29,11 @@
:stop-color color
:stop-opacity opacity}])]]))
(mf/defc radial-gradient [{:keys [id shape gradient]}]
(mf/defc radial-gradient [{:keys [id gradient shape]}]
(let [{:keys [x y width height]} shape]
[:defs
(let [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)))
@ -71,8 +70,14 @@
(mf/defc gradient
{::mf/wrap-props false}
[props]
(let [gradient (gobj/get props "gradient")]
(let [attr (obj/get props "attr")
shape (obj/get props "shape")
id (str (name attr) "_" (:id shape))
gradient (get shape attr)
gradient-props #js {:id id
:gradient gradient
:shape shape}]
(case (:type gradient)
:linear [:> linear-gradient props]
:radial [:> radial-gradient props]
nil)))
:linear [:> linear-gradient gradient-props]
:radial [:> radial-gradient gradient-props])))

View file

@ -27,9 +27,7 @@
{:keys [id x y width height]} shape
transform (geom/transform-matrix shape)
gradient-id (when (:fill-color-gradient shape) (str (uuid/next)))
props (-> (attrs/extract-style-attrs shape gradient-id)
props (-> (attrs/extract-style-attrs shape)
(obj/merge!
#js {:x x
:y y
@ -38,12 +36,6 @@
:width width
:height height}))]
[:*
(when gradient-id
[:& gradient {:id gradient-id
:shape shape
:gradient (:fill-color-gradient shape)}])
[:& shape-custom-stroke {:shape shape
:base-props props
:elem-name "rect"}]]))
[:& shape-custom-stroke {:shape shape
:base-props props
:elem-name "rect"}]))

View file

@ -68,17 +68,29 @@
fill-color (obj/get data "fill-color" fill)
fill-opacity (obj/get data "fill-opacity" opacity)
fill-color-gradient (obj/get data "fill-color-gradient" opacity)
fill-color-gradient (-> (js->clj fill-color-gradient :keywordize-keys true)
(update :type keyword))
fill-color-ref-id (obj/get data "fill-color-ref-id")
fill-color-ref-file (obj/get data "fill-color-ref-file")
[r g b a] (uc/hex->rgba fill-color fill-opacity)
background (if fill-color-gradient
(uc/gradient->css (js->clj fill-color-gradient))
(str/format "rgba(%s, %s, %s, %s)" r g b a))
fontsdb (deref fonts/fontsdb)
base #js {:textDecoration text-decoration
:color (str/format "rgba(%s, %s, %s, %s)" r g b a)
;:color (str/format "rgba(%s, %s, %s, %s)" r g b a)
:textTransform text-transform
:lineHeight (or line-height "inherit")}]
:lineHeight (or line-height "inherit")
:background background
:WebkitTextFillColor "transparent"
:WebkitBackgroundClip "text"
}]
(when (and (string? letter-spacing)
(pos? (alength letter-spacing)))
@ -167,7 +179,8 @@
(if (string? text)
(let [style (generate-text-styles (clj->js node))]
[:span {:style style :key index} (if (= text "") "\u00A0" text)])
[:span {:style style
:key (str index "-" (:fill-color node))} (if (= text "") "\u00A0" text)])
(let [children (map-indexed (fn [index node]
(mf/element text-node {:index index :node node :key index}))
children)]

View file

@ -21,7 +21,7 @@
[app.main.store :as st]
[app.main.refs :as refs]
[app.main.data.workspace.libraries :as dwl]
[app.main.data.colors :as dwc]
[app.main.data.colors :as dc]
[app.main.data.modal :as modal]
[app.main.ui.icons :as i]
[app.util.i18n :as i18n :refer [t]]))
@ -43,6 +43,8 @@
(def viewport
(l/derived (l/in [:workspace-local :vport]) st/state))
(def editing-spot-state-ref
(l/derived (l/in [:workspace-local :editing-stop]) st/state))
;; --- Color Picker Modal
@ -525,6 +527,8 @@
picked-color-select (mf/deref picked-color-select)
picked-shift? (mf/deref picked-shift?)
editing-spot-state (mf/deref editing-spot-state-ref)
locale (mf/deref i18n/locale)
;; data-ref (mf/use-var data)
@ -558,7 +562,8 @@
(fn [offset]
(let [offset-color (get-in @state [:stops offset])]
(swap! state assoc :current-color offset-color)
(swap! state assoc :editing-stop offset)))
(swap! state assoc :editing-stop offset)
(st/emit! (dc/select-gradient-stop offset))))
on-activate-gradient
(fn [type]
@ -568,11 +573,11 @@
(swap! state assoc :type :color)
(swap! state dissoc :editing-stop :stops :gradient-data))
(do
(swap! state assoc :type type)
(swap! state assoc :type type
:gradient-data (create-gradient-data type))
(when (not (:stops @state))
(swap! state assoc
:editing-stop 0
:gradient-data (create-gradient-data type)
:stops {0 (:current-color @state)
1 (-> (:current-color @state)
(assoc :alpha 0))}))))))]
@ -631,7 +636,7 @@
;; When closing the modal we update the recent-color list
#_(mf/use-effect
(fn [] (fn []
(st/emit! (dwc/stop-picker))
(st/emit! (dc/stop-picker))
(st/emit! (dwl/add-recent-color (state->data @state))))))
(mf/use-effect
@ -657,6 +662,16 @@
(fn [] (when (and picking-color? picked-color-select)
(on-change (:hex current-color) (:alpha current-color) nil nil picked-shift?))))
(mf/use-effect
(mf/deps editing-spot-state)
#(when (not= editing-spot-state (:editing-stop @state))
(handle-change-stop (or editing-spot-state 0))))
(mf/use-effect
(mf/deps data)
#(let [gradient-data (-> data data->state :gradient-data)]
(swap! state assoc :gradient-data gradient-data)))
(mf/use-effect
(mf/deps @state)
(fn []
@ -669,7 +684,7 @@
{:class (when picking-color? "active")
:on-click (fn []
(modal/allow-click-outside!)
(st/emit! (dwc/start-picker)))}
(st/emit! (dc/start-picker)))}
i/picker]
[:div.gradients-buttons
@ -735,7 +750,7 @@
i/plus])
[:div.color-bullet.button {:style {:background-color "white"}
:on-click #(st/emit! (dwc/show-palette (parse-selected @selected-library)))}
:on-click #(st/emit! (dc/show-palette (parse-selected @selected-library)))}
i/palette]
(for [[idx {:keys [id file-id value]}] (map-indexed vector @current-library-colors)]

View file

@ -13,6 +13,7 @@
[rumext.alpha :as mf]
[cuerdas.core :as str]
[beicon.core :as rx]
[okulary.core :as l]
[app.common.math :as mth]
[app.common.geom.point :as gpt]
[app.common.geom.matrix :as gmt]
@ -20,7 +21,9 @@
[app.main.store :as st]
[app.main.refs :as refs]
[app.main.streams :as ms]
[app.main.data.workspace.common :as dwc]))
[app.main.data.modal :as modal]
[app.main.data.workspace.common :as dwc]
[app.main.data.colors :as dc]))
(def gradient-line-stroke-width 2)
(def gradient-line-stroke-color "white")
@ -32,6 +35,9 @@
(def gradient-square-stroke-color "white")
(def gradient-square-stroke-color-selected "#1FDEA7")
(def editing-spot-ref
(l/derived (l/in [:workspace-local :editing-stop]) st/state))
(mf/defc shadow [{:keys [id x y width height offset]}]
[:filter {:id id
:x x
@ -78,24 +84,13 @@
:height (+ (/ (* 2 gradient-width-handler-radius) zoom) (/ 2 zoom) 4)
:offset (/ 2 zoom)}])
(def default-gradient
{:type :linear
:start-x 0.5 :start-y 0.5
:end-x 0.5 :end-y 1
:width 1.0
:stops [{:offset 0
:color "#FF0000"
:opacity 1}
{:offset 1
:color "#FF0000"
:opacity 0.2}]})
(def checkboard "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAA8AAAAPCAIAAAC0tAIdAAACvUlEQVQoFQGyAk39AeLi4gAAAAAAAB0dHQAAAAAAAOPj4wAAAAAAAB0dHQAAAAAAAOPj4wAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB////AAAAAAAA4+PjAAAAAAAAHR0dAAAAAAAA4+PjAAAAAAAAHR0dAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATj4+MAAAAAAAAdHR0AAAAAAADj4+MAAAAAAAAdHR0AAAAAAADj4+MAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAjScaa0cU7nIAAAAASUVORK5CYII=")
#_(def checkboard "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABHNCSVQICAgIfAhkiAAAADFJREFUOE9jZGBgEAFifOANPknGUQMYhkkYEEgG+NMJKAwIAbwJbdQABnBCIgRoG4gAIF8IsXB/Rs4AAAAASUVORK5CYII=")
(mf/defc gradient-color-handler
[{:keys [filter-id zoom point color angle on-click on-mouse-down on-mouse-up]}]
[{:keys [filter-id zoom point color angle selected
on-click on-mouse-down on-mouse-up]}]
[:g {:filter (str/fmt "url(#%s)" filter-id)
:transform (gmt/rotate-matrix angle point)}
@ -121,7 +116,7 @@
:rx (/ gradient-square-radius zoom)
:width (/ gradient-square-width zoom)
:height (/ gradient-square-width zoom)
:stroke "white"
:stroke (if selected "#31EFB8" "white")
:stroke-width (/ gradient-square-stroke-width zoom)
:fill (:value color)
:fill-opacity (:opacity color)
@ -130,18 +125,27 @@
:on-mouse-up on-mouse-up}]])
(mf/defc gradient-handler-transformed
[{:keys [from-p to-p width-p from-color to-color zoom on-change-start on-change-finish on-change-width on-change-stop-color]}]
[{:keys [from-p to-p width-p from-color to-color zoom editing
on-change-start on-change-finish on-change-width on-change-stop-color]}]
(let [moving-point (mf/use-var nil)
angle (+ 90 (gpt/angle from-p to-p))
on-click (fn [position event]
(dom/stop-propagation event)
(dom/prevent-default event))
(dom/prevent-default event)
(when (#{:from-p :to-p} position)
(st/emit! (dc/select-gradient-stop (case position
:from-p 0
:to-p 1)))))
on-mouse-down (fn [position event]
(dom/stop-propagation event)
(dom/prevent-default event)
(reset! moving-point position))
(reset! moving-point position)
(when (#{:from-p :to-p} position)
(st/emit! (dc/select-gradient-stop (case position
:from-p 0
:to-p 1)))))
on-mouse-up (fn [position event]
(dom/stop-propagation event)
@ -194,7 +198,8 @@
(when width-p
[:g {:filter "url(#gradient_width_handler_drop_shadow)"}
[:circle {:cx (:x width-p)
[:circle {:data-allow-click-modal "colorpicker"
:cx (:x width-p)
:cy (:y width-p)
:r (/ gradient-width-handler-radius zoom)
:fill gradient-width-handler-color
@ -202,7 +207,8 @@
:on-mouse-up (partial on-mouse-up :width-p)}]])
[:& gradient-color-handler
{:filter-id "gradient_square_from_drop_shadow"
{:selected (or (not editing) (= editing 0))
:filter-id "gradient_square_from_drop_shadow"
:zoom zoom
:point from-p
:color from-color
@ -212,7 +218,8 @@
:on-mouse-up (partial on-mouse-up :from-p)}]
[:& gradient-color-handler
{:filter-id "gradient_square_to_drop_shadow"
{:selected (= editing 1)
:filter-id "gradient_square_to_drop_shadow"
:zoom zoom
:point to-p
:color to-color
@ -221,11 +228,16 @@
:on-mouse-down (partial on-mouse-down :to-p)
:on-mouse-up (partial on-mouse-up :to-p)}]]))
(def modal-type-ref
(l/derived (comp :type ::modal/modal) st/state))
(mf/defc gradient-handlers
[{:keys [id zoom]}]
(let [shape (mf/deref (refs/object-by-id id))
{:keys [x y width height] :as sr} (:selrect shape)
gradient (:fill-color-gradient shape)
modal (mf/deref modal-type-ref)
editing-spot (mf/deref editing-spot-ref)
[{start-color :color start-opacity :opacity}
{end-color :color end-opacity :opacity}] (:stops gradient)
@ -271,13 +283,12 @@
norm-dist (/ (gpt/distance point from-p)
(* (/ width 2) scale-factor-y))]
(change! {:width norm-dist})))
(change! {:width norm-dist})))]
on-change-stop-color (fn [offset color opacity] (println "change-color"))]
(when gradient
(when (and gradient (= modal :colorpicker))
[:& gradient-handler-transformed
{:from-p from-p
{:editing editing-spot
:from-p from-p
:to-p to-p
:width-p (when (= :radial (:type gradient)) width-p)
:from-color {:value start-color :opacity start-opacity}
@ -285,5 +296,4 @@
:zoom zoom
:on-change-start on-change-start
:on-change-finish on-change-finish
:on-change-width on-change-width
:on-change-stop-color on-change-stop-color}])))
:on-change-width on-change-width}])))

View file

@ -15,7 +15,9 @@
[app.main.store :as st]
[app.main.ui.keyboard :as kbd]
[app.main.ui.shapes.filters :as filters]
[app.main.ui.shapes.gradients :as grad]
[app.util.dom :as dom]
[app.common.uuid :as uuid]
[app.common.geom.matrix :as gmt]
[app.common.geom.point :as gpt]
[app.common.geom.shapes :as geom]))
@ -72,10 +74,21 @@
(mf/deps shape)
#(on-context-menu % shape))
filter-id (mf/use-memo filters/get-filter-id)]
[:g.shape {:on-mouse-down on-mouse-down
:on-context-menu on-context-menu
:filter (filters/filter-str filter-id shape)}
[:& filters/filters {:filter-id filter-id :shape shape}]
(when (:fill-color-gradient shape)
[:& grad/gradient {:attr :fill-color-gradient
:shape shape}])
(when (:stroke-color-gradient shape)
[:& grad/gradient {:attr :stroke-color-gradient
:shape shape}])
[:& component {:shape shape}]])))

View file

@ -18,6 +18,7 @@
[app.main.ui.keyboard :as kbd]
[app.main.ui.shapes.path :as path]
[app.main.ui.shapes.filters :as filters]
[app.main.ui.shapes.gradients :as grad]
[app.main.ui.workspace.shapes.common :as common]
[app.main.data.workspace.drawing :as dr]
[app.util.dom :as dom]
@ -43,6 +44,7 @@
(dom/stop-propagation event)
(dom/prevent-default event)
(st/emit! (dw/start-edition-mode (:id shape)))))))
filter-id (mf/use-memo filters/get-filter-id)]
[:g.shape {:on-double-click on-double-click
@ -50,5 +52,14 @@
:on-context-menu on-context-menu
:filter (filters/filter-str filter-id shape)}
[:& filters/filters {:filter-id filter-id :shape shape}]
[:& path/path-shape {:shape shape :background? true}]]))
(when (:fill-color-gradient shape)
[:& grad/gradient {:attr :fill-color-gradient
:shape shape}])
(when (:stroke-color-gradient shape)
[:& grad/gradient {:attr :stroke-color-gradient
:shape shape}])
[:& path/path-shape {:shape shape
:background? true}]]))

View file

@ -129,7 +129,9 @@
(mf/use-effect
(mf/deps color)
#(reset! state (parse-color color)))
(fn []
(modal/update-props! :colorpicker {:data (parse-color color)})
(reset! state (parse-color color))))
[:div.row-flex.color-data
[:span.color-th

View file

@ -32,7 +32,7 @@
["slate" :refer [Transforms]]))
(def text-typography-attrs [:typography-ref-id :typography-ref-file])
(def text-fill-attrs [:fill-color :fill-opacity :fill-color-ref-id :fill-color-ref-file :fill :opacity ])
(def text-fill-attrs [:fill-color :fill-opacity :fill-color-ref-id :fill-color-ref-file :fill-color-gradient :fill :opacity ])
(def text-font-attrs [:font-id :font-family :font-variant-id :font-size :font-weight :font-style])
(def text-align-attrs [:text-align])
(def text-spacing-attrs [:line-height :letter-spacing])

View file

@ -88,5 +88,5 @@
(if (= type :linear)
(str/fmt "linear-gradient(to bottom, %s)" stops-css)
(str/fmt "radial-gradient(circle, %s" stops-css))))
(str/fmt "radial-gradient(circle, %s)" stops-css))))

View file

@ -15,6 +15,8 @@
[goog.object :as gobj]
["lodash/omit" :as omit]))
(defn new [] #js {})
(defn get
([obj k]
(when-not (nil? obj)