问题
I have the following code which takes as input some images and then compresses them using the compress-omg function, which takes a data-url of each image inputted in sequence and stores the compressed blobs in a db vector :images
(defn image-selector []
[:<>
;; appending image in this div
[:div {:id "test-div"}]
[:input {:type "file"
:multiple true
:id "input-image"
:on-change
(fn [e]
(let [files (array-seq (.. e -target -files))]
(doseq [file files]
;; must create a new file-reader in each iteration
(let [file-reader (js/FileReader.)]
(set! (.-onload file-reader)
(fn [e]
(let [data-url (-> e .-target .-result)]
(dispatch [:images (compress-img data-url)]))))
(.readAsDataURL file-reader file)))))}]])
(defn compress-img [data-url]
"takes data-url. Compresses it and returns a blob."
(let [image (js/Image.)
canvas (js/document.createElement "canvas")
ctx (.getContext canvas "2d")
blob (js/Blob. [data-url])
window-url (or (-> js/window .-URL) (-> js/window .-webkitURL ))
blob-url (.createObjectURL window-url blob)
]
(set! (.-onload image)
;; doesn't print
(fn [_]
(prn "image height" (.-height image))
(prn "image width " (.-width image))
(set! (.-src image) blob-url)
(.appendChild (js/document.getElementById "test-div") image)
(.drawImage ctx image 0 0 (.-width image) (.-height image))
;; approximating sizes of compressed and uncompressed images
(prn "size original (MB)" (* 0.75 0.000001 (.-length data-url)))
(prn "size upload (MB)" (* 0.75 0.000001 (.-length (.toDataURL canvas "image/png" 0.1))))
;; compressing and returning the blob
(js/Blob. [(.toDataURL canvas "image/png" 0.1)])
)
)
(set! (.-onerror image)
;; doesn't print
(fn [e]
(prn "error is " e)
(prn "error")))
))
But the onload and onerror events are never triggered. What am I doing wrong?
回答1:
In order to trigger either onload
or onerror
on Image
, you first need to (set! (.-src img) data-url)
.
Since you are trying to resize a data URL, here is how we do it in our codebase:
(defn clamp [^js image width heigth]
(let [img-width (.-width image)
img-height (.-height image)
ratio (/ img-width img-height)
portrait? (< img-width img-height)
w (if (< img-width width)
img-width
(if portrait?
width
(* width ratio)))
h (if (< img-height heigth)
img-height
(if portrait?
(/ heigth ratio)
heigth))]
[w h]))
(defn resize! [image width height callback]
(let [canvas (.createElement js/document "canvas")
ctx (.getContext canvas "2d")
[w h] (clamp image width height)]
(set! (.-width canvas) w)
(set! (.-height canvas) h)
(.drawImage ctx image 0 0 w h)
(callback (.toDataURL canvas))))
(defn resize-data-url! [uri width height callback]
(let [img (.createElement js/document "img")]
(set! (.-onload img) (fn [] (this-as image
(resize! image width height callback))))
(set! (.-src img) uri)
nil))
(defn with-image-data
"Asynchronously load an image `file` in a FileReader, then call the `callback`
fn with the FileReader's result and a DOM Image intance. Usefull to get the
image content as a DataUrl, image width and height. If the file is not an
image, call `callback` with nil arguments."
[file callback]
(if-not (= (files/type file) :file.type/image)
(callback nil nil)
(let [reader (js/FileReader.)
img (js/Image.)]
(set! (.-onload reader) #(set! (.-src img) (.-result reader)))
(set! (.-onerror reader) #(taoensso.timbre/error %))
(set! (.-onload img) (fn [_]
(resize-data-url! (.-result reader) 300 300
(fn [url]
(callback url img)))))
(.readAsDataURL reader file)
nil)))
回答2:
In your code example the (set! (.-src image) blob-url)
slipped into the onload
event handler itself.
You must call that outside that function to actually start loading the image so it can properly trigger the onload
.
Something like
(set! (.-src image) blob-url)
(set! (.-onload image)
(fn [_]
(prn "image height" (.-height image))
(prn "image width " (.-width image))
(.appendChild (js/document.getElementById "test-div") image)
(.drawImage ctx image 0 0 (.-width image) (.-height image))
;; approximating sizes of compressed and uncompressed images
(prn "size original (MB)" (* 0.75 0.000001 (.-length data-url)))
(prn "size upload (MB)" (* 0.75 0.000001 (.-length (.toDataURL canvas "image/png" 0.1))))
;; compressing and returning the blob
(js/Blob. [(.toDataURL canvas "image/png" 0.1)])
))
来源:https://stackoverflow.com/questions/60587859/set-onload-image-fn-not-working