Skip to content

Instantly share code, notes, and snippets.



Forked from cstorey/index.html
Created Oct 29, 2015
What would you like to do?
A trivial clojurescript paint program using Reagent and core.async. Greatly inspired by the RxJS paint example.
<script src="out/goog/base.js" type="text/javascript"></script>
<script src="hello_world.js" type="text/javascript"></script>
<script type="text/javascript">goog.require("hello_world.core");</script>
<script type="text/javascript"></script>
(defproject hello-world "0.1.0-SNAPSHOT"
:description "FIXME: write this!"
:url ""
:dependencies [[org.clojure/clojure "1.5.1"]
[org.clojure/clojurescript "0.0-2173"]
[reagent "0.4.1"]
[org.clojure/core.async ""]
[org.clojure/core.match "0.2.1"]]
:plugins [[lein-cljsbuild "1.0.2"]]
:source-paths ["src"]
:cljsbuild {
:builds [{:id "hello-world"
:source-paths ["src"]
:compiler {
:preamble ["reagent/react.js"]
:output-to "hello_world.js"
:output-dir "out"
:optimizations :whitespace
:source-map ""}}]})
; Actual path: src/hello_world/core.cljs
(ns hello-world.core
(:require [reagent.core :as reagent :refer [atom]]
[cljs.core.async :as async
:refer [<! >! chan close! sliding-buffer put! alts!]]
(:require-macros [cljs.core.async.macros :refer [go go-loop]]
[cljs.core.match.macros :refer [match]]))
(defn controller [inchan state-ref]
(go-loop [cur-x nil cur-y nil mouse-state :up]
(match [(<! inchan) mouse-state]
[({:type "mousedown" :x x :y y} :as e) :up]
(recur x y :down))
[({:type "mousemove" :x x :y y} :as e) :down]
(swap! state-ref update-in [:current-path] conj {:x x :y y})
(recur x y :down))
[({:type "mouseup" :x x :y y} :as e) :down]
(swap! state-ref (fn [{:keys [current-path paths] :as state}]
(assoc state :paths (conj paths current-path) :current-path [])))
(recur x y :up))
[s e] (recur cur-x cur-y mouse-state))))
(defn path-component [path fill-color]
(let [xys (map (fn [{:keys [x y]}] (str x " " y)) path)
points (apply str (interpose ", " xys))]
[:polyline {:points points :stroke fill-color :fill "none"}]))
(defn event-handler-fn [comms component e]
(let [bounds (. (reagent/dom-node component) getBoundingClientRect)
x (- (.-pageX e) (.-top bounds))
y (- (.-pageY e) (.-left bounds))]
(put! comms {:type (.-type e) :x x :y y})))
(defn hello-world-app [{:keys [state-ref comms] :as props}]
(let [{:keys [paths current-path]} @state-ref
component (reagent/current-component)
handler-fn (partial event-handler-fn comms component)]
[:svg {:height 480 :width 640
:on-mouse-up handler-fn :on-mouse-down handler-fn :on-mouse-move handler-fn
:style {:border "thin solid black"}}
(cons [path-component current-path "red"]
(map #(vector path-component % "black") paths))]))
(defn ^:export run []
(let [paths-atom (atom {:paths []})
ch (chan)
proc (controller ch paths-atom)]
[hello-world-app {:state-ref paths-atom :comms ch}]
(.-body js/document))
(let [exit (<! proc)]
(prn :exit! exit)))))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.