<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Mikko Vataja on Clojure]]></title><description><![CDATA[A Clojure/ClojureScript developer sharing things]]></description><link>https://clojure.blog/</link><image><url>https://clojure.blog/favicon.png</url><title>Mikko Vataja on Clojure</title><link>https://clojure.blog/</link></image><generator>Ghost 5.65</generator><lastBuildDate>Wed, 29 Apr 2026 04:15:32 GMT</lastBuildDate><atom:link href="https://clojure.blog/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Shadow-cljs Example: Replacing React with Preact for Smaller Bundle Size]]></title><description><![CDATA[<p>A while back I came across <a href="https://preactjs.com/?ref=clojure.blog">Preact</a>, which advertises itself as &quot;Fast 3kB alternative to React with the same modern API&quot;. I thought I&apos;d try it out with ClojureScript, shadow-cljs and reagent.</p><p>Let&apos;s start out with <a href="https://github.com/shadow-cljs/quickstart-browser?ref=clojure.blog">shadow-cljs browser quickstart template</a>. After cloning the repo,</p>]]></description><link>https://clojure.blog/replacing-react-with-preact/</link><guid isPermaLink="false">65af946d0f58ab67573d7f2d</guid><dc:creator><![CDATA[Mikko Vataja]]></dc:creator><pubDate>Tue, 23 Jan 2024 13:19:51 GMT</pubDate><content:encoded><![CDATA[<p>A while back I came across <a href="https://preactjs.com/?ref=clojure.blog">Preact</a>, which advertises itself as &quot;Fast 3kB alternative to React with the same modern API&quot;. I thought I&apos;d try it out with ClojureScript, shadow-cljs and reagent.</p><p>Let&apos;s start out with <a href="https://github.com/shadow-cljs/quickstart-browser?ref=clojure.blog">shadow-cljs browser quickstart template</a>. After cloning the repo, we need to install preact:</p><pre><code class="language-shell">$ npm install preact</code></pre><p>In shadow-cljs.edn inside our build we need to add</p><pre><code class="language-clojure">:js-options
 {:resolve
  {&quot;react&quot; {:target :npm :require &quot;preact/compat&quot;}
   &quot;react-dom&quot; {:target :npm :require &quot;preact/compat&quot;}}}</code></pre><p>Adding also reagent as a dependency, the file should look like this:</p><pre><code class="language-clojure">{:source-paths
 [&quot;src/dev&quot;
  &quot;src/main&quot;
  &quot;src/test&quot;]

 :dependencies
 [[reagent &quot;1.2.0&quot;]]

 :dev-http
 {8020 &quot;public&quot;}

 :builds
 {:app
  {:target :browser
   :output-dir &quot;public/js&quot;
   :asset-path &quot;/js&quot;
   :js-options
   {:resolve
    {&quot;react&quot; {:target :npm :require &quot;preact/compat&quot;}
     &quot;react-dom&quot; {:target :npm :require &quot;preact/compat&quot;}}}

   :modules
   {:main ; becomes public/js/main.js
    {:init-fn starter.browser/init}}}}}</code></pre><p>Then we can just <code>npx shadow-cljs watch app</code> and open it in browser. Checking the console we can see the messages from <code>js/console.log</code> statements as defined in <code>browser.cljs</code>.</p><p>Let&apos;s add some minimal functionality to <code>browser.cljs</code> to see that reagent still works:</p><pre><code class="language-clojure">(ns starter.browser
  (:require [reagent.core :as r]
            [reagent.dom :as rdom]))

(defn component-main []
  (let [state (r/atom {})]
    (fn []
      [:div
       [:input
        {:type &quot;text&quot;
         :on-change (fn [e]
                      (reset! state (-&gt; e .-target .-value)))}]
       [:div &quot;Reversed: &quot;
        (reverse @state)]])))

;; start is called by init and after code reloading finishes
(defn ^:dev/after-load start []
  (js/console.log &quot;start&quot;)
  (rdom/render [component-main]
               (js/document.getElementById &quot;app&quot;)))

(defn init []
  ;; init is called ONCE when the page loads
  ;; this is called in the index.html and must be exported
  ;; so it is available even in :advanced release builds
  (js/console.log &quot;init&quot;)
  (start))

;; this is called before any code is reloaded
(defn ^:dev/before-load stop []
  (js/console.log &quot;stop&quot;))</code></pre><p>You can view the full thing on <a href="https://github.com/vataja/shadow-cljs-and-preact?ref=clojure.blog">github</a>.</p><p>The resulting js-file (<code>npx shadow-cljs release app</code>) is roughly 182 KB. I tried the same using react and react-dom (version &quot;17.0.2&quot; for both) and the resulting file was 274 KB.</p><p>Note: if you&apos;re planning on migrating your current project from React to Preact make sure you go through <a href="https://preactjs.com/guide/v10/differences-to-react?ref=clojure.blog">differences to React</a> and test properly to ensure nothing breaks.</p><p>Edit: Chris McCormick already had a post about this: <a href="https://mccormick.cx/news/entries/replacing-react-with-preact-in-clojurescript?ref=clojure.blog">Replacing React with Preact in ClojureScript</a>. I highly recommend his writings.</p>]]></content:encoded></item><item><title><![CDATA[Reagent views and multimethods]]></title><description><![CDATA[<p>TL;DR: no <code>form-2</code> or <code>form-3</code> components as multimethods:</p><figure class="kg-card kg-code-card"><pre><code class="language-clojure">;; Don&apos;t do:
(defmethod page :hello [args]
  (setup-something! args)
  (fn [_]
    [:div &quot;hello&quot;]))

;; Do instead:
(defn hello-view [args]
  (setup-something! args)
  (fn [_]
    [:div &quot;hello&quot;]))

(defmethod page :hello [args]
  [hello-view args])</code></pre><figcaption>TL;DR</figcaption></figure><p>When creating multiple pages with the</p>]]></description><link>https://clojure.blog/reagent-views-and-multimethods/</link><guid isPermaLink="false">65251ada0f58ab67573d7ac9</guid><dc:creator><![CDATA[Mikko Vataja]]></dc:creator><pubDate>Sun, 03 Dec 2023 17:54:15 GMT</pubDate><content:encoded><![CDATA[<p>TL;DR: no <code>form-2</code> or <code>form-3</code> components as multimethods:</p><figure class="kg-card kg-code-card"><pre><code class="language-clojure">;; Don&apos;t do:
(defmethod page :hello [args]
  (setup-something! args)
  (fn [_]
    [:div &quot;hello&quot;]))

;; Do instead:
(defn hello-view [args]
  (setup-something! args)
  (fn [_]
    [:div &quot;hello&quot;]))

(defmethod page :hello [args]
  [hello-view args])</code></pre><figcaption>TL;DR</figcaption></figure><p>When creating multiple pages with the same layout (main navbar, static footer etc), a reasonable approach might be using multimethods to implement the main content of the page. Let&apos;s look at a simplified example:</p><pre><code class="language-clojure">(defonce state (r/atom {:current-view :front-page}))
(def current-view (r/cursor state [:current-view]))

(def pages
  ;; [[page-key page-title] ...]
  [[:front-page &quot;Front page&quot;]
   [:about &quot;About&quot;]
   [:setup &quot;Setup&quot;]])

(defmulti page :current-view)
(defmethod page :default [{:keys [current-view]}]
  [:div &quot;UNIMPLEMENTED:&quot; current-view])

(defn select-view! [view]
  (swap! state assoc :current-view view))

(defn navbar [{:keys [current-view]}]
  [:div.navbar
   (doall
     (for [[view title] pages
           :let [current? (= view current-view)]]
       ^{:key (str &quot;navbar-&quot; view)}
       [:a
        {:style {:padding &quot;5px&quot;
                 :background-color (when current?
                                     &quot;#dddddd&quot;)}
         :class-name (when (= view current-view)
                       &quot;active&quot;)
         :on-click (r/partial select-view! view)}
        title]))])

(defn main [state]
  [:div
   [navbar {:current-view @current-view}]
   [:div.main-content
    [page {:current-view @current-view}]]])

(defmethod page :front-page [_]
  [:div.content
   [:h1 &quot;Front page&quot;]
   [:div &quot;Welcome etc. [front page content here]&quot;]])

(defmethod page :about [_]
  [:div.content
   [:h1 &quot;About&quot;]
   [:div &quot;[About-page content here]&quot;]])</code></pre><p>In some cases we might want to add a view that needs to setup something, use local state etc. i.e. what <a href="https://github.com/reagent-project/reagent/blob/master/doc/CreatingReagentComponents.md?ref=clojure.blog#form-2--a-function-returning-a-function">form-2 components</a> are typically used for. You might try something like this:</p><pre><code class="language-clojure">(defmethod page :setup [_]
  ;; setup something etc. demonstrated by the print statement:
  (println &quot;run on each open, form-2&quot;)
  (fn [_]
    [:div.content
     [:h1 &quot;Do something, form-2&quot;]
     [:div &quot;[content here]&quot;]]))</code></pre><p>But when opening this view multiple times, the text is printed only once, and the main-content is stuck with the &quot;Do something, form-2&quot;:</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://clojure.blog/content/images/2023/11/form-2-multimethod.png" class="kg-image" alt loading="lazy" width="438" height="424"><figcaption>Note that &quot;About&quot; tab is selected</figcaption></figure><p>Let&apos;s see if making it a <a href="https://github.com/reagent-project/reagent/blob/master/doc/CreatingReagentComponents.md?ref=clojure.blog#form-3-a-class-with-life-cycle-methods">form-3 component</a> helps, using <code>:component-did-mount</code> to perform the setup instead:</p><pre><code class="language-clojure">;; remove previous implementation if needed:
(remove-method page :setup)

(defmethod page :setup [_]
  (r/create-class
    {:display-name &quot;setup-component&quot;
     :component-did-mount
     (fn [_]
       (println &quot;run on each open/mount, form-3&quot;))
     :reagent-render
     (fn [_]
       [:div.content
        [:h1 &quot;Do something, form-3&quot;]
        [:div &quot;[content here]&quot;]])}))</code></pre><p>Same problem. Seems like multimethods are incompatible with <code>form-2</code> and <code>form-3</code> components. Luckily, since <code>form-1</code> components seem to work fine, there&apos;s a simple solution:</p><pre><code class="language-clojure">(remove-method page :setup)

(defn setup-view [_]
  (println &quot;run on each open, wrapped&quot;)
  (fn [_]
    [:div.content
     [:h1 &quot;Do something, wrapped&quot;]
     [:div &quot;[content here]&quot;]]))

(defmethod page :setup [args]
  [setup-view args])</code></pre><p>Perform any setup actions inside an ordinary reagent component, and simply use the multimethod to wrap around this component. A gotcha I&apos;ve run into before.</p>]]></content:encoded></item><item><title><![CDATA[Array with drawn values, part 2]]></title><description><![CDATA[<p>I did some code updates for the <a href="https://clojure.blog/array-with-drawn-values/">earlier post</a>. The examples over there are already running the updated code. Since the code listings in that post are no longer up-to-date, let&apos;s go through the changes.</p><p>Better visibility with dark mode - <code>fillRect</code> with white color instead of <code>clearRect</code></p>]]></description><link>https://clojure.blog/array-with-drawn-values-part-2/</link><guid isPermaLink="false">65229b700f58ab67573d7a8b</guid><dc:creator><![CDATA[Mikko Vataja]]></dc:creator><pubDate>Wed, 18 Oct 2023 19:49:30 GMT</pubDate><content:encoded><![CDATA[<p>I did some code updates for the <a href="https://clojure.blog/array-with-drawn-values/">earlier post</a>. The examples over there are already running the updated code. Since the code listings in that post are no longer up-to-date, let&apos;s go through the changes.</p><p>Better visibility with dark mode - <code>fillRect</code> with white color instead of <code>clearRect</code>:</p><pre><code class="language-clojure">(.clearRect ctx 0 0 width height)
;; replaced with the following two lines
(set! (.-fillStyle ctx) &quot;white&quot;)
(.fillRect ctx 0 0 width height)</code></pre><p>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</p><pre><code class="language-css">touch-action: none;</code></pre><p>I added <code>(.preventDefault e)</code> &#xA0;for touchevents as well.</p><p>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; &quot;touchstart&quot;, &quot;touchend&quot;, and &quot;touchmove&quot; event objects don&apos;t contain offsetX and offsetY properties. We can calculate these from <code>clientX/Y</code> of <code>targetTouch</code> and the <code>getBoundingClientRect()</code> of the canvas (<a href="https://stackoverflow.com/questions/17130940/retrieve-the-same-offsetx-on-touch-like-mouse-event?ref=clojure.blog">more info here</a>). Hence:</p><pre><code class="language-clojure">(defn get-offset-x [e]
  (let [bcr   (-&gt; e .-target .getBoundingClientRect)
        bcr-x (.-x bcr)
        x     (-&gt; 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))
       ,,,])</code></pre><p>Same logic works for offset-y</p><p>I also made <code>setup-mouse-events</code> somewhat cleaner: </p><pre><code class="language-clojure">(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 &quot;mouseout&quot;
    (fn [_]
      (aset state &quot;prev-x&quot; nil)
      (aset state &quot;prev-y&quot; nil))))</code></pre><p>And the implementations:</p><pre><code class="language-clojure">(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 &quot;drawing?&quot; true)
            (aset state &quot;prev-x&quot; x)
            (aset state &quot;prev-y&quot; y)
            (aset arr x y)
            (draw-on-next-frame state &quot;canvas4-draw&quot; canvas arr)))]
    (.addEventListener
      canvas &quot;touchstart&quot;
      start-draw-fn)
    (.addEventListener
      canvas &quot;mousedown&quot;
      start-draw-fn)))
      
(defn setup-end-draw [state canvas arr]
  (let [end-draw-fn
        (fn [e]
          (aset state &quot;drawing?&quot; false))]
    (.addEventListener
      js/window &quot;mouseup&quot;
      end-draw-fn)
    (.addEventListener
      js/window &quot;touchend&quot;
      end-draw-fn)))
      
(defn setup-draw [state canvas arr]
  (let [draw-fn
        (fn [e]
          (when (aget state &quot;drawing?&quot;)
            (let [x      (or (.-offsetX e) (get-offset-x e))
                  y      (or (.-offsetY e) (get-offset-y e))
                  prev-x (aget state &quot;prev-x&quot;)
                  prev-y (aget state &quot;prev-y&quot;)]
              (aset state &quot;prev-x&quot; x)
              (aset state &quot;prev-y&quot; y)
              (when (and prev-x prev-y)
                (line-to-array arr prev-x prev-y x y))
              (draw-on-next-frame state &quot;canvas4-draw&quot; canvas arr))))]
    (.addEventListener
      canvas &quot;mousemove&quot;
      draw-fn)
    (.addEventListener
      canvas &quot;touchmove&quot;
      draw-fn)))</code></pre><p>That should be all the changes so far.</p>]]></content:encoded></item><item><title><![CDATA[Bug in ClojureScript 'rem' with JavaScript numbers]]></title><description><![CDATA[<p>(The ClojureScript version used in this post was &quot;1.11.60&quot;)</p><p>TL;DR:</p><figure class="kg-card kg-code-card"><pre><code class="language-clojure">&gt; (rem num 1000) ;; should be between -999 and 999 for all integers
6.1897001964269014E26</code></pre><figcaption>TL;DR</figcaption></figure><p>I wanted to create a simple Reagent toy example where a user can input a number and the</p>]]></description><link>https://clojure.blog/bug-in-clojurescript-rem-with-javascript-numbers/</link><guid isPermaLink="false">651942970f58ab67573d785e</guid><dc:creator><![CDATA[Mikko Vataja]]></dc:creator><pubDate>Sun, 08 Oct 2023 12:01:21 GMT</pubDate><content:encoded><![CDATA[<p>(The ClojureScript version used in this post was &quot;1.11.60&quot;)</p><p>TL;DR:</p><figure class="kg-card kg-code-card"><pre><code class="language-clojure">&gt; (rem num 1000) ;; should be between -999 and 999 for all integers
6.1897001964269014E26</code></pre><figcaption>TL;DR</figcaption></figure><p>I wanted to create a simple Reagent toy example where a user can input a number and the number is displayed in words. A straightforward implementation can use the already existing <a href="https://cljs.github.io/api/cljs.pprint/cl-format?ref=clojure.blog">cl-format from cljs.pprint</a>:</p><pre><code class="language-clojure">&gt; (cljs.pprint/cl-format false &quot;~R&quot; 123456)
&quot;one hundred twenty-three thousand, four hundred fifty-six&quot;</code></pre><p>Because of the way numbers are represented in JavaScript it was clear from the beginning that this will not work properly for large numbers. For example:</p><pre><code class="language-clojure">&gt; (cljs.pprint/cl-format false &quot;~R&quot; 44444444444444444444444444444444444444)
&quot;forty-four undecillion, four hundred forty-four decillion, four hundred forty-four nonillion, four hundred forty-four octillion, four hundred forty-four septillion, four hundred thirty-two sextillion&quot;
</code></pre><p>We can see it ends with &quot;thirty-two sextillion&quot;; a clear loss of precision.</p><p>What surprised me was when playing around with the Reagent example, the whole thing suddenly broke. The textual representation was no longer getting updated, and there was an error message in the console:</p><pre><code>Uncaught (in promise) Error: No item 6.189700196426902e+24 in vector of length 20
...</code></pre><figure class="kg-card kg-image-card"><img src="https://clojure.blog/content/images/2023/10/cl-format-error.png" class="kg-image" alt loading="lazy" width="943" height="488" srcset="https://clojure.blog/content/images/size/w600/2023/10/cl-format-error.png 600w, https://clojure.blog/content/images/2023/10/cl-format-error.png 943w" sizes="(min-width: 720px) 720px"></figure><p>So what&apos;s going on? Trying to replicate the issue, I found an example input value:</p><pre><code class="language-clojure">;; we&apos;ll use this definition in some of the later REPL examples as well:
(def num 5555555555555555555555555555555555555555555)

(cljs.pprint/cl-format false &quot;~R&quot; num)
Execution error (Error) at (&lt;cljs repl&gt;:1).
No item 6.189700196426902e+24 in vector of length 20
:repl/exception!
</code></pre><p>The stack trace contains a line</p><pre><code class="language-clojure">at cljs$pprint$format_simple_cardinal (pprint.cljs:1187:33)</code></pre><p> which seemed like a good place to start digging around the source code (<a href="https://github.com/clojure/clojurescript/blob/39709c9614d37b9a3dd398be8fed83cd3dda534b/src/main/cljs/cljs/pprint.cljs?ref=clojure.blog#L1187">https://github.com/clojure/clojurescript/blob/39709c9614d37b9a3dd398be8fed83cd3dda534b/src/main/cljs/cljs/pprint.cljs#L1187</a>):</p><pre><code class="language-clojure">      (if (pos? hundreds) (str (nth english-cardinal-units hundreds) &quot; hundred&quot;))</code></pre><p>Since the error was &quot;No item 6.189700196426902e+24 in vector of length 20&quot;, the (nth english-cardinal-units hundreds) is the likely culprit - <strong>hundreds</strong> is defined as</p><pre><code class="language-clojure">(defn- format-simple-cardinal
  &quot;Convert a number less than 1000 to a cardinal english string&quot;
  [num]
  (let [hundreds (quot num 100)
        ,,,]</code></pre><p>Since the function formats a number less than 1000, &apos;hundreds&apos; should always be between 0 and 9, but here it seems to be larger than that. Let&apos;s take a look at where this is getting called. In <a href="https://github.com/clojure/clojurescript/blob/39709c9614d37b9a3dd398be8fed83cd3dda534b/src/main/cljs/cljs/pprint.cljs?ref=clojure.blog#L1222">format-cardinal-english</a> we find</p><pre><code class="language-clojure">      (let [,,,
            parts (remainders 1000 abs-arg)]
        (if (&lt;= (count parts) (count english-scale-numbers))
          (let [parts-strs (map format-simple-cardinal parts)</code></pre><p>Let&apos;s see if there&apos;s anything weird going on with &apos;remainders&apos; (note: the #&apos;ns/fn syntax can be used to access private functions from other namespaces):</p><pre><code class="language-clojure">&gt; (#&apos;cljs.pprint/remainders 1000 num)
(5 555 555 555 555 554 0 0 0 0 0 0 0 0 6.1897001964269014E26)</code></pre><p>The final value in the list looks familiar. Since &apos;remainders&apos; is using &apos;rem&apos; let&apos;s just quickly try:</p><pre><code class="language-clojure">&gt; (rem num 1000)
6.1897001964269014E26</code></pre><p>There we go. <a href="https://cljs.github.io/api/cljs.core/rem?ref=clojure.blog">rem</a> i.e. remainder &quot;Returns the remainder of dividing numerator <code>n</code> by denominator <code>d</code>.&quot; Ignoring the precision issues, the remainder should be 555. At least it should never be greater than (or equal to) 1000, the denominator. Here we&apos;re getting a massive number instead.</p><p>The source code of rem is</p><pre><code class="language-clojure">(defn rem
  [n d]
  (let [q (quot n d)]
    (- n (* d q))))</code></pre><p>We can see that numerator is divided by denominator (taking only the quotient), then the quotient is multiplied again by the denominator. This is then subtracted from the numerator, which mathematically should give the remainder - something about the number representation causes us to lose precision here. To further pinpoint the issue:</p><pre><code class="language-clojure">&gt; (let [n 5555555555555555555555555555555555555555555
        d 1000
        q (quot n d)
        multiplied (* d q)]
    [n multiplied (- n multiplied)])
[5.5555555555555556E42
 5.555555555555555E42
 6.1897001964269014E26]</code></pre><p>An alternative example:</p><pre><code class="language-clojure">&gt; 5555555555555555555555555555555555555555555
5.5555555555555556E42
&gt; 5555555555555555555555555555555555555555
5.555555555555555E39
&gt; (* *1 1000)
5.555555555555555E42
&gt; (- 5555555555555555555555555555555555555555555 *1)
6.1897001964269014E26</code></pre><p>Where we can see the original number vs the multiplied one differ slightly in the representation (5.[snip]5<strong>6</strong>E42 vs 5.[snip]5E42), which causes an error in the subtraction to the tune of 6e26.</p><p>I did some searching and I found a report <a href="https://clojure.atlassian.net/browse/CLJS-1164?ref=clojure.blog">quot and rem are inefficient</a> from 2015. The proposed solution (rem defined as js-mod) seems to fix this problem:</p><pre><code class="language-clojure">(with-redefs [rem js-mod]
  (cljs.pprint/cl-format false &quot;~R&quot; 5555555555555555555555555555555555555555555))
&quot;five tredecillion, five hundred fifty-five duodecillion, five hundred fifty-five undecillion, five hundred fifty-five decillion, five hundred fifty-five nonillion, five hundred fifty-four octillion, three hundred four septillion, four hundred eighty-eight sextillion, nine hundred seventy-six quintillion, seven hundred eighty-four quadrillion, four hundred sixteen trillion, eight hundred seventy-two billion, nine hundred seventy-six million, three hundred twenty thousand, four hundred eighty&quot;</code></pre><p>It still has the aforementioned precision issues, but at least it doesn&apos;t outright fail.</p><p>Another solution would be to just replace the rem with js-mod permanently:</p><pre><code class="language-clojure">(set! rem js-mod)</code></pre>]]></content:encoded></item><item><title><![CDATA[Array with drawn values]]></title><description><![CDATA[<p>There are plenty of libraries for visualizing data such as chart.js and highcharts.</p><p>I&apos;d like to have a version where I can see the contents of an array as an xy-plot that is also drawable i.e. I want to change the values of the array by</p>]]></description><link>https://clojure.blog/array-with-drawn-values/</link><guid isPermaLink="false">6511d39f0f58ab67573d77e5</guid><dc:creator><![CDATA[Mikko Vataja]]></dc:creator><pubDate>Mon, 25 Sep 2023 19:27:27 GMT</pubDate><media:content url="https://clojure.blog/content/images/2023/09/drawable-canvas-screenshot.png" medium="image"/><content:encoded><![CDATA[<img src="https://clojure.blog/content/images/2023/09/drawable-canvas-screenshot.png" alt="Array with drawn values"><p>There are plenty of libraries for visualizing data such as chart.js and highcharts.</p><p>I&apos;d like to have a version where I can see the contents of an array as an xy-plot that is also drawable i.e. I want to change the values of the array by drawing with my mouse. I&apos;ll use a canvas that corresponds to values in an array.</p><p>I also decided to experiment with getting the generated javascript as small as possible using the rules and conventions described in <a href="https://mccormick.cx/news/entries/clojurescript-uis-in-500-bytes?ref=clojure.blog"><strong>ClojureScript UIs in 500 Bytes</strong></a> by Chris McCormick (see also <a href="https://github.com/chr15m/cljs-ultralight?ref=clojure.blog">cljs-ultralight</a>).</p><pre><code class="language-clojure">(ns canvas-array.main
  (:require [ultralight.core :as u]))
</code></pre><p>To make this simple let&apos;s start with default width and height of 300 and 150:</p><pre><code class="language-html">&lt;canvas id=&quot;canvasv1&quot; width=&quot;300&quot; height=&quot;150&quot;&gt;
&lt;/canvas&gt;</code></pre><p>Let&apos;s continue with drawing the contents of an array. We&apos;ll assume the contents of the array form a continuous path where the array index corresponds to an x-coordinate and the value corresponds to a y-coordinate.</p><pre><code class="language-clojure">(defn draw-array! [canvas arr]
  (let [ctx (.getContext canvas &quot;2d&quot;)
        width (.-clientWidth canvas)
        height (.-clientHeight canvas)
        length (.-length arr)]
    (.clearRect ctx 0 0 width height)
    (.beginPath ctx)
    (set! (.-lineWidth ctx) &quot;1&quot;)
    (set! (.-fillStyle ctx) &quot;black&quot;)
    (.moveTo ctx 0 (aget arr 0))
    (dotimes [n (dec length)]
      (let [x (inc n)]
        (.lineTo ctx x (aget arr x))))
    (.stroke ctx)))</code></pre><p>Let&apos;s try it out with an array of a length of 300 (corresponding to the canvas width) and we&apos;ll fill it up with some values so we don&apos;t have to worry about nonexistent values breaking the logic above:</p><pre><code class="language-clojure">(defn setup-canvas-1 [el arr]
  (.fill arr 100 0)
  (aset arr 0 90)
  (aset arr 299 110)
  (draw-array! el arr))

;; defined as :init-fn in shadow-cljs.edn
(defn main! []
  (let [c1 (u/$ &quot;#canvasv1&quot;)
        arr1 (js/Array 300)]
    (setup-canvas-1 c1 arr1)))</code></pre><p>And the end result (with some additional styling for the canvas) looks like this:</p><!--kg-card-begin: html--><!--<div style="background-color:#a00" width=310 height=160>-->
<div style="touch-action: none;" width="310" height="160">
<canvas id="canvasv1" width="300" height="150" style="background-color:#fff;margin: 5px;border:5px solid #ddd">
</canvas>
</div><!--kg-card-end: html--><p>No interactivity so far, so let&apos;s make this drawable. Let&apos;s see what we can do with simple mouse events. We&apos;re using another canvas with an id of &quot;canvasv2&quot;:</p><pre><code class="language-clojure">(defn setup-mouse-events-1 [state canvas arr]
  (.addEventListener
   canvas &quot;mousedown&quot;
   (fn [e]
     (let [x (.-offsetX e)
           y (.-offsetY e)]
       ;; keep track whether we should keep drawing on mousemove event:
       (aset state &quot;drawing?&quot; true)
       (aset arr x y)
       (draw-array canvas arr)))
  (.addEventListener
   js/window &quot;mouseup&quot;
   (fn [e]
     (aset state &quot;drawing?&quot; false)))
  (.addEventListener
   canvas &quot;mousemove&quot;
   (fn [e]
     (when (aget state &quot;drawing?&quot;)
       (let [x (.-offsetX e)
             y (.-offsetY e)]
         (aset arr x y)
         (draw-array canvas arr))))))
         
(defn setup-canvas-2 [canvas arr]
  (.fill arr 100 0)
  (aset arr 0 110)
  (aset arr 299 90)
  (draw-array canvas arr)
  (setup-mouse-events-1 #js {} canvas arr))</code></pre><p>The resulting canvas (try drawing on it with your mouse):</p><!--kg-card-begin: html--><div style="touch-action:none;" width="310" height="160">
<canvas id="canvasv2" width="300" height="150" style="background-color:#fff;margin: 5px;border:5px solid #ddd">
</canvas>
</div><!--kg-card-end: html--><p>Since mousemove event isn&apos;t triggered on each neighboring pixel, the resulting values inside the array will be non-continuous and looks something like this:</p><figure class="kg-card kg-image-card"><img src="https://clojure.blog/content/images/2023/09/drawable-canvas-1.png" class="kg-image" alt="Array with drawn values" loading="lazy" width="319" height="169"></figure><p>We&apos;ll need to improve the mousemove event handler. Instead of setting the individual xy-values from the event, we should rather draw a line from previous xy-coordinate to the next one. Luckily we already have a state variable we can use:</p><pre><code class="language-clojure">(defn line-to-array [arr x1 y1 x2 y2]
  (if (coercive-= x1 x2)
    (aset arr x1 y2)
    (let [ydelta (- y2 y1)
          xdelta (- x2 x1)
          min-x (min x1 x2)
          max-x (max x1 x2)
          x-range (inc (- max-x min-x))
          coeff (/ ydelta xdelta)
          yval (fn [x]
                 (+ y1 (* coeff (- x x1))))]
      (dotimes [i x-range]
        (let [k (+ i min-x)]
          (aset arr k (yval k)))))))

(defn setup-mouse-events-2 [^js state canvas arr]
  (.addEventListener
   canvas &quot;mousedown&quot;
   (fn [e]
     (let [x (.-offsetX e)
           y (.-offsetY e)]
       (aset state &quot;drawing?&quot; true)
       (aset state &quot;prev-x&quot; x)
       (aset state &quot;prev-y&quot; y)
       (aset arr x y)
       (draw-array canvas arr))))
  (.addEventListener
   js/window &quot;mouseup&quot;
   (fn [_]
     (aset state &quot;drawing?&quot; false)))
  (.addEventListener
   canvas &quot;mousemove&quot;
   (fn [e]
     (when (aget state &quot;drawing?&quot;)
       (let [x (.-offsetX e)
             y (.-offsetY e)
             prev-x (aget state &quot;prev-x&quot;)
             prev-y (aget state &quot;prev-y&quot;)]
         (aset state &quot;prev-x&quot; x)
         (aset state &quot;prev-y&quot; y)
         (line-to-array arr prev-x prev-y x y)
         (draw-array canvas arr))))))

(defn setup-canvas-3[canvas arr]
  (.fill arr 100 0)
  (aset arr 0 110)
  (aset arr 299 90)
  (draw-array canvas arr)
  (setup-mouse-events-2 #js {} canvas arr))</code></pre><p>The improved version:</p><!--kg-card-begin: html--><div style width="310" height="160">
<canvas id="canvasv3" width="300" height="150" style="background-color:#fff;margin: 5px;border:5px solid #ddd">
</canvas>
</div><!--kg-card-end: html--><p>Now this seems more reasonable. We can still notice some unwanted behavior; while holding down the mouse button, move the cursor out of the canvas and then move it back - we get a line drawn from the coordinates we exited the canvas to the coordinates we re-entered the canvas.</p><p>We can fix this by resetting the prev-x and prev-y on mouseout, then draw the line only if those values are defined:</p><pre><code class="language-clojure">(.addEventListener
 canvas &quot;mouseout&quot;
 (fn [_]
   (aset state &quot;prev-x&quot; nil)
   (aset state &quot;prev-y&quot; nil)))

;; and update the earlier mousemove handler from earlier
  (line-to-array arr prev-x prev-y x y)
  ;; to
  (when (and prev-x prev-y)
    (line-to-array arr prev-x prev-y x y))</code></pre><p>The result:</p><!--kg-card-begin: html--><div style width="310" height="160">
<canvas id="canvasv4" width="300" height="150" style="background-color:#fff;margin: 5px;border:5px solid #ddd">
</canvas>
</div><!--kg-card-end: html--><p>Since mouse events might be triggered multiple times per frame, we don&apos;t have to redraw the canvas on each mousemove. Let&apos;s create another function that uses <a href="https://developer.mozilla.org/en-US/docs/Web/API/window/requestAnimationFrame?ref=clojure.blog">requestAnimationFrame</a> to ensure draw-array is called only once per frame.</p><pre><code class="language-clojure">(defn draw-on-next-frame [state k canvas arr]
  (let [draw? (aget state k)]
    (when-not draw?
      (aset state k true)
      (js/requestAnimationFrame
       (fn [_]
         (aset state k nil)
         (draw-array canvas arr))))))</code></pre><p>The function takes a state variable (a js-hashmap) and a key where to keep track whether we&apos;re redrawing the canvas on the next frame:</p><pre><code>;;replace (draw-canvas canvas arr) with:
(draw-on-next-frame state &quot;canvasv4-draw&quot; canvas arr)</code></pre><p>The visible functionality won&apos;t differ from the previous example, so no need to replicate it here.</p><p>That&apos;s it for now. What happens when there are non-numerical values inside the array? What if the canvas width differs from the array length? Let&apos;s leave these considerations for later.</p><p>FYI: the resulting js-file is smaller than 4 kB.</p><!--kg-card-begin: html--><script type="text/javascript" src="https://clojure.blog/content/files/2023/06/canvasarray.js"></script><!--kg-card-end: html-->]]></content:encoded></item></channel></rss>