Array with drawn values, part 2

I did some code updates for the earlier post. The examples over there are already running the updated code. Since the code listings in that post are no longer up-to-date, let's go through the changes.

Better visibility with dark mode - fillRect with white color instead of clearRect:

(.clearRect ctx 0 0 width height)
;; replaced with the following two lines
(set! (.-fillStyle ctx) "white")
(.fillRect ctx 0 0 width height)

I also wanted to make it work for touch devices. First, I needed to prevent scrolling when trying to draw with a touch screen; added css property

touch-action: none;

I added (.preventDefault e)  for touchevents as well.

Same drawing behavior for touch events. I decided to implement this by setting the same handlers for touch events as for click events. Some helper functions were needed; "touchstart", "touchend", and "touchmove" event objects don't contain offsetX and offsetY properties. We can calculate these from clientX/Y of targetTouch and the getBoundingClientRect() of the canvas (more info here). Hence:

(defn get-offset-x [e]
  (let [bcr   (-> e .-target .getBoundingClientRect)
        bcr-x (.-x bcr)
        x     (-> e
              .-targetTouches
              (aget 0)
              .-clientX
              (- bcr-x)
              int)]
    x))
    
 ;; this can be used by replacing the let-binding:
 (let [x (.-offsetX e)
       ,,,])
 ;; with
 (let [x (or (.-offsetX e) (get-offset-x e))
       ,,,])

Same logic works for offset-y

I also made setup-mouse-events somewhat cleaner:

(defn setup-mouse-events [^js state canvas arr]
  (setup-start-draw state canvas arr)
  (setup-end-draw state canvas arr)
  (setup-draw state canvas arr)
  (.addEventListener
    canvas "mouseout"
    (fn [_]
      (aset state "prev-x" nil)
      (aset state "prev-y" nil))))

And the implementations:

(defn setup-start-draw [state canvas arr]
  (let [start-draw-fn
        (fn [e]
          (let [x (or (.-offsetX e) (get-offset-x e))
                y (or (.-offsetY e) (get-offset-y e))]
            (.preventDefault e)
            (aset state "drawing?" true)
            (aset state "prev-x" x)
            (aset state "prev-y" y)
            (aset arr x y)
            (draw-on-next-frame state "canvas4-draw" canvas arr)))]
    (.addEventListener
      canvas "touchstart"
      start-draw-fn)
    (.addEventListener
      canvas "mousedown"
      start-draw-fn)))
      
(defn setup-end-draw [state canvas arr]
  (let [end-draw-fn
        (fn [e]
          (aset state "drawing?" false))]
    (.addEventListener
      js/window "mouseup"
      end-draw-fn)
    (.addEventListener
      js/window "touchend"
      end-draw-fn)))
      
(defn setup-draw [state canvas arr]
  (let [draw-fn
        (fn [e]
          (when (aget state "drawing?")
            (let [x      (or (.-offsetX e) (get-offset-x e))
                  y      (or (.-offsetY e) (get-offset-y e))
                  prev-x (aget state "prev-x")
                  prev-y (aget state "prev-y")]
              (aset state "prev-x" x)
              (aset state "prev-y" y)
              (when (and prev-x prev-y)
                (line-to-array arr prev-x prev-y x y))
              (draw-on-next-frame state "canvas4-draw" canvas arr))))]
    (.addEventListener
      canvas "mousemove"
      draw-fn)
    (.addEventListener
      canvas "touchmove"
      draw-fn)))

That should be all the changes so far.