mirror of
https://github.com/penpot/penpot.git
synced 2025-02-03 04:49:03 -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])
|
(let [edition-id (get-in state [:workspace-local :edition])
|
||||||
path-edit-mode (get-in state [:workspace-local :edit-path edition-id :edit-mode])]
|
path-edit-mode (get-in state [:workspace-local :edit-path edition-id :edit-mode])]
|
||||||
(if-not (= :draw path-edit-mode)
|
(if-not (= :draw path-edit-mode)
|
||||||
(rx/of :interrupt
|
(rx/of :interrupt (deselect-all true))
|
||||||
(deselect-all true))
|
|
||||||
(rx/empty))))))
|
(rx/empty))))))
|
||||||
|
|
||||||
(defn c-mod
|
(defn c-mod
|
||||||
|
|
|
@ -64,7 +64,7 @@
|
||||||
(dom/stop-propagation event)
|
(dom/stop-propagation event)
|
||||||
(st/emit! (modal/hide))
|
(st/emit! (modal/hide))
|
||||||
(on-accept props))))
|
(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))))
|
#(events/unlistenByKey key))))
|
||||||
|
|
||||||
[:div.modal-overlay
|
[:div.modal-overlay
|
||||||
|
|
|
@ -76,7 +76,7 @@
|
||||||
(mf/deps allow-click-outside)
|
(mf/deps allow-click-outside)
|
||||||
(fn []
|
(fn []
|
||||||
(let [keys [(events/listen js/window EventType.POPSTATE on-pop-state)
|
(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)]]
|
(events/listen (dom/get-root) EventType.CLICK handle-click-outside)]]
|
||||||
#(doseq [key keys]
|
#(doseq [key keys]
|
||||||
(events/unlistenByKey key)))))
|
(events/unlistenByKey key)))))
|
||||||
|
|
|
@ -54,106 +54,124 @@
|
||||||
[:& shape-wrapper {:shape item
|
[:& shape-wrapper {:shape item
|
||||||
:key (:id 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/defc pixel-overlay
|
||||||
{::mf/wrap-props false}
|
{::mf/wrap-props false}
|
||||||
[props]
|
[props]
|
||||||
(let [vport (unchecked-get props "vport")
|
(let [vport (unchecked-get props "vport")
|
||||||
vbox (unchecked-get props "vbox")
|
vbox (unchecked-get props "vbox")
|
||||||
viewport-ref (unchecked-get props "viewport-ref")
|
viewport-ref (unchecked-get props "viewport-ref")
|
||||||
options (unchecked-get props "options")
|
options (unchecked-get props "options")
|
||||||
svg-ref (mf/use-ref nil)
|
svg-ref (mf/use-ref nil)
|
||||||
canvas-ref (mf/use-ref nil)
|
canvas-ref (mf/use-ref nil)
|
||||||
fetch-pending (mf/deref (mdf/pending-ref))
|
img-ref (mf/use-ref nil)
|
||||||
|
|
||||||
update-canvas-stream (rx/subject)
|
update-str (rx/subject)
|
||||||
|
|
||||||
handle-keydown
|
handle-keydown
|
||||||
(fn [event]
|
(mf/use-callback
|
||||||
(when (and (kbd/esc? event))
|
(fn [event]
|
||||||
(do (dom/stop-propagation event)
|
(when (and (kbd/esc? event))
|
||||||
(dom/prevent-default event)
|
(do (dom/stop-propagation event)
|
||||||
(st/emit! (dwc/stop-picker))
|
(dom/prevent-default event)
|
||||||
(modal/disallow-click-outside!))))
|
(st/emit! (dwc/stop-picker))
|
||||||
|
(modal/disallow-click-outside!)))))
|
||||||
|
|
||||||
on-mouse-move-picker
|
handle-mouse-move-picker
|
||||||
(fn [event]
|
(mf/use-callback
|
||||||
(when-let [zoom-view-node (.getElementById js/document "picker-detail")]
|
(mf/deps viewport-ref)
|
||||||
(let [{brx :left bry :top} (dom/get-bounding-rect (mf/ref-val viewport-ref))
|
(fn [event]
|
||||||
x (- (.-clientX event) brx)
|
(when-let [zoom-view-node (.getElementById js/document "picker-detail")]
|
||||||
y (- (.-clientY event) bry)
|
(let [viewport-node (mf/ref-val viewport-ref)
|
||||||
|
canvas-node (mf/ref-val canvas-ref)
|
||||||
|
|
||||||
zoom-context (.getContext zoom-view-node "2d")
|
{brx :left bry :top} (dom/get-bounding-rect viewport-node)
|
||||||
canvas-node (mf/ref-val canvas-ref)
|
x (- (.-clientX event) brx)
|
||||||
canvas-context (.getContext canvas-node "2d")
|
y (- (.-clientY event) bry)
|
||||||
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)]
|
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)
|
handle-mouse-down-picker
|
||||||
(p/then (fn [image]
|
(mf/use-callback
|
||||||
;; Draw area
|
(fn [event]
|
||||||
(obj/set! zoom-context "imageSmoothingEnabled" false)
|
(dom/prevent-default event)
|
||||||
(.drawImage zoom-context image 0 0 200 160))))
|
(dom/stop-propagation event)
|
||||||
(st/emit! (dwc/pick-color [r g b a])))))
|
(st/emit! (dwc/pick-color-select true (kbd/shift? event)))))
|
||||||
|
|
||||||
on-mouse-down-picker
|
handle-mouse-up-picker
|
||||||
(fn [event]
|
(mf/use-callback
|
||||||
(dom/prevent-default event)
|
(fn [event]
|
||||||
(dom/stop-propagation event)
|
(dom/prevent-default event)
|
||||||
(st/emit! (dwc/pick-color-select true (kbd/shift? event))))
|
(dom/stop-propagation event)
|
||||||
|
(st/emit! (dwc/stop-picker))
|
||||||
|
(modal/disallow-click-outside!)))
|
||||||
|
|
||||||
on-mouse-up-picker
|
handle-image-load
|
||||||
(fn [event]
|
(mf/use-callback
|
||||||
(dom/prevent-default event)
|
(mf/deps img-ref)
|
||||||
(dom/stop-propagation event)
|
(fn []
|
||||||
(st/emit! (dwc/stop-picker))
|
(let [canvas-node (mf/ref-val canvas-ref)
|
||||||
(modal/disallow-click-outside!))]
|
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
|
(mf/use-effect
|
||||||
(fn []
|
(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))))
|
#(events/unlistenByKey listener))))
|
||||||
|
|
||||||
(mf/use-effect
|
(mf/use-effect
|
||||||
(fn []
|
(fn []
|
||||||
(let [sub (->> update-canvas-stream
|
(let [sub (->> update-str
|
||||||
(rx/debounce 10)
|
(rx/debounce 10)
|
||||||
(rx/subs #(draw-picker-canvas (mf/ref-val svg-ref)
|
(rx/subs handle-draw-picker-canvas))]
|
||||||
(mf/ref-val canvas-ref))))]
|
|
||||||
|
|
||||||
#(rx/dispose! sub))))
|
#(rx/dispose! sub))))
|
||||||
|
|
||||||
(mf/use-effect
|
(mf/use-effect
|
||||||
(mf/deps svg-ref canvas-ref)
|
(mf/deps svg-ref)
|
||||||
(fn []
|
(fn []
|
||||||
(when (and svg-ref canvas-ref)
|
(when svg-ref
|
||||||
|
(let [config #js {:attributes true
|
||||||
(let [config (clj->js {:attributes true
|
:childList true
|
||||||
:childList true
|
:subtree true
|
||||||
:subtree true
|
:characterData true}
|
||||||
:characterData true})
|
svg-node (mf/ref-val svg-ref)
|
||||||
on-svg-change (fn [mutation-list] (rx/push! update-canvas-stream :update))
|
observer (js/MutationObserver. handle-svg-change)]
|
||||||
observer (js/MutationObserver. on-svg-change)]
|
(.observe observer svg-node config)
|
||||||
|
(handle-svg-change)
|
||||||
(.observe observer (mf/ref-val svg-ref) config)
|
|
||||||
|
|
||||||
;; Disconnect on unmount
|
;; Disconnect on unmount
|
||||||
#(.disconnect observer)))))
|
#(.disconnect observer)))))
|
||||||
|
@ -167,21 +185,31 @@
|
||||||
:width "100%"
|
:width "100%"
|
||||||
:height "100%"
|
:height "100%"
|
||||||
:cursor cur/picker}
|
:cursor cur/picker}
|
||||||
:on-mouse-down on-mouse-down-picker
|
:on-mouse-down handle-mouse-down-picker
|
||||||
:on-mouse-up on-mouse-up-picker
|
:on-mouse-up handle-mouse-up-picker
|
||||||
:on-mouse-move on-mouse-move-picker}]
|
:on-mouse-move handle-mouse-move-picker}
|
||||||
[:canvas {:ref canvas-ref
|
[:div {:style {:display "none"}}
|
||||||
:width (:width vport 0)
|
[:img {:ref img-ref
|
||||||
:height (:height vport 0)
|
:on-load handle-image-load
|
||||||
:style {:display "none"}}]
|
: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}
|
[:& (mf/provider muc/embed-ctx) {:value true}
|
||||||
[:svg.viewport
|
[:svg.viewport
|
||||||
{:ref svg-ref
|
{:ref svg-ref
|
||||||
:preserveAspectRatio "xMidYMid meet"
|
:preserveAspectRatio "xMidYMid meet"
|
||||||
:width (:width vport 0)
|
:width (:width vport 0)
|
||||||
:height (:height vport 0)
|
:height (:height vport 0)
|
||||||
:view-box (format-viewbox vbox)
|
:view-box (format-viewbox vbox)
|
||||||
:style {:display "none"
|
:style {:position "absolute"
|
||||||
:background-color (get options :background "#E8E9EA")}}
|
:width "100%"
|
||||||
[:& overlay-frames]]]]))
|
: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)
|
(let [node (mf/ref-val viewport-ref)
|
||||||
prnt (dom/get-parent node)
|
prnt (dom/get-parent node)
|
||||||
|
|
||||||
keys [(events/listen (dom/get-root) EventType.KEYDOWN on-key-down)
|
keys [(events/listen js/document EventType.KEYDOWN on-key-down)
|
||||||
(events/listen (dom/get-root) EventType.KEYUP on-key-up)
|
(events/listen js/document EventType.KEYUP on-key-up)
|
||||||
(events/listen node EventType.MOUSEMOVE on-mouse-move)
|
(events/listen node EventType.MOUSEMOVE on-mouse-move)
|
||||||
;; bind with passive=false to allow the event to be cancelled
|
;; bind with passive=false to allow the event to be cancelled
|
||||||
;; https://stackoverflow.com/a/57582286/3219895
|
;; https://stackoverflow.com/a/57582286/3219895
|
||||||
|
|
Loading…
Add table
Reference in a new issue