Skip to content

Instantly share code, notes, and snippets.

@DarrenN
Created December 7, 2013 16:42
Show Gist options
  • Save DarrenN/7845047 to your computer and use it in GitHub Desktop.
Save DarrenN/7845047 to your computer and use it in GitHub Desktop.
Gnarly drag/drop with ClojureScript and core.async - http://cljsfiddle.net/fiddle/DarrenN.async.delta
<div id="canvas">
<h1>Click and drag boxes</h1>
<ul>
<li class="box"></li>
<li class="box"></li>
<li class="box"></li>
</ul>
</div>
(ns DarrenN.async.delta
(:require [cljs.core.async :refer (<! >! chan put! take! alts! timeout close! dropping-buffer sliding-buffer)]
[domina :as dom]
[domina.events :as events]
[domina.css :as css])
(:require-macros [cljs.core.async.macros :refer (go alt!)]))
(defn log [o]
(.log js/console o))
(defn msg [s id]
(dom/set-text! (dom/by-id id) s))
(defn merge-chans [& chans]
(let [rc (chan)]
(go
(loop []
(put! rc (first (alts! chans)))
(recur)))
rc))
(defn filter-chan [pred channel]
(let [rc (chan)]
(go (loop []
(let [val (<! channel)]
(if (pred val) (put! rc val))
(recur))
))
rc))
(defn listen [el type msg]
(let [in (chan)]
(events/listen! (dom/by-id el) type (fn [e] (put! in [msg e])))
in))
(defn stop-drag [box chan]
(close! chan)
(dom/remove-class! box "drag")
box)
; this is WAY gnarly and needs a re-think
(defn start-drag [msg evt chan]
(when (dom/has-class? (:target evt) "box")
; make a new channel just for the drag
(let [box (:target evt)
mouse (listen "canvas" :mousemove :mouse)
up (listen "canvas" :mouseup :up)
chan (merge-chans mouse up)]
(dom/add-class! box "drag")
(go
(loop [b box]
(let [[msg-name msg-data] (<! chan)]
(when (= msg-name :up)
(stop-drag box chan))
(when-not (= msg-name :up)
(dom/set-styles! b {:top (str (- (:clientY msg-data) 40) "px"), :left (str (- (:clientX msg-data) 40) "px")})
(recur box))))))))
(defn app-loop [start-state]
"Global loop - listen to all the things and dispatch"
(let [mouse (listen "canvas" :mousemove :mouse)
down (listen "canvas" :mousedown :down)
up (listen "canvas" :mouseup :up)
input (merge-chans mouse down up)]
(go
(loop [state start-state]
; Here do a condp on the msg-name and route
; an :up to its own function with its own go routine
; which handles the dragging of an object
(let [[msg-name msg-data] (<! input)]
(recur (condp = msg-name
:down (start-drag msg-name msg-data input)
:up msg-name
:mouse msg-name)))))))
(app-loop :up) ; loopin!
; scatter dem boxes
(doseq [x (dom/nodes (css/sel ".box"))]
(dom/set-styles! x {:top (str (.floor js/Math (+ (rand 400) 50)) "px"), :left (str (.floor js/Math (+ (rand 400) 50)) "px")}))
p {
color: #f41;
font-family: helvetica;
font-size: 2em;
text-align: center;
margin-top: 60px;
}
h1 {
font-size: 16px;
color: white;
font-family: sans-serif;
padding: 20px;
}
#canvas {
width: 100%;
min-height: 500px;
background: red;
}
ul {
margin: 20px;
}
.box {
width: 80px;
height: 80px;
background: yellow;
position: absolute;
z-index: 100;
list-style-type: none;
display: inline-block;
box-shadow: 0px 0px 3px maroon;
}
.drag {
z-index: 200;
background: green;
position: absolute;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment