block by dribnet 5027818

strokes - General Update Pattern, II

Full Screen

strokes fork and port of mike’s general update pattern tutorial, II.

merge requests should go to the example in the repo


By adding a key to the data-join, letters that are already displayed are put in the update selection. Now updates can occur anywhere in the array, depending on the overlap between the old letters and the new letters. The text content only needs updating on enter because the mapping from letter to element never changes; however, the x-position of the text element must now be recomputed on update as well as enter.

It’ll be easier to see what’s going on when we add animated transitions next!

Next: Update Transitions

Previous: General Update Pattern

index.html

<!DOCTYPE html>
<meta charset="utf-8">
<style>

text {
  font: bold 48px monospace;
}

.enter {
  fill: green;
}

.update {
  fill: #bbb;
}

</style>
<body>
  <script type="text/javascript" src="//d3js.org/d3.v3.min.js"></script>
  <script type="text/javascript" src="gup2.js"></script>
</body>

gup2.cljs

(ns gup2
  (:require [strokes :refer [d3]]))

(strokes/bootstrap)

; 26 characters in a vec
(def alphabet (vec "abcdefghijklmnopqrstuwvxyz"))

(def width 960)
(def height 500)

(def svg (-> d3 (.select "body") (.append "svg")
      (.attr {:width width :height height})
    (.append "g")
      (.attr {:transform (str "translate(32," (/ height 2) ")")})))

(defn update [data]
  ; DATA JOIN
  ; Join new data with old elements, if any.
  (let [text (-> svg (.selectAll "text") (.data data identity))]
    ; UPDATE
    ; Update old elements as needed
    (-> text (.attr {:class "update"}))

    ; ENTER
    ; Create new elments as needed
    (-> text (.enter) (.append "text")
      (.attr {:class "enter"
              :dy    ".35em"})
      (.text identity))

    ; ENTER + UPDATE
    ; Appending to the enter selection expands the update selection to include
    ; entering elements; so, operations on the update selection after appending to
    ; the enter selection will apply to both entering and updating nodes.
    (-> text (.attr {:x #(* %2 32)}))

    ; EXIT
    ; Remove old elements as needed.
    (-> text (.exit) (.remove))))

; The initial display - all letters
(update alphabet)

; Grab a random sample of letters from the alphabet, in alphabetical order.
(.setInterval js/window (fn []
  (-> alphabet
    shuffle
    (subvec (rand-int 26))
    sort
    vec
    update))
  ; 2 seconds between swaps
  2000)