0
Fork 0
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:
alonso.torres 2020-12-02 15:37:11 +01:00 committed by Andrey Antukh
parent ba529b9fd6
commit 264811c5ee
7 changed files with 158 additions and 127 deletions

View 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;
});
};
}

View file

@ -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

View file

@ -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

View file

@ -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)))))

View file

@ -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]]]]]]))

View file

@ -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]]))

View file

@ -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