mirror of
https://github.com/penpot/penpot.git
synced 2025-06-03 09:11:40 +02:00
✨ Fixes problems with pixel picker
This commit is contained in:
parent
245c39b1f6
commit
9f0a443b5c
10 changed files with 248 additions and 245 deletions
|
@ -297,10 +297,10 @@
|
|||
|
||||
|
||||
(s/def :internal.color/name ::string)
|
||||
(s/def :internal.color/value ::string)
|
||||
(s/def :internal.color/color ::string)
|
||||
(s/def :internal.color/opacity ::safe-number)
|
||||
(s/def :internal.color/gradient ::gradient)
|
||||
(s/def :internal.color/value (s/nilable ::string))
|
||||
(s/def :internal.color/color (s/nilable ::string))
|
||||
(s/def :internal.color/opacity (s/nilable ::safe-number))
|
||||
(s/def :internal.color/gradient (s/nilable ::gradient))
|
||||
|
||||
(s/def ::color
|
||||
(s/keys :req-un [::id
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 16 16">
|
||||
<path fill="#fff" d="M.607 13.076v2.401l2.224.025 7.86-7.405s-.05-.885-.253-1.087c-.202-.202-2.25-1.744-2.25-1.744z"/>
|
||||
<path fill="#000" d="M.343 15.974a.514.514 0 01-.317-.321c-.023-.07-.026-.23-.026-1.43 0-1.468-.001-1.445.09-1.586.02-.032 1.703-1.724 3.74-3.759a596.805 596.805 0 003.7-3.716c0-.009-.367-.384-.816-.833a29.9 29.9 0 01-.817-.833c0-.01.474-.49 1.054-1.07l1.053-1.053.948.946.947.947 1.417-1.413C12.366.806 12.765.418 12.856.357c.238-.161.52-.28.792-.334.17-.034.586-.03.76.008.801.173 1.41.794 1.57 1.603.03.15.03.569 0 .718a2.227 2.227 0 01-.334.793c-.061.09-.45.49-1.496 1.54L12.734 6.1l.947.948.947.947-1.053 1.054c-.58.58-1.061 1.054-1.07 1.054-.01 0-.384-.368-.833-.817-.45-.45-.824-.817-.834-.817-.009 0-1.68 1.666-3.716 3.701a493.093 493.093 0 01-3.759 3.74c-.14.091-.117.09-1.59.089-1.187 0-1.366-.004-1.43-.027zm6.024-4.633a592.723 592.723 0 003.663-3.68c0-.02-1.67-1.69-1.69-1.69-.01 0-1.666 1.648-3.68 3.663L.996 13.297v.834c0 .627.005.839.02.854.015.014.227.02.854.02h.833l3.664-3.664z"/>
|
||||
</svg>
|
||||
|
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 981 B |
|
@ -29,7 +29,7 @@
|
|||
border: none;
|
||||
cursor: pointer;
|
||||
|
||||
&.active,
|
||||
&.active svg,
|
||||
&:hover svg {
|
||||
fill: $color-primary;
|
||||
}
|
||||
|
|
|
@ -133,36 +133,6 @@
|
|||
(map #(dwt/update-text-attrs {:id % :editor (get editors %) :attrs attrs}) text-ids)
|
||||
(dwc/update-shapes shape-ids update-fn))))))))
|
||||
|
||||
#_(defn change-fill
|
||||
([ids color id file-id]
|
||||
(change-fill ids color 1 id file-id))
|
||||
([ids color opacity id file-id]
|
||||
(ptk/reify ::change-fill
|
||||
ptk/WatchEvent
|
||||
(watch [_ state s]
|
||||
(let [pid (:current-page-id state)
|
||||
objects (get-in state [:workspace-data :pages-index pid :objects])
|
||||
children (mapcat #(cph/get-children % objects) ids)
|
||||
ids (into ids children)
|
||||
|
||||
is-text? #(= :text (:type (get objects %)))
|
||||
text-ids (filter is-text? ids)
|
||||
shape-ids (filter (comp not is-text?) ids)
|
||||
|
||||
attrs (cond-> {:fill-color color
|
||||
:fill-color-ref-id id
|
||||
:fill-color-ref-file file-id}
|
||||
(and opacity (not= opacity :multiple)) (assoc :fill-opacity opacity))
|
||||
|
||||
update-fn (fn [shape] (merge shape attrs))
|
||||
editors (get-in state [:workspace-local :editors])
|
||||
reduce-fn (fn [state id]
|
||||
(update-in state [:workspace-data :pages-index pid :objects id] update-fn))]
|
||||
|
||||
(rx/from (conj
|
||||
(map #(dwt/update-text-attrs {:id % :editor (get editors %) :attrs attrs}) text-ids)
|
||||
(dwc/update-shapes shape-ids update-fn))))))))
|
||||
|
||||
(defn change-stroke [ids color]
|
||||
(ptk/reify ::change-stroke
|
||||
ptk/WatchEvent
|
||||
|
@ -185,49 +155,44 @@
|
|||
:stroke-opacity 1)))]
|
||||
(rx/of (dwc/update-shapes ids update-fn))))))
|
||||
|
||||
#_(defn change-stroke [ids color id file-id]
|
||||
(ptk/reify ::change-stroke
|
||||
ptk/WatchEvent
|
||||
(watch [_ state s]
|
||||
(let [objects (get-in state [:workspace-data :pages-index (:current-page-id state) :objects])
|
||||
children (mapcat #(cph/get-children % objects) ids)
|
||||
ids (into ids children)
|
||||
|
||||
update-fn (fn [s]
|
||||
(cond-> s
|
||||
true
|
||||
(assoc :stroke-color color
|
||||
:stroke-color-ref-id id
|
||||
:stroke-color-ref-file file-id)
|
||||
|
||||
(= (:stroke-style s) :none)
|
||||
(assoc :stroke-style :solid
|
||||
:stroke-width 1
|
||||
:stroke-opacity 1)))]
|
||||
(rx/of (dwc/update-shapes ids update-fn))))))
|
||||
|
||||
(defn picker-for-selected-shape []
|
||||
;; TODO: replace st/emit! by a subject push and set that in the WatchEvent
|
||||
(let [handle-change-color
|
||||
(fn [color shift?]
|
||||
(let [ids (get-in @st/state [:workspace-local :selected])]
|
||||
(st/emit!
|
||||
(if shift?
|
||||
(change-stroke ids color)
|
||||
(change-fill ids color))
|
||||
(md/hide))))]
|
||||
(ptk/reify ::start-picker
|
||||
(let [sub (rx/subject)]
|
||||
(ptk/reify ::picker-for-selected-shape
|
||||
ptk/WatchEvent
|
||||
(watch [_ state stream]
|
||||
(let [ids (get-in state [:workspace-local :selected])
|
||||
stop? (->> stream
|
||||
(rx/filter (ptk/type? ::stop-picker)))
|
||||
|
||||
update-events (fn [[color shift?]]
|
||||
(rx/of (if shift?
|
||||
(change-stroke ids color)
|
||||
(change-fill ids color))
|
||||
(stop-picker)))]
|
||||
(rx/merge
|
||||
;; Stream that updates the stroke/width and stops if `esc` pressed
|
||||
(->> sub
|
||||
(rx/take-until stop?)
|
||||
(rx/flat-map update-events))
|
||||
|
||||
;; Hide the modal if the stop event is emitted
|
||||
(->> stop?
|
||||
(rx/first)
|
||||
(rx/map #(md/hide))))))
|
||||
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
(assoc-in [:workspace-local :picking-color?] true)
|
||||
(assoc ::md/modal {:id (random-uuid)
|
||||
:type :colorpicker
|
||||
:props {:on-change handle-change-color}
|
||||
:allow-click-outside true}))))))
|
||||
(let [handle-change-color (fn [color shift?] (rx/push! sub [color shift?]))]
|
||||
(-> state
|
||||
(assoc-in [:workspace-local :picking-color?] true)
|
||||
(assoc ::md/modal {:id (random-uuid)
|
||||
:data {:color "#000000" :opacity 1}
|
||||
:type :colorpicker
|
||||
:props {:on-change handle-change-color}
|
||||
:allow-click-outside true})))))))
|
||||
|
||||
(defn select-gradient-stop [spot]
|
||||
(ptk/reify ::start-picker
|
||||
(ptk/reify ::select-gradient-stop
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(-> state
|
||||
|
|
|
@ -50,7 +50,9 @@
|
|||
(ptk/reify ::update-modal
|
||||
ptk/UpdateEvent
|
||||
(update [_ state]
|
||||
(c/update state ::modal merge options))))
|
||||
(cond-> state
|
||||
(::modal state)
|
||||
(c/update ::modal merge options)))))
|
||||
|
||||
(defn show!
|
||||
[type props]
|
||||
|
|
|
@ -20,10 +20,10 @@
|
|||
(:import goog.events.EventType))
|
||||
|
||||
(defn- on-esc-clicked
|
||||
[event]
|
||||
(when (k/esc? event)
|
||||
(st/emit! (dm/hide))
|
||||
(dom/stop-propagation event)))
|
||||
[event allow-click-outside]
|
||||
(when (and (k/esc? event) (not allow-click-outside))
|
||||
(do (dom/stop-propagation event)
|
||||
(st/emit! (dm/hide)))))
|
||||
|
||||
(defn- on-pop-state
|
||||
[event]
|
||||
|
@ -62,16 +62,23 @@
|
|||
(let [data (unchecked-get props "data")
|
||||
wrapper-ref (mf/use-ref nil)
|
||||
|
||||
allow-click-outside (:allow-click-outside data)
|
||||
|
||||
handle-click-outside
|
||||
(fn [event]
|
||||
(on-click-outside event wrapper-ref (:type data) (:allow-click-outside data)))]
|
||||
(on-click-outside event wrapper-ref (:type data) allow-click-outside))
|
||||
|
||||
handle-keydown
|
||||
(fn [event]
|
||||
(on-esc-clicked event allow-click-outside))]
|
||||
|
||||
(mf/use-layout-effect
|
||||
(mf/deps allow-click-outside)
|
||||
(fn []
|
||||
(let [keys [(events/listen js/document EventType.KEYDOWN on-esc-clicked)
|
||||
(let [keys [(events/listen js/document EventType.KEYDOWN handle-keydown)
|
||||
(events/listen js/window EventType.POPSTATE on-pop-state)
|
||||
(events/listen js/document EventType.CLICK handle-click-outside)]]
|
||||
#(for [key keys]
|
||||
#(doseq [key keys]
|
||||
(events/unlistenByKey key)))))
|
||||
|
||||
[:div.modal-wrapper {:ref wrapper-ref}
|
||||
|
|
|
@ -121,6 +121,9 @@
|
|||
|
||||
ref-picker (mf/use-ref)
|
||||
|
||||
dirty? (mf/use-var false)
|
||||
last-color (mf/use-var data)
|
||||
|
||||
picking-color? (mf/deref picking-color?)
|
||||
picked-color (mf/deref picked-color)
|
||||
picked-color-select (mf/deref picked-color-select)
|
||||
|
@ -128,8 +131,6 @@
|
|||
|
||||
editing-spot-state (mf/deref editing-spot-state-ref)
|
||||
|
||||
;; data-ref (mf/use-var data)
|
||||
|
||||
current-color (:current-color @state)
|
||||
|
||||
change-tab
|
||||
|
@ -140,15 +141,9 @@
|
|||
(fn [changes]
|
||||
(let [editing-stop (:editing-stop @state)]
|
||||
(swap! state #(cond-> %
|
||||
true (update :current-color merge changes)
|
||||
editing-stop (update-in [:stops editing-stop] merge changes)))
|
||||
|
||||
#_(when (:hex changes)
|
||||
(reset! value-ref (:hex changes)))
|
||||
|
||||
;; TODO: CHANGE TO SUPPORT GRADIENTS
|
||||
#_(on-change (:hex changes (:hex current-color))
|
||||
(:alpha changes (:alpha current-color)))))
|
||||
true (update :current-color merge changes)
|
||||
editing-stop (update-in [:stops editing-stop] merge changes)))
|
||||
(reset! dirty? true)))
|
||||
|
||||
handle-change-stop
|
||||
(fn [offset]
|
||||
|
@ -160,11 +155,15 @@
|
|||
(st/emit! (dc/select-gradient-stop offset)))))
|
||||
|
||||
on-select-library-color
|
||||
(fn [color] (prn "color" color))
|
||||
(fn [color] (reset! state (data->state color)))
|
||||
|
||||
on-add-library-color
|
||||
(fn [color] (st/emit! (dwl/add-color (state->data @state))))
|
||||
|
||||
on-activate-gradient
|
||||
(fn [type]
|
||||
(fn []
|
||||
(reset! dirty? true)
|
||||
(if (= type (:type @state))
|
||||
(do
|
||||
(swap! state assoc :type :color)
|
||||
|
@ -179,13 +178,6 @@
|
|||
1 (-> (:current-color @state)
|
||||
(assoc :alpha 0))}))))))]
|
||||
|
||||
;; Update state when there is a change in the props upstream
|
||||
;; TODO: Review for gradients
|
||||
#_(mf/use-effect
|
||||
(mf/deps value opacity)
|
||||
(fn []
|
||||
(swap! state assoc current-color (as-color-components value opacity))))
|
||||
|
||||
;; Updates the CSS color variable when there is a change in the color
|
||||
(mf/use-effect
|
||||
(mf/deps current-color)
|
||||
|
@ -208,48 +200,47 @@
|
|||
|
||||
;; When closing the modal we update the recent-color list
|
||||
(mf/use-effect
|
||||
(fn [] (fn []
|
||||
(st/emit! (dc/stop-picker))
|
||||
(st/emit! (dwl/add-recent-color (state->data @state))))))
|
||||
#(fn []
|
||||
(st/emit! (dc/stop-picker))
|
||||
(when @last-color
|
||||
(st/emit! (dwl/add-recent-color @last-color)))))
|
||||
|
||||
;; Updates color when used el pixel picker
|
||||
(mf/use-effect
|
||||
(mf/deps picking-color? picked-color)
|
||||
(mf/deps picking-color? picked-color-select)
|
||||
(fn []
|
||||
(when picking-color?
|
||||
(let [[r g b] (or picked-color [0 0 0])
|
||||
(when (and picking-color? picked-color-select)
|
||||
(let [[r g b alpha] picked-color
|
||||
hex (uc/rgb->hex [r g b])
|
||||
[h s v] (uc/hex->hsv hex)]
|
||||
(handle-change-color {:hex hex
|
||||
:r r :g g :b b
|
||||
:h h :s s :v v
|
||||
:alpha alpha})))))
|
||||
|
||||
(swap! state update :current-color assoc
|
||||
:r r :g g :b b
|
||||
:h h :s s :v v
|
||||
:hex hex)
|
||||
|
||||
;; TODO: UPDATE TO USE GRADIENTS
|
||||
#_(reset! value-ref hex)
|
||||
#_(when picked-color-select
|
||||
(on-change hex (:alpha current-color) nil nil picked-shift?))))))
|
||||
|
||||
;; TODO: UPDATE TO USE GRADIENTS
|
||||
#_(mf/use-effect
|
||||
(mf/deps picking-color? picked-color-select)
|
||||
(fn [] (when (and picking-color? picked-color-select)
|
||||
(on-change (:hex current-color) (:alpha current-color) nil nil picked-shift?))))
|
||||
|
||||
;; Changes when another gradient handler is selected
|
||||
(mf/use-effect
|
||||
(mf/deps editing-spot-state)
|
||||
#(when (not= editing-spot-state (:editing-stop @state))
|
||||
(handle-change-stop (or editing-spot-state 0))))
|
||||
|
||||
;; Changes on the viewport when moving a gradient handler
|
||||
(mf/use-effect
|
||||
(mf/deps data)
|
||||
#(if-let [gradient-data (-> data data->state :gradient-data)]
|
||||
(swap! state assoc :gradient-data gradient-data)))
|
||||
(do
|
||||
(reset! dirty? true)
|
||||
(swap! state assoc :gradient-data gradient-data))))
|
||||
|
||||
;; Send the properties to the store
|
||||
(mf/use-effect
|
||||
(mf/deps @state)
|
||||
(fn []
|
||||
(on-change (state->data @state))))
|
||||
(if @dirty?
|
||||
(let [color (state->data @state)]
|
||||
(reset! dirty? false)
|
||||
(reset! last-color color)
|
||||
(on-change color)))))
|
||||
|
||||
[:div.colorpicker {:ref ref-picker}
|
||||
[:div.colorpicker-content
|
||||
|
@ -306,7 +297,8 @@
|
|||
:on-change handle-change-color}]
|
||||
|
||||
[:& libraries {:current-color current-color
|
||||
:on-select-color on-select-library-color}]
|
||||
:on-select-color on-select-library-color
|
||||
:on-add-library-color on-add-library-color}]
|
||||
|
||||
(when on-accept
|
||||
[:div.actions
|
||||
|
|
|
@ -32,7 +32,7 @@
|
|||
[app.main.ui.workspace.colorpicker.ramp :refer [ramp-selector]]
|
||||
[app.main.ui.workspace.colorpicker.color-inputs :refer [color-inputs]]))
|
||||
|
||||
(mf/defc libraries [{:keys [current-color on-select-color]}]
|
||||
(mf/defc libraries [{:keys [current-color on-select-color on-add-library-color]}]
|
||||
(let [selected-library (mf/use-state "recent")
|
||||
current-library-colors (mf/use-state [])
|
||||
|
||||
|
@ -69,7 +69,7 @@
|
|||
(mf/use-effect
|
||||
(mf/deps file-colors)
|
||||
(fn [] (when (= @selected-library "file")
|
||||
(let [colors (map #(select-keys % [:id :value]) (vals file-colors))]
|
||||
(let [colors (vals file-colors)]
|
||||
(reset! current-library-colors (into [] colors))))))
|
||||
|
||||
|
||||
|
@ -88,7 +88,7 @@
|
|||
[:div.selected-colors
|
||||
(when (= "file" @selected-library)
|
||||
[:div.color-bullet.button.plus-button {:style {:background-color "white"}
|
||||
:on-click #(st/emit! (dwl/add-color current-color))}
|
||||
:on-click on-add-library-color}
|
||||
i/plus])
|
||||
|
||||
[:div.color-bullet.button {:style {:background-color "white"}
|
||||
|
|
|
@ -10,20 +10,157 @@
|
|||
(ns app.main.ui.workspace.colorpicker.pixel-overlay
|
||||
(:require
|
||||
[rumext.alpha :as mf]
|
||||
[okulary.core :as l]
|
||||
[cuerdas.core :as str]
|
||||
[app.common.geom.point :as gpt]
|
||||
[app.common.math :as math]
|
||||
[app.common.uuid :refer [uuid]]
|
||||
[okulary.core :as l]
|
||||
[promesa.core :as p]
|
||||
[goog.events :as events]
|
||||
[app.common.uuid :as uuid]
|
||||
[app.util.timers :as timers]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.color :as uc]
|
||||
[app.util.object :as obj]
|
||||
[app.main.store :as st]
|
||||
[app.main.refs :as refs]
|
||||
[app.main.data.workspace.libraries :as dwl]
|
||||
[app.main.data.colors :as dc]
|
||||
[app.main.data.colors :as dwc]
|
||||
[app.main.data.fetch :as mdf]
|
||||
[app.main.data.modal :as modal]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.util.i18n :as i18n :refer [t]]))
|
||||
[app.main.refs :as refs]
|
||||
[app.main.store :as st]
|
||||
[app.main.ui.context :as muc]
|
||||
[app.main.ui.cursors :as cur]
|
||||
[app.main.ui.keyboard :as kbd]
|
||||
[app.main.ui.workspace.shapes :refer [shape-wrapper frame-wrapper]])
|
||||
(:import goog.events.EventType))
|
||||
|
||||
(defn format-viewbox [vbox]
|
||||
(str/join " " [(+ (:x vbox 0) (:left-offset vbox 0))
|
||||
(:y vbox 0)
|
||||
(:width vbox 0)
|
||||
(:height vbox 0)]))
|
||||
|
||||
(mf/defc overlay-frames
|
||||
{::mf/wrap [mf/memo]
|
||||
::mf/wrap-props false}
|
||||
[]
|
||||
(let [data (mf/deref refs/workspace-page)
|
||||
objects (:objects data)
|
||||
root (get objects uuid/zero)
|
||||
shapes (->> (:shapes root) (map #(get objects %)))]
|
||||
[:*
|
||||
[:g.shapes
|
||||
(for [item shapes]
|
||||
(if (= (:type item) :frame)
|
||||
[:& frame-wrapper {:shape item
|
||||
:key (:id item)
|
||||
:objects objects}]
|
||||
[:& shape-wrapper {:shape item
|
||||
:key (:id item)}]))]]))
|
||||
|
||||
(mf/defc pixel-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))
|
||||
|
||||
handle-keydown
|
||||
(fn [event]
|
||||
(when (and (kbd/esc? event))
|
||||
(do (dom/stop-propagation event)
|
||||
(dom/prevent-default event)
|
||||
(st/emit! (dwc/stop-picker))
|
||||
(modal/disallow-click-outside!))))
|
||||
|
||||
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
|
||||
(fn []
|
||||
(let [listener (events/listen js/document EventType.KEYDOWN handle-keydown)]
|
||||
#(events/unlistenByKey listener))))
|
||||
|
||||
(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
|
||||
(let [canvas-node (mf/ref-val canvas-ref)
|
||||
canvas-context (.getContext canvas-node "2d")
|
||||
svg-node (mf/ref-val svg-ref)]
|
||||
(timers/schedule 100
|
||||
#(let [xml (.serializeToString (js/XMLSerializer.) svg-node)
|
||||
img-src (str "data:image/svg+xml;base64,"
|
||||
(-> xml js/encodeURIComponent js/unescape js/btoa))
|
||||
img (js/Image.)
|
||||
on-error (fn [err] (.error js/console "ERROR" err))
|
||||
on-load (fn [] (.drawImage canvas-context img 0 0))]
|
||||
(.addEventListener img "error" on-error)
|
||||
(.addEventListener img "load" on-load)
|
||||
(obj/set! img "src" img-src))))
|
||||
(catch :default e (.error js/console e)))))
|
||||
|
||||
[:*
|
||||
[:div.overlay
|
||||
{:tab-index 0
|
||||
: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")}}
|
||||
[:& overlay-frames]]]]))
|
||||
|
|
|
@ -41,6 +41,7 @@
|
|||
[app.main.ui.workspace.frame-grid :refer [frame-grid]]
|
||||
[app.main.ui.workspace.shapes.outline :refer [outline]]
|
||||
[app.main.ui.workspace.gradients :refer [gradient-handlers]]
|
||||
[app.main.ui.workspace.colorpicker.pixel-overlay :refer [pixel-overlay]]
|
||||
[app.common.math :as mth]
|
||||
[app.util.dom :as dom]
|
||||
[app.util.dom.dnd :as dnd]
|
||||
|
@ -185,104 +186,6 @@
|
|||
(: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
|
||||
(let [canvas-node (mf/ref-val canvas-ref)
|
||||
canvas-context (.getContext canvas-node "2d")
|
||||
svg-node (mf/ref-val svg-ref)]
|
||||
(timers/schedule 100
|
||||
#(let [xml (.serializeToString (js/XMLSerializer.) svg-node)
|
||||
img-src (str "data:image/svg+xml;base64,"
|
||||
(-> xml js/encodeURIComponent js/unescape js/btoa))
|
||||
img (js/Image.)
|
||||
on-error (fn [err] (.error js/console "ERROR" err))
|
||||
on-load (fn [] (.drawImage canvas-context img 0 0))]
|
||||
(.addEventListener img "error" on-error)
|
||||
(.addEventListener img "load" on-load)
|
||||
(obj/set! img "src" img-src))))
|
||||
(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
|
||||
[{:keys [page-id page local layout] :as props}]
|
||||
(let [{:keys [options-mode
|
||||
|
@ -310,8 +213,6 @@
|
|||
drawing-tool (:tool drawing)
|
||||
drawing-obj (:object drawing)
|
||||
|
||||
pick-color (mf/use-state [255 255 255 255])
|
||||
|
||||
zoom (or zoom 1)
|
||||
|
||||
on-mouse-down
|
||||
|
@ -587,11 +488,11 @@
|
|||
|
||||
[:*
|
||||
(when picking-color?
|
||||
[:& pixel-picker-overlay {:vport vport
|
||||
:vbox vbox
|
||||
:viewport-ref viewport-ref
|
||||
:options options
|
||||
:layout layout}])
|
||||
[:& pixel-overlay {:vport vport
|
||||
:vbox vbox
|
||||
:viewport-ref viewport-ref
|
||||
:options options
|
||||
:layout layout}])
|
||||
|
||||
[:svg.viewport
|
||||
{:preserveAspectRatio "xMidYMid meet"
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue