mirror of
https://github.com/penpot/penpot.git
synced 2025-01-23 23:18:48 -05:00
🐛 Fixes problems with the picker for Safari and Firefox
This commit is contained in:
parent
ba529b9fd6
commit
264811c5ee
7 changed files with 158 additions and 127 deletions
33
frontend/resources/polyfills/createImageBitmap.js
Normal file
33
frontend/resources/polyfills/createImageBitmap.js
Normal file
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Safari and Edge polyfill for createImageBitmap
|
||||
* https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/createImageBitmap
|
||||
*
|
||||
* Support source image types Blob and ImageData.
|
||||
*
|
||||
* From: https://dev.to/nektro/createimagebitmap-polyfill-for-safari-and-edge-228
|
||||
* Updated by Yoan Tournade <yoan@ytotech.com>
|
||||
*/
|
||||
if (!('createImageBitmap' in window)) {
|
||||
window.createImageBitmap = async function (data) {
|
||||
return new Promise((resolve,reject) => {
|
||||
let dataURL;
|
||||
if (data instanceof Blob) {
|
||||
dataURL = URL.createObjectURL(data);
|
||||
} else if (data instanceof ImageData) {
|
||||
const canvas = document.createElement('canvas');
|
||||
const ctx = canvas.getContext('2d');
|
||||
canvas.width = data.width;
|
||||
canvas.height = data.height;
|
||||
ctx.putImageData(data,0,0);
|
||||
dataURL = canvas.toDataURL();
|
||||
} else {
|
||||
throw new Error('createImageBitmap does not handle the provided image source type');
|
||||
}
|
||||
const img = document.createElement('img');
|
||||
img.addEventListener('load',function () {
|
||||
resolve(this);
|
||||
});
|
||||
img.src = dataURL;
|
||||
});
|
||||
};
|
||||
}
|
|
@ -1621,8 +1621,7 @@
|
|||
(let [edition-id (get-in state [:workspace-local :edition])
|
||||
path-edit-mode (get-in state [:workspace-local :edit-path edition-id :edit-mode])]
|
||||
(if-not (= :draw path-edit-mode)
|
||||
(rx/of :interrupt
|
||||
(deselect-all true))
|
||||
(rx/of :interrupt (deselect-all true))
|
||||
(rx/empty))))))
|
||||
|
||||
(defn c-mod
|
||||
|
|
|
@ -64,7 +64,7 @@
|
|||
(dom/stop-propagation event)
|
||||
(st/emit! (modal/hide))
|
||||
(on-accept props))))
|
||||
key (events/listen (dom/get-root) EventType.KEYDOWN on-keydown)]
|
||||
key (events/listen js/document EventType.KEYDOWN on-keydown)]
|
||||
#(events/unlistenByKey key))))
|
||||
|
||||
[:div.modal-overlay
|
||||
|
|
|
@ -76,7 +76,7 @@
|
|||
(mf/deps allow-click-outside)
|
||||
(fn []
|
||||
(let [keys [(events/listen js/window EventType.POPSTATE on-pop-state)
|
||||
(events/listen (dom/get-root) EventType.KEYDOWN handle-keydown)
|
||||
(events/listen js/document EventType.KEYDOWN handle-keydown)
|
||||
(events/listen (dom/get-root) EventType.CLICK handle-click-outside)]]
|
||||
#(doseq [key keys]
|
||||
(events/unlistenByKey key)))))
|
||||
|
|
|
@ -54,106 +54,124 @@
|
|||
[:& shape-wrapper {:shape item
|
||||
:key (:id item)}]))]]))
|
||||
|
||||
(defn draw-picker-canvas [svg-node canvas-node]
|
||||
(let [canvas-context (.getContext canvas-node "2d")
|
||||
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)))
|
||||
|
||||
(mf/defc pixel-overlay
|
||||
{::mf/wrap-props false}
|
||||
[props]
|
||||
(let [vport (unchecked-get props "vport")
|
||||
vbox (unchecked-get props "vbox")
|
||||
(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))
|
||||
options (unchecked-get props "options")
|
||||
svg-ref (mf/use-ref nil)
|
||||
canvas-ref (mf/use-ref nil)
|
||||
img-ref (mf/use-ref nil)
|
||||
|
||||
update-canvas-stream (rx/subject)
|
||||
update-str (rx/subject)
|
||||
|
||||
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!))))
|
||||
(mf/use-callback
|
||||
(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)
|
||||
handle-mouse-move-picker
|
||||
(mf/use-callback
|
||||
(mf/deps viewport-ref)
|
||||
(fn [event]
|
||||
(when-let [zoom-view-node (.getElementById js/document "picker-detail")]
|
||||
(let [viewport-node (mf/ref-val viewport-ref)
|
||||
canvas-node (mf/ref-val canvas-ref)
|
||||
|
||||
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)
|
||||
{brx :left bry :top} (dom/get-bounding-rect viewport-node)
|
||||
x (- (.-clientX event) brx)
|
||||
y (- (.-clientY event) bry)
|
||||
|
||||
area-data (.getImageData canvas-context (- x 25) (- y 20) 50 40)]
|
||||
zoom-context (.getContext zoom-view-node "2d")
|
||||
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]))))))
|
||||
|
||||
(-> (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])))))
|
||||
handle-mouse-down-picker
|
||||
(mf/use-callback
|
||||
(fn [event]
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
(st/emit! (dwc/pick-color-select true (kbd/shift? event)))))
|
||||
|
||||
on-mouse-down-picker
|
||||
(fn [event]
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
(st/emit! (dwc/pick-color-select true (kbd/shift? event))))
|
||||
handle-mouse-up-picker
|
||||
(mf/use-callback
|
||||
(fn [event]
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
(st/emit! (dwc/stop-picker))
|
||||
(modal/disallow-click-outside!)))
|
||||
|
||||
on-mouse-up-picker
|
||||
(fn [event]
|
||||
(dom/prevent-default event)
|
||||
(dom/stop-propagation event)
|
||||
(st/emit! (dwc/stop-picker))
|
||||
(modal/disallow-click-outside!))]
|
||||
handle-image-load
|
||||
(mf/use-callback
|
||||
(mf/deps img-ref)
|
||||
(fn []
|
||||
(let [canvas-node (mf/ref-val canvas-ref)
|
||||
img-node (mf/ref-val img-ref)
|
||||
canvas-context (.getContext canvas-node "2d")]
|
||||
(.drawImage canvas-context img-node 0 0))))
|
||||
|
||||
handle-draw-picker-canvas
|
||||
(mf/use-callback
|
||||
(mf/deps img-ref)
|
||||
(fn []
|
||||
(let [img-node (mf/ref-val img-ref)
|
||||
svg-node (mf/ref-val svg-ref)
|
||||
xml (-> (js/XMLSerializer.)
|
||||
(.serializeToString svg-node)
|
||||
js/encodeURIComponent
|
||||
js/unescape
|
||||
js/btoa)
|
||||
img-src (str "data:image/svg+xml;base64," xml)]
|
||||
(obj/set! img-node "src" img-src))))
|
||||
|
||||
handle-svg-change
|
||||
(mf/use-callback
|
||||
(fn []
|
||||
(rx/push! update-str :update)))]
|
||||
|
||||
(mf/use-effect
|
||||
(fn []
|
||||
(let [listener (events/listen (dom/get-root) EventType.KEYDOWN handle-keydown)]
|
||||
(let [listener (events/listen js/document EventType.KEYDOWN handle-keydown)]
|
||||
#(events/unlistenByKey listener))))
|
||||
|
||||
(mf/use-effect
|
||||
(fn []
|
||||
(let [sub (->> update-canvas-stream
|
||||
(let [sub (->> update-str
|
||||
(rx/debounce 10)
|
||||
(rx/subs #(draw-picker-canvas (mf/ref-val svg-ref)
|
||||
(mf/ref-val canvas-ref))))]
|
||||
|
||||
(rx/subs handle-draw-picker-canvas))]
|
||||
#(rx/dispose! sub))))
|
||||
|
||||
(mf/use-effect
|
||||
(mf/deps svg-ref canvas-ref)
|
||||
(mf/deps svg-ref)
|
||||
(fn []
|
||||
(when (and svg-ref canvas-ref)
|
||||
|
||||
(let [config (clj->js {:attributes true
|
||||
:childList true
|
||||
:subtree true
|
||||
:characterData true})
|
||||
on-svg-change (fn [mutation-list] (rx/push! update-canvas-stream :update))
|
||||
observer (js/MutationObserver. on-svg-change)]
|
||||
|
||||
(.observe observer (mf/ref-val svg-ref) config)
|
||||
(when svg-ref
|
||||
(let [config #js {:attributes true
|
||||
:childList true
|
||||
:subtree true
|
||||
:characterData true}
|
||||
svg-node (mf/ref-val svg-ref)
|
||||
observer (js/MutationObserver. handle-svg-change)]
|
||||
(.observe observer svg-node config)
|
||||
(handle-svg-change)
|
||||
|
||||
;; Disconnect on unmount
|
||||
#(.disconnect observer)))))
|
||||
|
@ -167,21 +185,31 @@
|
|||
: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"}}]
|
||||
:on-mouse-down handle-mouse-down-picker
|
||||
:on-mouse-up handle-mouse-up-picker
|
||||
:on-mouse-move handle-mouse-move-picker}
|
||||
[:div {:style {:display "none"}}
|
||||
[:img {:ref img-ref
|
||||
:on-load handle-image-load
|
||||
:style {:position "absolute"
|
||||
:width "100%"
|
||||
:height "100%"}}]
|
||||
[:canvas {:ref canvas-ref
|
||||
:width (:width vport 0)
|
||||
:height (:height vport 0)
|
||||
:style {:position "absolute"
|
||||
:width "100%"
|
||||
:height "100%"}}]
|
||||
|
||||
[:& (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]]]]))
|
||||
[:& (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 {:position "absolute"
|
||||
:width "100%"
|
||||
:height "100%"
|
||||
:background-color (get options :background "#E8E9EA")}}
|
||||
[:& overlay-frames]]]]]]))
|
||||
|
|
|
@ -1,29 +0,0 @@
|
|||
;; 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.workspace.colorpicker.pixel-picker
|
||||
(: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]]
|
||||
[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.modal :as modal]
|
||||
[app.main.ui.icons :as i]
|
||||
[app.util.i18n :as i18n :refer [t]]))
|
||||
|
||||
|
|
@ -490,8 +490,8 @@
|
|||
(let [node (mf/ref-val viewport-ref)
|
||||
prnt (dom/get-parent node)
|
||||
|
||||
keys [(events/listen (dom/get-root) EventType.KEYDOWN on-key-down)
|
||||
(events/listen (dom/get-root) EventType.KEYUP on-key-up)
|
||||
keys [(events/listen js/document EventType.KEYDOWN on-key-down)
|
||||
(events/listen js/document EventType.KEYUP on-key-up)
|
||||
(events/listen node EventType.MOUSEMOVE on-mouse-move)
|
||||
;; bind with passive=false to allow the event to be cancelled
|
||||
;; https://stackoverflow.com/a/57582286/3219895
|
||||
|
|
Loading…
Add table
Reference in a new issue