0
Fork 0
mirror of https://github.com/penpot/penpot.git synced 2025-03-15 17:21:17 -05:00

Merge pull request #2826 from penpot/alotor-thumbnails-performance

Thumbnails performance
This commit is contained in:
Alejandro 2023-01-24 09:59:20 +01:00 committed by GitHub
commit 88e2a5c56e
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
3 changed files with 37 additions and 29 deletions

View file

@ -28,22 +28,27 @@
(rx/filter #(= % id)) (rx/filter #(= % id))
(rx/take 1))) (rx/take 1)))
(defn thumbnail-stream (defn thumbnail-canvas-blob-stream
[object-id] [object-id]
;; Look for the thumbnail canvas to send the data to the backend
(let [node (dom/query (dm/fmt "canvas.thumbnail-canvas[data-object-id='%'][data-ready='true']" object-id))
stopper (->> st/stream
(rx/filter (ptk/type? :app.main.data.workspace/finalize-page))
(rx/take 1))]
(if (some? node)
;; Success: we generate the blob (async call)
(rx/create (rx/create
(fn [subs] (fn [subs]
;; We look in the DOM a canvas that 1) matches the id and 2) that it's not empty
;; will be empty on first rendering before drawing the thumbnail and we don't want to store that
(let [node (dom/query (dm/fmt "canvas.thumbnail-canvas[data-object-id='%'][data-ready='true']" object-id))]
(if (some? node)
(.toBlob node (fn [blob] (.toBlob node (fn [blob]
(rx/push! subs blob) (rx/push! subs blob)
(rx/end! subs)) (rx/end! subs))
"image/png") "image/png")))
;; If we cannot find the node we send `nil` and the upsert will delete the thumbnail ;; Not found, we retry after delay
(do (rx/push! subs nil) (->> (rx/timer 250)
(rx/end! subs))))))) (rx/flat-map #(thumbnail-canvas-blob-stream object-id))
(rx/take-until stopper)))))
(defn clear-thumbnail (defn clear-thumbnail
[page-id frame-id] [page-id frame-id]
@ -64,7 +69,7 @@
(watch [_ state _] (watch [_ state _]
(let [object-id (dm/str page-id frame-id) (let [object-id (dm/str page-id frame-id)
file-id (or file-id (:current-file-id state)) file-id (or file-id (:current-file-id state))
blob-result (thumbnail-stream object-id) blob-result (thumbnail-canvas-blob-stream object-id)
params {:file-id file-id :object-id object-id :data nil}] params {:file-id file-id :object-id object-id :data nil}]
(rx/concat (rx/concat
@ -80,7 +85,7 @@
(rx/merge-map (rx/merge-map
(fn [data] (fn [data]
(if (some? file-id) (if (and (some? data) (some? file-id))
(let [params (assoc params :data data)] (let [params (assoc params :data data)]
(rx/merge (rx/merge
;; Update the local copy of the thumbnails so we don't need to request it again ;; Update the local copy of the thumbnails so we don't need to request it again

View file

@ -16,7 +16,6 @@
[app.main.ui.hooks :as hooks] [app.main.ui.hooks :as hooks]
[app.main.ui.shapes.frame :as frame] [app.main.ui.shapes.frame :as frame]
[app.util.dom :as dom] [app.util.dom :as dom]
[app.util.object :as obj]
[app.util.timers :as ts] [app.util.timers :as ts]
[beicon.core :as rx] [beicon.core :as rx]
[cuerdas.core :as str] [cuerdas.core :as str]
@ -116,28 +115,30 @@
(mf/use-callback (mf/use-callback
(fn generate-thumbnail [] (fn generate-thumbnail []
(try (try
;; When starting generating the canvas we mark it as not ready so its not send to back until
;; we have time to update it
(let [canvas-node (mf/ref-val frame-canvas-ref)]
(dom/set-property! canvas-node "data-ready" "false"))
(let [node @node-ref] (let [node @node-ref]
(if (dom/has-children? node) (if (dom/has-children? node)
;; The frame-content need to have children in order to generate the thumbnail ;; The frame-content need to have children in order to generate the thumbnail
(let [frame-html (dom/node->xml node) (let [style-node (dom/query (dm/str "#frame-container-" (:id shape) " style"))
style-node (dom/query (dm/str "#frame-container-" (:id shape) " style"))
style-str (or (-> style-node dom/node->xml) "")
{:keys [x y width height]} @shape-bb-ref {:keys [x y width height]} @shape-bb-ref
viewbox (dm/str x " " y " " width " " height) viewbox (dm/str x " " y " " width " " height)
svg-node ;; This is way faster than creating a node through the DOM API
(-> (dom/make-node "http://www.w3.org/2000/svg" "svg") svg-data
(dom/set-property! "version" "1.1") (dm/fmt "<svg xmlns=\"http://www.w3.org/2000/svg\" version=\"1.1\" viewBox=\"%\" width=\"%\" height=\"%\" fill=\"none\">% %</svg>"
(dom/set-property! "viewBox" viewbox) viewbox
(dom/set-property! "width" width) width
(dom/set-property! "height" height) height
(dom/set-property! "fill" "none") (if (some? style-node) (dom/node->xml style-node) "")
(obj/set! "innerHTML" (dm/str style-str frame-html))) (dom/node->xml node))
img-src blob (js/Blob. #js [svg-data] #js {:type "image/svg+xml;charset=utf-8"})
(-> svg-node dom/node->xml dom/svg->data-uri)]
img-src (.createObjectURL js/URL blob)]
(reset! image-url img-src)) (reset! image-url img-src))
;; Node not yet ready, we schedule a new generation ;; Node not yet ready, we schedule a new generation

View file

@ -643,8 +643,10 @@
:descent (.-fontBoundingBoxDescent measure)})) :descent (.-fontBoundingBoxDescent measure)}))
(defn clone-node (defn clone-node
[^js node] ([^js node]
(.cloneNode node)) (clone-node node true))
([^js node deep?]
(.cloneNode node deep?)))
(defn has-children? (defn has-children?
[^js node] [^js node]