mirror of
https://github.com/penpot/penpot.git
synced 2025-06-06 00:01:40 +02:00
🎉 Refactor, performance improvements
This commit is contained in:
parent
726cdb9a27
commit
e737ec0311
7 changed files with 165 additions and 98 deletions
|
@ -18,12 +18,12 @@ goog.scope(function() {
|
||||||
const self = app.common.uuid_impl;
|
const self = app.common.uuid_impl;
|
||||||
|
|
||||||
const fill = (() => {
|
const fill = (() => {
|
||||||
if (typeof window === "object") {
|
if (typeof window === "object" && typeof window.crypto !== "undefined") {
|
||||||
return (buf) => {
|
return (buf) => {
|
||||||
window.crypto.getRandomValues(buf);
|
window.crypto.getRandomValues(buf);
|
||||||
return buf;
|
return buf;
|
||||||
};
|
};
|
||||||
} else if (typeof self === "object") {
|
} else if (typeof self === "object" && typeof self.crypto !== "undefined") {
|
||||||
return (buf) => {
|
return (buf) => {
|
||||||
self.crypto.getRandomValues(buf);
|
self.crypto.getRandomValues(buf);
|
||||||
return buf;
|
return buf;
|
||||||
|
|
|
@ -10,13 +10,26 @@
|
||||||
(ns app.main.data.fetch
|
(ns app.main.data.fetch
|
||||||
(:require
|
(:require
|
||||||
[promesa.core :as p]
|
[promesa.core :as p]
|
||||||
[app.util.object :as obj]))
|
[potok.core :as ptk]
|
||||||
|
[okulary.core :as l]
|
||||||
|
[app.util.object :as obj]
|
||||||
|
[app.main.store :as st]))
|
||||||
|
|
||||||
|
(defn pending-ref []
|
||||||
|
(l/derived ::to-fetch st/state))
|
||||||
|
|
||||||
|
(defn add [to-fetch id]
|
||||||
|
(let [to-fetch (or to-fetch (hash-set))]
|
||||||
|
(conj to-fetch id)))
|
||||||
|
|
||||||
(defn fetch-as-data-uri [url]
|
(defn fetch-as-data-uri [url]
|
||||||
(-> (js/fetch url)
|
(let [id (random-uuid)]
|
||||||
(p/then (fn [res] (.blob res)))
|
(st/emit! (fn [state] (update state ::to-fetch add id)))
|
||||||
(p/then (fn [blob]
|
(-> (js/fetch url)
|
||||||
(let [reader (js/FileReader.)]
|
(p/then (fn [res] (.blob res)))
|
||||||
(p/create (fn [resolve reject]
|
(p/then (fn [blob]
|
||||||
(obj/set! reader "onload" #(resolve [url (.-result reader)]))
|
(let [reader (js/FileReader.)]
|
||||||
(.readAsDataURL reader blob))))))))
|
(p/create (fn [resolve reject]
|
||||||
|
(obj/set! reader "onload" #(resolve [url (.-result reader)]))
|
||||||
|
(.readAsDataURL reader blob))))))
|
||||||
|
(p/finally #(st/emit! (fn [state] (update state ::to-fetch disj id)))))))
|
||||||
|
|
14
frontend/src/app/main/ui/context.cljs
Normal file
14
frontend/src/app/main/ui/context.cljs
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
;; 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 UXBOX Labs SL
|
||||||
|
|
||||||
|
(ns app.main.ui.context
|
||||||
|
(:require
|
||||||
|
[rumext.alpha :as mf]))
|
||||||
|
|
||||||
|
(def embed-ctx (mf/create-context false))
|
|
@ -14,6 +14,7 @@
|
||||||
[app.common.geom.shapes :as geom]
|
[app.common.geom.shapes :as geom]
|
||||||
[app.main.ui.shapes.attrs :as attrs]
|
[app.main.ui.shapes.attrs :as attrs]
|
||||||
[app.util.object :as obj]
|
[app.util.object :as obj]
|
||||||
|
[app.main.ui.context :as muc]
|
||||||
[app.main.data.fetch :as df]
|
[app.main.data.fetch :as df]
|
||||||
[promesa.core :as p]))
|
[promesa.core :as p]))
|
||||||
|
|
||||||
|
@ -24,13 +25,15 @@
|
||||||
(let [shape (unchecked-get props "shape")
|
(let [shape (unchecked-get props "shape")
|
||||||
{:keys [id x y width height rotation metadata]} shape
|
{:keys [id x y width height rotation metadata]} shape
|
||||||
uri (cfg/resolve-media-path (:path metadata))
|
uri (cfg/resolve-media-path (:path metadata))
|
||||||
data-uri (mf/use-state nil)]
|
embed-resources? (mf/use-ctx muc/embed-ctx)
|
||||||
|
data-uri (mf/use-state (when (not embed-resources?) uri))]
|
||||||
|
|
||||||
(mf/use-effect
|
(mf/use-effect
|
||||||
(mf/deps shape)
|
(mf/deps uri)
|
||||||
(fn []
|
(fn []
|
||||||
(-> (df/fetch-as-data-uri uri)
|
(if embed-resources?
|
||||||
(p/then #(reset! data-uri (second %))))))
|
(-> (df/fetch-as-data-uri uri)
|
||||||
|
(p/then #(reset! data-uri (second %)))))))
|
||||||
|
|
||||||
(let [transform (geom/transform-matrix shape)
|
(let [transform (geom/transform-matrix shape)
|
||||||
props (-> (attrs/extract-style-attrs shape)
|
props (-> (attrs/extract-style-attrs shape)
|
||||||
|
@ -49,7 +52,4 @@
|
||||||
:stroke "#000000"})]
|
:stroke "#000000"})]
|
||||||
[:> "image" (obj/merge!
|
[:> "image" (obj/merge!
|
||||||
props
|
props
|
||||||
#js {:xlinkHref @data-uri})]))
|
#js {:href @data-uri})]))))
|
||||||
|
|
||||||
|
|
||||||
))
|
|
||||||
|
|
|
@ -6,16 +6,17 @@
|
||||||
|
|
||||||
(ns app.main.ui.shapes.text
|
(ns app.main.ui.shapes.text
|
||||||
(:require
|
(:require
|
||||||
|
[clojure.set :as set]
|
||||||
[promesa.core :as p]
|
[promesa.core :as p]
|
||||||
[cuerdas.core :as str]
|
[cuerdas.core :as str]
|
||||||
[rumext.alpha :as mf]
|
[rumext.alpha :as mf]
|
||||||
[app.main.data.fetch :as df]
|
[app.main.data.fetch :as df]
|
||||||
|
[app.main.fonts :as fonts]
|
||||||
|
[app.main.ui.context :as muc]
|
||||||
[app.common.data :as d]
|
[app.common.data :as d]
|
||||||
[app.common.geom.shapes :as geom]
|
[app.common.geom.shapes :as geom]
|
||||||
[app.common.geom.matrix :as gmt]
|
[app.common.geom.matrix :as gmt]
|
||||||
[app.main.fonts :as fonts]
|
[app.util.object :as obj]))
|
||||||
[app.util.object :as obj]
|
|
||||||
[clojure.set :as set]))
|
|
||||||
|
|
||||||
;; --- Text Editor Rendering
|
;; --- Text Editor Rendering
|
||||||
|
|
||||||
|
@ -114,11 +115,12 @@
|
||||||
([node] (render-text-node 0 node))
|
([node] (render-text-node 0 node))
|
||||||
([index {:keys [type text children] :as node}]
|
([index {:keys [type text children] :as node}]
|
||||||
(mf/html
|
(mf/html
|
||||||
(let [embeded-fonts (mf/use-state nil)]
|
(let [embed-resources? (mf/use-ctx muc/embed-ctx)
|
||||||
|
embeded-fonts (mf/use-state nil)]
|
||||||
(mf/use-effect
|
(mf/use-effect
|
||||||
(mf/deps node)
|
(mf/deps node)
|
||||||
(fn []
|
(fn []
|
||||||
(when (= type "root")
|
(when (and embed-resources? (= type "root"))
|
||||||
(let [font-to-embed (get-all-fonts node)
|
(let [font-to-embed (get-all-fonts node)
|
||||||
embeded (map embed-font font-to-embed)]
|
embeded (map embed-font font-to-embed)]
|
||||||
(-> (p/all embeded)
|
(-> (p/all embeded)
|
||||||
|
|
|
@ -19,7 +19,6 @@
|
||||||
[app.common.uuid :refer [uuid]]
|
[app.common.uuid :refer [uuid]]
|
||||||
[app.main.data.workspace.libraries :as dwl]
|
[app.main.data.workspace.libraries :as dwl]
|
||||||
[app.main.data.colors :as dwc]
|
[app.main.data.colors :as dwc]
|
||||||
#_[app.main.ui.modal :as modal]
|
|
||||||
[app.main.ui.modal :as modal]
|
[app.main.ui.modal :as modal]
|
||||||
[okulary.core :as l]
|
[okulary.core :as l]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
[app.main.data.workspace :as dw]
|
[app.main.data.workspace :as dw]
|
||||||
[app.main.data.workspace.drawing :as dd]
|
[app.main.data.workspace.drawing :as dd]
|
||||||
[app.main.data.colors :as dwc]
|
[app.main.data.colors :as dwc]
|
||||||
|
[app.main.data.fetch :as mdf]
|
||||||
[app.main.refs :as refs]
|
[app.main.refs :as refs]
|
||||||
[app.main.store :as st]
|
[app.main.store :as st]
|
||||||
[app.main.streams :as ms]
|
[app.main.streams :as ms]
|
||||||
|
@ -42,10 +43,12 @@
|
||||||
[app.util.dom :as dom]
|
[app.util.dom :as dom]
|
||||||
[app.util.dom.dnd :as dnd]
|
[app.util.dom.dnd :as dnd]
|
||||||
[app.util.object :as obj]
|
[app.util.object :as obj]
|
||||||
|
[app.main.ui.context :as muc]
|
||||||
[app.common.geom.shapes :as gsh]
|
[app.common.geom.shapes :as gsh]
|
||||||
[app.common.geom.point :as gpt]
|
[app.common.geom.point :as gpt]
|
||||||
[app.util.perf :as perf]
|
[app.util.perf :as perf]
|
||||||
[app.common.uuid :as uuid])
|
[app.common.uuid :as uuid]
|
||||||
|
[app.util.timers :as timers])
|
||||||
(:import goog.events.EventType))
|
(:import goog.events.EventType))
|
||||||
|
|
||||||
;; --- Coordinates Widget
|
;; --- Coordinates Widget
|
||||||
|
@ -161,6 +164,108 @@
|
||||||
:selected selected
|
:selected selected
|
||||||
:hover hover}]]))
|
:hover hover}]]))
|
||||||
|
|
||||||
|
(defn format-viewbox [vbox]
|
||||||
|
(str/join " " [(+ (:x vbox 0) (:left-offset vbox 0))
|
||||||
|
(:y vbox 0)
|
||||||
|
(:width vbox 0)
|
||||||
|
(:height vbox 0)]))
|
||||||
|
|
||||||
|
(mf/defc pixel-picker-overlay
|
||||||
|
{::mf/wrap-props false}
|
||||||
|
[props]
|
||||||
|
(let [vport (unchecked-get props "vport")
|
||||||
|
vbox (unchecked-get props "vbox")
|
||||||
|
viewport-ref (unchecked-get props "viewport-ref")
|
||||||
|
options (unchecked-get props "options")
|
||||||
|
svg-ref (mf/use-ref nil)
|
||||||
|
canvas-ref (mf/use-ref nil)
|
||||||
|
fetch-pending (mf/deref (mdf/pending-ref))
|
||||||
|
|
||||||
|
on-mouse-move-picker
|
||||||
|
(fn [event]
|
||||||
|
(when-let [zoom-view-node (.getElementById js/document "picker-detail")]
|
||||||
|
(let [{brx :left bry :top} (dom/get-bounding-rect (mf/ref-val viewport-ref))
|
||||||
|
x (- (.-clientX event) brx)
|
||||||
|
y (- (.-clientY event) bry)
|
||||||
|
|
||||||
|
zoom-context (.getContext zoom-view-node "2d")
|
||||||
|
canvas-node (mf/ref-val canvas-ref)
|
||||||
|
canvas-context (.getContext canvas-node "2d")
|
||||||
|
pixel-data (.getImageData canvas-context x y 1 1)
|
||||||
|
rgba (.-data pixel-data)
|
||||||
|
r (obj/get rgba 0)
|
||||||
|
g (obj/get rgba 1)
|
||||||
|
b (obj/get rgba 2)
|
||||||
|
a (obj/get rgba 3)
|
||||||
|
|
||||||
|
area-data (.getImageData canvas-context (- x 25) (- y 20) 50 40)]
|
||||||
|
|
||||||
|
(-> (js/createImageBitmap area-data)
|
||||||
|
(p/then (fn [image]
|
||||||
|
;; Draw area
|
||||||
|
(obj/set! zoom-context "imageSmoothingEnabled" false)
|
||||||
|
(.drawImage zoom-context image 0 0 200 160))))
|
||||||
|
(st/emit! (dwc/pick-color [r g b a])))))
|
||||||
|
|
||||||
|
on-mouse-down-picker
|
||||||
|
(fn [event]
|
||||||
|
(dom/prevent-default event)
|
||||||
|
(dom/stop-propagation event)
|
||||||
|
(st/emit! (dwc/pick-color-select true (kbd/shift? event))))
|
||||||
|
|
||||||
|
on-mouse-up-picker
|
||||||
|
(fn [event]
|
||||||
|
(dom/prevent-default event)
|
||||||
|
(dom/stop-propagation event)
|
||||||
|
(st/emit! (dwc/stop-picker))
|
||||||
|
(modal/disallow-click-outside!))]
|
||||||
|
|
||||||
|
(mf/use-effect
|
||||||
|
;; Everytime we finish retrieving a new URL we redraw the canvas
|
||||||
|
;; so even if we're not finished the user can start to pick basic
|
||||||
|
;; shapes
|
||||||
|
(mf/deps fetch-pending)
|
||||||
|
(fn []
|
||||||
|
(try
|
||||||
|
(timers/raf
|
||||||
|
#(let [svg-node (mf/ref-val svg-ref)
|
||||||
|
canvas-node (mf/ref-val canvas-ref)
|
||||||
|
canvas-context (.getContext canvas-node "2d")
|
||||||
|
xml (.serializeToString (js/XMLSerializer.) svg-node)
|
||||||
|
content (str "data:image/svg+xml;base64," (js/btoa xml))
|
||||||
|
img (js/Image.)]
|
||||||
|
(obj/set! img "onload"
|
||||||
|
(fn []
|
||||||
|
(.drawImage canvas-context img 0 0)))
|
||||||
|
(obj/set! img "src" content)))
|
||||||
|
(catch :default e (.error js/console e)))))
|
||||||
|
|
||||||
|
[:*
|
||||||
|
[:div.overlay
|
||||||
|
{:style {:position "absolute"
|
||||||
|
:top 0
|
||||||
|
:left 0
|
||||||
|
:width "100%"
|
||||||
|
:height "100%"
|
||||||
|
:cursor cur/picker}
|
||||||
|
:on-mouse-down on-mouse-down-picker
|
||||||
|
:on-mouse-up on-mouse-up-picker
|
||||||
|
:on-mouse-move on-mouse-move-picker}]
|
||||||
|
[:canvas {:ref canvas-ref
|
||||||
|
:width (:width vport 0)
|
||||||
|
:height (:height vport 0)
|
||||||
|
:style {:display "none"}}]
|
||||||
|
[:& (mf/provider muc/embed-ctx) {:value true}
|
||||||
|
[:svg.viewport
|
||||||
|
{:ref svg-ref
|
||||||
|
:preserveAspectRatio "xMidYMid meet"
|
||||||
|
:width (:width vport 0)
|
||||||
|
:height (:height vport 0)
|
||||||
|
:view-box (format-viewbox vbox)
|
||||||
|
:style {:display "none"
|
||||||
|
:background-color (get options :background "#E8E9EA")}}
|
||||||
|
[:& frames]]]]))
|
||||||
|
|
||||||
(mf/defc viewport
|
(mf/defc viewport
|
||||||
[{:keys [page-id page local layout] :as props}]
|
[{:keys [page-id page local layout] :as props}]
|
||||||
(let [{:keys [options-mode
|
(let [{:keys [options-mode
|
||||||
|
@ -176,7 +281,6 @@
|
||||||
|
|
||||||
file (mf/deref refs/workspace-file)
|
file (mf/deref refs/workspace-file)
|
||||||
viewport-ref (mf/use-ref nil)
|
viewport-ref (mf/use-ref nil)
|
||||||
canvas-ref (mf/use-ref nil)
|
|
||||||
zoom-view-ref (mf/use-ref nil)
|
zoom-view-ref (mf/use-ref nil)
|
||||||
last-position (mf/use-var nil)
|
last-position (mf/use-var nil)
|
||||||
drawing (mf/deref refs/workspace-drawing)
|
drawing (mf/deref refs/workspace-drawing)
|
||||||
|
@ -418,46 +522,7 @@
|
||||||
prnt (dom/get-parent node)]
|
prnt (dom/get-parent node)]
|
||||||
(st/emit! (dw/update-viewport-size (dom/get-client-size prnt)))))
|
(st/emit! (dw/update-viewport-size (dom/get-client-size prnt)))))
|
||||||
|
|
||||||
options (mf/deref refs/workspace-page-options)
|
options (mf/deref refs/workspace-page-options)]
|
||||||
|
|
||||||
on-mouse-move-picker
|
|
||||||
(fn [event]
|
|
||||||
(when-let [zoom-view-node (.getElementById js/document "picker-detail")]
|
|
||||||
(let [{brx :left bry :top} (dom/get-bounding-rect (mf/ref-val viewport-ref))
|
|
||||||
x (- (.-clientX event) brx)
|
|
||||||
y (- (.-clientY event) bry)
|
|
||||||
|
|
||||||
zoom-context (.getContext zoom-view-node "2d")
|
|
||||||
canvas-node (mf/ref-val canvas-ref)
|
|
||||||
canvas-context (.getContext canvas-node "2d")
|
|
||||||
pixel-data (.getImageData canvas-context x y 1 1)
|
|
||||||
rgba (.-data pixel-data)
|
|
||||||
r (obj/get rgba 0)
|
|
||||||
g (obj/get rgba 1)
|
|
||||||
b (obj/get rgba 2)
|
|
||||||
a (obj/get rgba 3)
|
|
||||||
|
|
||||||
area-data (.getImageData canvas-context (- x 25) (- y 20) 50 40)]
|
|
||||||
|
|
||||||
(-> (js/createImageBitmap area-data)
|
|
||||||
(p/then (fn [image]
|
|
||||||
;; Draw area
|
|
||||||
(obj/set! zoom-context "imageSmoothingEnabled" false)
|
|
||||||
(.drawImage zoom-context image 0 0 200 160))))
|
|
||||||
(st/emit! (dwc/pick-color [r g b a])))))
|
|
||||||
|
|
||||||
on-mouse-down-picker
|
|
||||||
(fn [event]
|
|
||||||
(dom/prevent-default event)
|
|
||||||
(dom/stop-propagation event)
|
|
||||||
(st/emit! (dwc/pick-color-select true (kbd/shift? event))))
|
|
||||||
|
|
||||||
on-mouse-up-picker
|
|
||||||
(fn [event]
|
|
||||||
(dom/prevent-default event)
|
|
||||||
(dom/stop-propagation event)
|
|
||||||
(st/emit! (dwc/stop-picker))
|
|
||||||
(modal/disallow-click-outside!))]
|
|
||||||
|
|
||||||
(mf/use-layout-effect
|
(mf/use-layout-effect
|
||||||
(fn []
|
(fn []
|
||||||
|
@ -481,44 +546,18 @@
|
||||||
|
|
||||||
(mf/use-layout-effect (mf/deps layout) on-resize)
|
(mf/use-layout-effect (mf/deps layout) on-resize)
|
||||||
|
|
||||||
(mf/use-effect
|
|
||||||
(mf/deps props)
|
|
||||||
(fn []
|
|
||||||
(when picking-color?
|
|
||||||
(try
|
|
||||||
(let [svg-node (mf/ref-val viewport-ref)
|
|
||||||
canvas-node (mf/ref-val canvas-ref)
|
|
||||||
canvas-context (.getContext canvas-node "2d")
|
|
||||||
xml (.serializeToString (js/XMLSerializer.) svg-node)
|
|
||||||
content (str "data:image/svg+xml;base64," (js/btoa xml))
|
|
||||||
img (js/Image.)]
|
|
||||||
(obj/set! img "onload"
|
|
||||||
(fn []
|
|
||||||
(.drawImage canvas-context img 0 0)))
|
|
||||||
(obj/set! img "src" content))
|
|
||||||
(catch :default e (.error js/console e))))))
|
|
||||||
|
|
||||||
[:*
|
[:*
|
||||||
|
|
||||||
(when picking-color?
|
(when picking-color?
|
||||||
[:canvas {:ref canvas-ref
|
[:& pixel-picker-overlay {:vport vport
|
||||||
:width (:width vport 0)
|
:vbox vbox
|
||||||
:height (:height vport 0)
|
:viewport-ref viewport-ref
|
||||||
:on-mouse-down on-mouse-down-picker
|
:options options}])
|
||||||
:on-mouse-up on-mouse-up-picker
|
|
||||||
:on-mouse-move on-mouse-move-picker
|
|
||||||
:style {:position "absolute"
|
|
||||||
:top 0
|
|
||||||
:left 0
|
|
||||||
:cursor cur/picker}}])
|
|
||||||
[:svg.viewport
|
[:svg.viewport
|
||||||
{:preserveAspectRatio "xMidYMid meet"
|
{:preserveAspectRatio "xMidYMid meet"
|
||||||
:width (:width vport 0)
|
:width (:width vport 0)
|
||||||
:height (:height vport 0)
|
:height (:height vport 0)
|
||||||
:view-box (str/join " " [(+ (:x vbox 0) (:left-offset vbox 0))
|
:view-box (format-viewbox vbox)
|
||||||
(:y vbox 0)
|
|
||||||
(:width vbox 0)
|
|
||||||
(:height vbox 0)])
|
|
||||||
:ref viewport-ref
|
:ref viewport-ref
|
||||||
:class (when drawing-tool "drawing")
|
:class (when drawing-tool "drawing")
|
||||||
:style {:cursor (cond
|
:style {:cursor (cond
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue