mirror of
https://github.com/penpot/penpot.git
synced 2025-01-08 07:50:43 -05:00
✨ Fix shadows in frames for dashboard and viewer
This commit is contained in:
parent
e1d6cded62
commit
8e60834292
11 changed files with 215 additions and 258 deletions
|
@ -98,13 +98,6 @@
|
|||
(defn distance-shapes [shape other]
|
||||
(distance-selrect (:selrect shape) (:selrect other)))
|
||||
|
||||
(defn shape-stroke-margin
|
||||
[shape stroke-width]
|
||||
(if (= (:type shape) :path)
|
||||
;; TODO: Calculate with the stroke offset (not implemented yet
|
||||
(mth/sqrt (* 2 stroke-width stroke-width))
|
||||
(- (mth/sqrt (* 2 stroke-width stroke-width)) stroke-width)))
|
||||
|
||||
(defn close-attrs?
|
||||
"Compares two shapes attributes to see if they are equal or almost
|
||||
equal (in case of numeric). Takes into account attributes that are
|
||||
|
|
151
common/src/app/common/geom/shapes/bounds.cljc
Normal file
151
common/src/app/common/geom/shapes/bounds.cljc
Normal file
|
@ -0,0 +1,151 @@
|
|||
(ns app.common.geom.shapes.bounds
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.geom.shapes.rect :as gsr]
|
||||
[app.common.math :as mth]
|
||||
[app.common.pages.helpers :as cph]))
|
||||
|
||||
(defn shape-stroke-margin
|
||||
[shape stroke-width]
|
||||
(if (= (:type shape) :path)
|
||||
;; TODO: Calculate with the stroke offset (not implemented yet
|
||||
(mth/sqrt (* 2 stroke-width stroke-width))
|
||||
(- (mth/sqrt (* 2 stroke-width stroke-width)) stroke-width)))
|
||||
|
||||
(defn blur-filters [type value]
|
||||
(->> [value]
|
||||
(remove :hidden)
|
||||
(filter #(= (:type %) type))
|
||||
(map #(hash-map :id (str "filter_" (:id %))
|
||||
:type (:type %)
|
||||
:params %))))
|
||||
|
||||
(defn shadow-filters [type filters]
|
||||
(->> filters
|
||||
(remove :hidden)
|
||||
(filter #(= (:style %) type))
|
||||
(map #(hash-map :id (str "filter_" (:id %))
|
||||
:type (:style %)
|
||||
:params %))))
|
||||
|
||||
(defn shape->filters
|
||||
[shape]
|
||||
(d/concat-vec
|
||||
[{:id "BackgroundImageFix" :type :image-fix}]
|
||||
|
||||
;; Background blur won't work in current SVG specification
|
||||
;; We can revisit this in the future
|
||||
#_(->> shape :blur (blur-filters :background-blur))
|
||||
|
||||
(->> shape :shadow (shadow-filters :drop-shadow))
|
||||
[{:id "shape" :type :blend-filters}]
|
||||
(->> shape :shadow (shadow-filters :inner-shadow))
|
||||
(->> shape :blur (blur-filters :layer-blur))))
|
||||
|
||||
(defn calculate-filter-bounds [{:keys [x y width height]} filter-entry]
|
||||
(let [{:keys [offset-x offset-y blur spread] :or {offset-x 0 offset-y 0 blur 0 spread 0}} (:params filter-entry)
|
||||
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-y) (* spread 2) (* blur 2) 10)]
|
||||
(gsr/make-selrect filter-x filter-y filter-width filter-height)))
|
||||
|
||||
(defn get-rect-filter-bounds
|
||||
[selrect filters blur-value]
|
||||
(let [filter-bounds (->> filters
|
||||
(filter #(= :drop-shadow (:type %)))
|
||||
(map (partial calculate-filter-bounds selrect))
|
||||
(concat [selrect])
|
||||
(gsr/join-selrects))
|
||||
delta-blur (* blur-value 2)
|
||||
|
||||
result
|
||||
(-> filter-bounds
|
||||
(update :x - delta-blur)
|
||||
(update :y - delta-blur)
|
||||
(update :x1 - delta-blur)
|
||||
(update :x1 - delta-blur)
|
||||
(update :x2 + delta-blur)
|
||||
(update :y2 + delta-blur)
|
||||
(update :width + (* delta-blur 2))
|
||||
(update :height + (* delta-blur 2)))]
|
||||
|
||||
result))
|
||||
|
||||
(defn get-shape-filter-bounds
|
||||
([shape]
|
||||
(let [svg-root? (and (= :svg-raw (:type shape)) (not= :svg (get-in shape [:content :tag])))]
|
||||
(if svg-root?
|
||||
(:selrect shape)
|
||||
|
||||
(let [filters (shape->filters shape)
|
||||
blur-value (or (-> shape :blur :value) 0)]
|
||||
(get-rect-filter-bounds (-> shape :points gsr/points->selrect) filters blur-value))))))
|
||||
|
||||
(defn calculate-padding
|
||||
([shape]
|
||||
(calculate-padding shape false))
|
||||
|
||||
([shape ignore-margin?]
|
||||
(let [stroke-width (apply max 0 (map #(case (:stroke-alignment % :center)
|
||||
:center (/ (:stroke-width % 0) 2)
|
||||
:outer (:stroke-width % 0)
|
||||
0) (:strokes shape)))
|
||||
|
||||
margin (if ignore-margin?
|
||||
0
|
||||
(apply max 0 (map #(shape-stroke-margin % stroke-width) (:strokes shape))))
|
||||
|
||||
shadow-width (apply max 0 (map #(case (:style % :drop-shadow)
|
||||
:drop-shadow (+ (mth/abs (:offset-x %)) (* (:spread %) 2) (* (:blur %) 2) 10)
|
||||
0) (:shadow shape)))
|
||||
|
||||
shadow-height (apply max 0 (map #(case (:style % :drop-shadow)
|
||||
:drop-shadow (+ (mth/abs (:offset-y %)) (* (:spread %) 2) (* (:blur %) 2) 10)
|
||||
0) (:shadow shape)))]
|
||||
|
||||
{:horizontal (+ stroke-width margin shadow-width)
|
||||
:vertical (+ stroke-width margin shadow-height)})))
|
||||
|
||||
(defn- add-padding
|
||||
[bounds padding]
|
||||
(-> bounds
|
||||
(update :x - (:horizontal padding))
|
||||
(update :y - (:vertical padding))
|
||||
(update :width + (* 2 (:horizontal padding)))
|
||||
(update :height + (* 2 (:vertical padding)))))
|
||||
|
||||
(defn get-object-bounds
|
||||
[objects shape]
|
||||
|
||||
(let [calculate-base-bounds
|
||||
(fn [shape]
|
||||
(-> (get-shape-filter-bounds shape)
|
||||
(add-padding (calculate-padding shape true))))
|
||||
|
||||
bounds
|
||||
(cph/reduce-objects
|
||||
objects
|
||||
(fn [shape]
|
||||
(and (d/not-empty? (:shapes shape))
|
||||
(or (not (cph/frame-shape? shape))
|
||||
(:show-content shape))
|
||||
|
||||
(or (not (cph/group-shape? shape))
|
||||
(not (:masked-group? shape)))))
|
||||
|
||||
(:id shape)
|
||||
|
||||
(fn [result shape]
|
||||
(conj result (get-object-bounds objects shape)))
|
||||
|
||||
[(calculate-base-bounds shape)])
|
||||
|
||||
|
||||
children-bounds (or (:children-bounds shape) (gsr/join-selrects bounds))
|
||||
|
||||
filters (shape->filters shape)
|
||||
blur-value (or (-> shape :blur :value) 0)]
|
||||
|
||||
(get-rect-filter-bounds children-bounds filters blur-value)))
|
||||
|
|
@ -657,7 +657,10 @@
|
|||
(reduce-objects objects nil reducer-fn init-val))
|
||||
|
||||
([objects check-children? reducer-fn init-val]
|
||||
(let [root-children (get-in objects [uuid/zero :shapes])]
|
||||
(reduce-objects objects check-children? uuid/zero reducer-fn init-val))
|
||||
|
||||
([objects check-children? root-id reducer-fn init-val]
|
||||
(let [root-children (get-in objects [root-id :shapes])]
|
||||
(if (empty? root-children)
|
||||
init-val
|
||||
|
||||
|
|
|
@ -359,7 +359,6 @@
|
|||
|
||||
(p/let [params {:file-id file-id
|
||||
:page-id page-id
|
||||
:render-texts true
|
||||
:render-embed true
|
||||
:object-id (mapv :id objects)
|
||||
:route "objects"}
|
||||
|
|
|
@ -147,6 +147,7 @@
|
|||
height: 120px;
|
||||
border: 1px solid $color-gray-20;
|
||||
border-radius: 2px;
|
||||
padding: 4px;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
|
|
|
@ -14,11 +14,11 @@
|
|||
(:require
|
||||
["react-dom/server" :as rds]
|
||||
[app.common.colors :as clr]
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.geom.shapes.bounds :as gsb]
|
||||
[app.common.math :as mth]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.config :as cfg]
|
||||
|
@ -28,7 +28,6 @@
|
|||
[app.main.ui.shapes.circle :as circle]
|
||||
[app.main.ui.shapes.embed :as embed]
|
||||
[app.main.ui.shapes.export :as export]
|
||||
[app.main.ui.shapes.filters :as filters]
|
||||
[app.main.ui.shapes.frame :as frame]
|
||||
[app.main.ui.shapes.group :as group]
|
||||
[app.main.ui.shapes.image :as image]
|
||||
|
@ -61,13 +60,11 @@
|
|||
|
||||
(defn- calculate-dimensions
|
||||
[objects]
|
||||
(let [rect
|
||||
(let [bounds
|
||||
(->> (cph/get-root-objects objects)
|
||||
(map #(if (some? (:children-bounds %))
|
||||
(:children-bounds %)
|
||||
(gsh/points->selrect (:points %))))
|
||||
(gsh/join-selrects))]
|
||||
(-> rect
|
||||
(map (partial gsb/get-object-bounds objects))
|
||||
(gsh/join-rects))]
|
||||
(-> bounds
|
||||
(update :x mth/finite 0)
|
||||
(update :y mth/finite 0)
|
||||
(update :width mth/finite 100000)
|
||||
|
@ -193,36 +190,12 @@
|
|||
|
||||
(reduce updt-fn objects mod-ids)))
|
||||
|
||||
(defn get-object-bounds
|
||||
[objects object-id]
|
||||
(let [object (get objects object-id)
|
||||
padding (filters/calculate-padding object true)
|
||||
bounds (-> (filters/get-filters-bounds object)
|
||||
(update :x - (:horizontal padding))
|
||||
(update :y - (:vertical padding))
|
||||
(update :width + (* 2 (:horizontal padding)))
|
||||
(update :height + (* 2 (:vertical padding))))]
|
||||
|
||||
(cond
|
||||
(and (cph/group-shape? object) (:masked-group? object))
|
||||
(get-object-bounds objects (-> object :shapes first))
|
||||
|
||||
(or (cph/group-shape? object)
|
||||
(and (cph/frame-shape? object) (:show-content object)))
|
||||
(->> (:shapes object)
|
||||
(into [bounds] (map (partial get-object-bounds objects)))
|
||||
(gsh/join-rects))
|
||||
|
||||
:else
|
||||
bounds)))
|
||||
|
||||
(mf/defc page-svg
|
||||
{::mf/wrap [mf/memo]}
|
||||
[{:keys [data thumbnails? render-embed? include-metadata?] :as props
|
||||
:or {render-embed? false include-metadata? false}}]
|
||||
(let [objects (:objects data)
|
||||
shapes (cph/get-immediate-children objects)
|
||||
|
||||
dim (calculate-dimensions objects)
|
||||
vbox (format-viewbox dim)
|
||||
bgcolor (dm/get-in data [:options :background] default-color)
|
||||
|
@ -248,7 +221,6 @@
|
|||
(when include-metadata?
|
||||
[:& export/export-page {:options (:options data)}])
|
||||
|
||||
|
||||
(let [shapes (->> shapes
|
||||
(remove cph/frame-shape?)
|
||||
(mapcat #(cph/get-children-with-self objects (:id %))))
|
||||
|
@ -262,20 +234,20 @@
|
|||
|
||||
;; Component that serves for render frame thumbnails, mainly used in
|
||||
;; the viewer and handoff
|
||||
|
||||
(mf/defc frame-svg
|
||||
{::mf/wrap [mf/memo]}
|
||||
[{:keys [objects frame zoom show-thumbnails?] :or {zoom 1} :as props}]
|
||||
(let [frame-id (:id frame)
|
||||
include-metadata? (mf/use-ctx export/include-metadata-ctx)
|
||||
|
||||
bounds (or (:children-bounds frame) (gsh/points->rect (:points frame)))
|
||||
bounds (gsb/get-object-bounds objects frame)
|
||||
|
||||
modifier
|
||||
(mf/with-memo [(:x bounds) (:y bounds)]
|
||||
(-> (gpt/point (:x bounds) (:y bounds))
|
||||
(gpt/negate)
|
||||
(gmt/translate-matrix)))
|
||||
;; Bounds without shadows/blur will be the bounds of the thumbnail
|
||||
bounds2 (gsb/get-object-bounds objects (dissoc frame :shadow :blur))
|
||||
|
||||
delta-bounds (gpt/point (:x bounds) (:y bounds))
|
||||
|
||||
modifier (gmt/translate-matrix (gpt/negate delta-bounds))
|
||||
|
||||
children-ids
|
||||
(cph/get-children-ids objects frame-id)
|
||||
|
@ -293,19 +265,19 @@
|
|||
(assoc-in [:modifiers :displacement] modifier)
|
||||
(gsh/transform-shape)))
|
||||
|
||||
bounds
|
||||
(if (:show-content frame)
|
||||
(gsh/selection-rect (concat [frame] (->> children-ids (map (d/getf objects)))))
|
||||
(-> frame :points gsh/points->rect))
|
||||
|
||||
frame
|
||||
(cond-> frame
|
||||
(and (some? bounds) (nil? (:children-bounds bounds)))
|
||||
(assoc :children-bounds bounds))
|
||||
(assoc :children-bounds bounds2))
|
||||
|
||||
frame-wrapper
|
||||
(mf/with-memo [objects]
|
||||
(frame-wrapper-factory objects))
|
||||
frame (-> frame
|
||||
(update-in [:children-bounds :x] - (:x delta-bounds))
|
||||
(update-in [:children-bounds :y] - (:y delta-bounds)))
|
||||
|
||||
shape-wrapper
|
||||
(mf/use-memo
|
||||
(mf/deps objects)
|
||||
#(shape-wrapper-factory objects))
|
||||
|
||||
width (* (:width bounds) zoom)
|
||||
height (* (:height bounds) zoom)
|
||||
|
@ -321,13 +293,11 @@
|
|||
:xmlns:penpot (when include-metadata? "https://penpot.app/xmlns")
|
||||
:fill "none"}
|
||||
|
||||
[:> shape-container {:shape frame}
|
||||
[:& frame-wrapper {:shape frame :view-box vbox}]]]]))
|
||||
[:& shape-wrapper {:shape frame}]]]))
|
||||
|
||||
|
||||
;; Component for rendering a thumbnail of a single componenent. Mainly
|
||||
;; used to render thumbnails on assets panel.
|
||||
|
||||
(mf/defc component-svg
|
||||
{::mf/wrap [mf/memo #(mf/deferred % ts/idle-then-raf)]}
|
||||
[{:keys [objects group zoom] :or {zoom 1} :as props}]
|
||||
|
@ -375,7 +345,7 @@
|
|||
|
||||
(mf/defc object-svg
|
||||
{::mf/wrap [mf/memo]}
|
||||
[{:keys [objects object-id render-texts? render-embed?]
|
||||
[{:keys [objects object-id render-embed?]
|
||||
:or {render-embed? false}
|
||||
:as props}]
|
||||
(let [object (get objects object-id)
|
||||
|
@ -383,30 +353,21 @@
|
|||
(:hide-fill-on-export object)
|
||||
(assoc :fills []))
|
||||
|
||||
{:keys [x y width height]} (get-object-bounds objects object-id)
|
||||
vbox (dm/str x " " y " " width " " height)
|
||||
|
||||
frame-wrapper
|
||||
(mf/with-memo [objects]
|
||||
(frame-wrapper-factory objects))
|
||||
|
||||
group-wrapper
|
||||
(mf/with-memo [objects]
|
||||
(group-wrapper-factory objects))
|
||||
bounds (gsb/get-object-bounds objects object)
|
||||
vbox (format-viewbox bounds)
|
||||
fonts (ff/shape->fonts object objects)
|
||||
|
||||
shape-wrapper
|
||||
(mf/with-memo [objects]
|
||||
(shape-wrapper-factory objects))
|
||||
|
||||
text-shapes (sequence (filter cph/text-shape?) (vals objects))
|
||||
render-texts? (and render-texts? (d/seek (comp nil? :position-data) text-shapes))]
|
||||
(shape-wrapper-factory objects))]
|
||||
|
||||
[:& (mf/provider export/include-metadata-ctx) {:value false}
|
||||
[:& (mf/provider embed/context) {:value render-embed?}
|
||||
[:svg {:id (dm/str "screenshot-" object-id)
|
||||
:view-box vbox
|
||||
:width width
|
||||
:height height
|
||||
:width (:width bounds)
|
||||
:height (:height bounds)
|
||||
:version "1.1"
|
||||
:xmlns "http://www.w3.org/2000/svg"
|
||||
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
||||
|
@ -415,30 +376,8 @@
|
|||
:style {:-webkit-print-color-adjust :exact}
|
||||
:fill "none"}
|
||||
|
||||
(let [fonts (ff/shape->fonts object objects)]
|
||||
[:& ff/fontfaces-style {:fonts fonts}])
|
||||
|
||||
(case (:type object)
|
||||
:frame [:> shape-container {:shape object}
|
||||
[:& frame-wrapper {:shape object :view-box vbox}]]
|
||||
:group [:> shape-container {:shape object}
|
||||
[:& group-wrapper {:shape object}]]
|
||||
[:& shape-wrapper {:shape object}])]
|
||||
|
||||
;; Auxiliary SVG for rendering text-shapes
|
||||
(when render-texts?
|
||||
(for [object text-shapes]
|
||||
[:& (mf/provider muc/text-plain-colors-ctx) {:value true}
|
||||
[:svg
|
||||
{:id (dm/str "screenshot-text-" (:id object))
|
||||
:view-box (dm/str "0 0 " (:width object) " " (:height object))
|
||||
:width (:width object)
|
||||
:height (:height object)
|
||||
:version "1.1"
|
||||
:xmlns "http://www.w3.org/2000/svg"
|
||||
:xmlnsXlink "http://www.w3.org/1999/xlink"
|
||||
:fill "none"}
|
||||
[:& shape-wrapper {:shape (assoc object :x 0 :y 0)}]]]))]]))
|
||||
[:& ff/fontfaces-style {:fonts fonts}]
|
||||
[:& shape-wrapper {:shape object}]]]]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;; SPRITES (DEBUG)
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.geom.shapes.bounds :as gsb]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.main.ui.context :as muc]
|
||||
[app.main.ui.shapes.attrs :as attrs]
|
||||
|
@ -45,7 +46,7 @@
|
|||
:center (/ (:stroke-width shape 0) 2)
|
||||
:outer (:stroke-width shape 0)
|
||||
0)
|
||||
margin (gsh/shape-stroke-margin shape stroke-width)
|
||||
margin (gsb/shape-stroke-margin shape stroke-width)
|
||||
bounding-box (-> (gsh/points->selrect (:points shape))
|
||||
(update :x - (+ stroke-width margin))
|
||||
(update :y - (+ stroke-width margin))
|
||||
|
|
|
@ -8,8 +8,7 @@
|
|||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.math :as mth]
|
||||
[app.common.geom.shapes.bounds :as gsb]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.util.color :as color]
|
||||
[cuerdas.core :as str]
|
||||
|
@ -108,34 +107,6 @@
|
|||
:in2 filter-in
|
||||
:result filter-id}])
|
||||
|
||||
(defn filter-bounds [shape filter-entry]
|
||||
(let [{:keys [x y width height]} (:selrect shape)
|
||||
{:keys [offset-x offset-y blur spread] :or {offset-x 0 offset-y 0 blur 0 spread 0}} (:params filter-entry)
|
||||
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-y) (* spread 2) (* blur 2) 10)]
|
||||
{:x1 filter-x
|
||||
:y1 filter-y
|
||||
:x2 (+ filter-x filter-width)
|
||||
:y2 (+ filter-y filter-height)}))
|
||||
|
||||
(defn blur-filters [type value]
|
||||
(->> [value]
|
||||
(remove :hidden)
|
||||
(filter #(= (:type %) type))
|
||||
(map #(hash-map :id (str "filter_" (:id %))
|
||||
:type (:type %)
|
||||
:params %))))
|
||||
|
||||
(defn shadow-filters [type filters]
|
||||
(->> filters
|
||||
(remove :hidden)
|
||||
(filter #(= (:style %) type))
|
||||
(map #(hash-map :id (str "filter_" (:id %))
|
||||
:type (:style %)
|
||||
:params %))))
|
||||
|
||||
(mf/defc filter-entry [{:keys [entry]}]
|
||||
(let [props #js {:filter-id (:id entry)
|
||||
:filter-in (:filter-in entry)
|
||||
|
@ -148,84 +119,6 @@
|
|||
:image-fix [:> image-fix-filter props]
|
||||
:blend-filters [:> blend-filters props])))
|
||||
|
||||
(defn shape->filters
|
||||
[shape]
|
||||
(d/concat-vec
|
||||
[{:id "BackgroundImageFix" :type :image-fix}]
|
||||
|
||||
;; Background blur won't work in current SVG specification
|
||||
;; We can revisit this in the future
|
||||
#_(->> shape :blur (blur-filters :background-blur))
|
||||
|
||||
(->> shape :shadow (shadow-filters :drop-shadow))
|
||||
[{:id "shape" :type :blend-filters}]
|
||||
(->> shape :shadow (shadow-filters :inner-shadow))
|
||||
(->> shape :blur (blur-filters :layer-blur))))
|
||||
|
||||
(defn get-filters-bounds
|
||||
([shape]
|
||||
(let [filters (shape->filters shape)
|
||||
blur-value (or (-> shape :blur :value) 0)]
|
||||
(get-filters-bounds shape filters blur-value)))
|
||||
|
||||
([shape filters blur-value]
|
||||
|
||||
(let [svg-root? (and (= :svg-raw (:type shape)) (not= :svg (get-in shape [:content :tag])))
|
||||
{:keys [x y width height]} (:selrect shape)]
|
||||
(if svg-root?
|
||||
;; When is a raw-svg but not the root we use the whole svg as bound for the filter. Is the maximum
|
||||
;; we're allowed to display
|
||||
{:x x :y y :width width :height height}
|
||||
|
||||
;; Otherwise we calculate the bound
|
||||
(let [filter-bounds (->> filters
|
||||
(filter #(= :drop-shadow (:type %)))
|
||||
(map (partial filter-bounds shape)))
|
||||
;; We add the selrect so the minimum size will be the selrect
|
||||
filter-bounds (conj filter-bounds (-> shape :points gsh/points->selrect))
|
||||
|
||||
x1 (apply min (map :x1 filter-bounds))
|
||||
y1 (apply min (map :y1 filter-bounds))
|
||||
x2 (apply max (map :x2 filter-bounds))
|
||||
y2 (apply max (map :y2 filter-bounds))
|
||||
|
||||
x1 (- x1 (* blur-value 2))
|
||||
x2 (+ x2 (* blur-value 2))
|
||||
y1 (- y1 (* blur-value 2))
|
||||
y2 (+ y2 (* blur-value 2))]
|
||||
|
||||
;; We should move the frame filter coordinates because they should be
|
||||
;; relative with the frame. By default they come as absolute
|
||||
{:x x1
|
||||
:y y1
|
||||
:width (- x2 x1)
|
||||
:height (- y2 y1)})))))
|
||||
|
||||
(defn calculate-padding
|
||||
([shape]
|
||||
(calculate-padding shape false))
|
||||
|
||||
([shape ignore-margin?]
|
||||
(let [stroke-width (apply max 0 (map #(case (:stroke-alignment % :center)
|
||||
:center (/ (:stroke-width % 0) 2)
|
||||
:outer (:stroke-width % 0)
|
||||
0) (:strokes shape)))
|
||||
|
||||
margin (if ignore-margin?
|
||||
0
|
||||
(apply max 0 (map #(gsh/shape-stroke-margin % stroke-width) (:strokes shape))))
|
||||
|
||||
shadow-width (apply max 0 (map #(case (:style % :drop-shadow)
|
||||
:drop-shadow (+ (mth/abs (:offset-x %)) (* (:spread %) 2) (* (:blur %) 2) 10)
|
||||
0) (:shadow shape)))
|
||||
|
||||
shadow-height (apply max 0 (map #(case (:style % :drop-shadow)
|
||||
:drop-shadow (+ (mth/abs (:offset-y %)) (* (:spread %) 2) (* (:blur %) 2) 10)
|
||||
0) (:shadow shape)))]
|
||||
|
||||
{:horizontal (+ stroke-width margin shadow-width)
|
||||
:vertical (+ stroke-width margin shadow-height)})))
|
||||
|
||||
(defn change-filter-in
|
||||
"Adds the previous filter as `filter-in` parameter"
|
||||
[filters]
|
||||
|
@ -234,9 +127,9 @@
|
|||
(mf/defc filters
|
||||
[{:keys [filter-id shape]}]
|
||||
|
||||
(let [filters (-> shape shape->filters change-filter-in)
|
||||
bounds (get-filters-bounds shape filters (or (-> shape :blur :value) 0))
|
||||
padding (calculate-padding shape)
|
||||
(let [filters (-> shape gsb/shape->filters change-filter-in)
|
||||
bounds (gsb/get-rect-filter-bounds (:selrect shape) filters (or (-> shape :blur :value) 0))
|
||||
padding (gsb/calculate-padding shape)
|
||||
selrect (:selrect shape)
|
||||
filter-x (/ (- (:x bounds) (:x selrect) (:horizontal padding)) (:width selrect))
|
||||
filter-y (/ (- (:y bounds) (:y selrect) (:vertical padding)) (:height selrect))
|
||||
|
|
|
@ -10,7 +10,7 @@
|
|||
[app.common.data.macros :as dm]
|
||||
[app.common.geom.matrix :as gmt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.main.ui.shapes.filters :as f]
|
||||
[app.common.geom.shapes.bounds :as gsb]
|
||||
[app.util.svg :as usvg]
|
||||
[rumext.alpha :as mf]))
|
||||
|
||||
|
@ -87,7 +87,7 @@
|
|||
(d/parse-double (get-in svg-def [:attrs :width]))
|
||||
(d/parse-double (get-in svg-def [:attrs :height])))
|
||||
(gsh/transform-rect transform))
|
||||
(f/get-filters-bounds shape))))
|
||||
(gsb/get-shape-filter-bounds shape))))
|
||||
|
||||
(mf/defc svg-defs [{:keys [shape render-id]}]
|
||||
(let [svg-defs (:svg-defs shape)
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
[app.common.data.macros :as dm]
|
||||
[app.common.exceptions :as ex]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.geom.shapes :as gsh]
|
||||
[app.common.geom.shapes.bounds :as gsb]
|
||||
[app.common.pages.helpers :as cph]
|
||||
[app.common.text :as txt]
|
||||
[app.main.data.comments :as dcm]
|
||||
|
@ -23,7 +23,6 @@
|
|||
[app.main.ui.context :as ctx]
|
||||
[app.main.ui.hooks :as hooks]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.main.ui.shapes.filters :as filters]
|
||||
[app.main.ui.share-link]
|
||||
[app.main.ui.static :as static]
|
||||
[app.main.ui.viewer.comments :refer [comments-layer comments-sidebar]]
|
||||
|
@ -39,16 +38,8 @@
|
|||
[rumext.alpha :as mf]))
|
||||
|
||||
(defn- calculate-size
|
||||
[frame zoom bounds]
|
||||
(let [frame-bounds (filters/get-filters-bounds frame)
|
||||
{:keys [x y width height]} (if (:show-content frame)
|
||||
(gsh/join-rects [bounds frame-bounds])
|
||||
frame-bounds)
|
||||
padding (filters/calculate-padding frame)
|
||||
x (- x (:horizontal padding))
|
||||
y (- y (:vertical padding))
|
||||
width (+ width (* 2 (:horizontal padding)))
|
||||
height (+ height (* 2 (:vertical padding)))]
|
||||
[objects frame zoom]
|
||||
(let [{:keys [x y width height]} (gsb/get-object-bounds objects frame)]
|
||||
{:base-width width
|
||||
:base-height height
|
||||
:x x
|
||||
|
@ -83,10 +74,9 @@
|
|||
[:div.counter (str/join " / " [(+ index 1) num-frames])]
|
||||
[:span]]])
|
||||
|
||||
|
||||
(mf/defc viewer-wrapper
|
||||
[{:keys [wrapper-size scroll orig-frame orig-viewport-ref orig-size page file users current-viewport-ref
|
||||
size frame interactions-mode overlays zoom close-overlay section index children-bounds] :as props}]
|
||||
size frame interactions-mode overlays zoom close-overlay section index] :as props}]
|
||||
(let [{clist :list} (mf/deref refs/comments-local)
|
||||
show-comments-list (and (= section :comments) (= :show clist))]
|
||||
[:*
|
||||
|
@ -135,7 +125,7 @@
|
|||
:interactions-mode interactions-mode}]
|
||||
|
||||
(for [overlay overlays]
|
||||
(let [size-over (calculate-size (:frame overlay) zoom children-bounds)]
|
||||
(let [size-over (calculate-size (:objects page) (:frame overlay) zoom)]
|
||||
[:*
|
||||
(when (or (:close-click-outside overlay)
|
||||
(:background-overlay overlay))
|
||||
|
@ -204,12 +194,6 @@
|
|||
frames (:frames page)
|
||||
frame (get frames index)
|
||||
|
||||
children-bounds
|
||||
(mf/use-memo
|
||||
(mf/deps page (:id frame))
|
||||
#(-> (cph/get-children (:objects page) (:id frame))
|
||||
(gsh/selection-rect)))
|
||||
|
||||
fullscreen? (mf/deref refs/viewer-fullscreen?)
|
||||
overlays (:overlays local)
|
||||
scroll (mf/use-state nil)
|
||||
|
@ -219,13 +203,13 @@
|
|||
(d/seek #(= (:id %) (:orig-frame-id current-animation)) frames))
|
||||
|
||||
size (mf/use-memo
|
||||
(mf/deps frame zoom children-bounds)
|
||||
(fn [] (calculate-size frame zoom children-bounds)))
|
||||
(mf/deps frame zoom)
|
||||
(fn [] (calculate-size (:objects page) frame zoom)))
|
||||
|
||||
orig-size (mf/use-memo
|
||||
(mf/deps orig-frame zoom)
|
||||
(fn [] (when orig-frame
|
||||
(calculate-size orig-frame zoom children-bounds))))
|
||||
(calculate-size (:objects page) orig-frame zoom))))
|
||||
|
||||
wrapper-size (mf/use-memo
|
||||
(mf/deps size orig-size zoom)
|
||||
|
@ -318,7 +302,7 @@
|
|||
wrapper-size)))))
|
||||
|
||||
(mf/use-layout-effect
|
||||
(mf/deps current-animation children-bounds)
|
||||
(mf/deps current-animation)
|
||||
(fn []
|
||||
;; Overlay animations may be started when needed.
|
||||
(when current-animation
|
||||
|
@ -328,7 +312,7 @@
|
|||
(let [overlay-viewport (dom/get-element (str "overlay-" (str (:overlay-id current-animation))))
|
||||
overlay (d/seek #(= (:id (:frame %)) (:overlay-id current-animation))
|
||||
overlays)
|
||||
overlay-size (calculate-size (:frame overlay) zoom children-bounds)
|
||||
overlay-size (calculate-size (:objects page) (:frame overlay) zoom)
|
||||
overlay-position {:x (* (:x (:position overlay)) zoom)
|
||||
:y (* (:y (:position overlay)) zoom)}]
|
||||
(interactions/animate-open-overlay
|
||||
|
@ -342,7 +326,7 @@
|
|||
(let [overlay-viewport (dom/get-element (str "overlay-" (str (:overlay-id current-animation))))
|
||||
overlay (d/seek #(= (:id (:frame %)) (:overlay-id current-animation))
|
||||
overlays)
|
||||
overlay-size (calculate-size (:frame overlay) zoom children-bounds)
|
||||
overlay-size (calculate-size (:objects page) (:frame overlay) zoom)
|
||||
overlay-position {:x (* (:x (:position overlay)) zoom)
|
||||
:y (* (:y (:position overlay)) zoom)}]
|
||||
(interactions/animate-close-overlay
|
||||
|
@ -427,8 +411,7 @@
|
|||
:overlays overlays
|
||||
:zoom zoom
|
||||
:section section
|
||||
:index index
|
||||
:children-bounds children-bounds}]))]]]))
|
||||
:index index}]))]]]))
|
||||
|
||||
;; --- Component: Viewer Page
|
||||
|
||||
|
|
|
@ -98,7 +98,7 @@
|
|||
state))
|
||||
|
||||
(mf/defc object-svg
|
||||
[{:keys [page-id file-id object-id render-embed? render-texts?]}]
|
||||
[{:keys [page-id file-id object-id render-embed?]}]
|
||||
(let [fetch-state (mf/use-fn
|
||||
(mf/deps file-id page-id object-id)
|
||||
(fn []
|
||||
|
@ -131,11 +131,10 @@
|
|||
[:& render/object-svg
|
||||
{:objects objects
|
||||
:object-id object-id
|
||||
:render-embed? render-embed?
|
||||
:render-texts? render-texts?}])))
|
||||
:render-embed? render-embed?}])))
|
||||
|
||||
(mf/defc objects-svg
|
||||
[{:keys [page-id file-id object-ids render-embed? render-texts?]}]
|
||||
[{:keys [page-id file-id object-ids render-embed?]}]
|
||||
(let [fetch-state (mf/use-fn
|
||||
(mf/deps file-id page-id)
|
||||
(fn []
|
||||
|
@ -157,27 +156,24 @@
|
|||
{:objects objects
|
||||
:key (str object-id)
|
||||
:object-id object-id
|
||||
:render-embed? render-embed?
|
||||
:render-texts? render-texts?}])))))
|
||||
:render-embed? render-embed?}])))))
|
||||
|
||||
(s/def ::page-id ::us/uuid)
|
||||
(s/def ::file-id ::us/uuid)
|
||||
(s/def ::object-id
|
||||
(s/or :single ::us/uuid
|
||||
:multiple (s/coll-of ::us/uuid)))
|
||||
(s/def ::render-text ::us/boolean)
|
||||
(s/def ::embed ::us/boolean)
|
||||
|
||||
(s/def ::render-objects
|
||||
(s/keys :req-un [::file-id ::page-id ::object-id]
|
||||
:opt-un [::render-text ::render-embed]))
|
||||
:opt-un [::render-embed]))
|
||||
|
||||
(defn- render-objects
|
||||
[params]
|
||||
(let [{:keys [file-id
|
||||
page-id
|
||||
render-embed
|
||||
render-texts]
|
||||
render-embed]
|
||||
:as params}
|
||||
(us/conform ::render-objects params)
|
||||
|
||||
|
@ -190,8 +186,7 @@
|
|||
{:file-id file-id
|
||||
:page-id page-id
|
||||
:object-id object-id
|
||||
:render-embed? render-embed
|
||||
:render-texts? render-texts}])
|
||||
:render-embed? render-embed}])
|
||||
|
||||
:multiple
|
||||
(mf/html
|
||||
|
@ -199,8 +194,7 @@
|
|||
{:file-id file-id
|
||||
:page-id page-id
|
||||
:object-ids (into #{} object-id)
|
||||
:render-embed? render-embed
|
||||
:render-texts? render-texts}]))))
|
||||
:render-embed? render-embed}]))))
|
||||
|
||||
;; ---- COMPONENTS SPRITE
|
||||
|
||||
|
|
Loading…
Reference in a new issue