├── .gitignore
├── LICENSE
├── README.md
├── preview.gif
├── project.clj
├── publish.sh
├── resources
└── public
│ ├── cljs.svg
│ ├── css
│ ├── keys.css
│ └── main.css
│ ├── index.html
│ ├── index_release.html
│ └── js.svg
├── sketch.jpg
├── src-dev
└── solar_system_of_js
│ └── dev.cljs
├── src
└── solar_system_of_js
│ ├── actions.cljs
│ ├── animate.cljs
│ ├── canvas.cljs
│ ├── caption.cljs
│ ├── control.cljs
│ ├── core.cljs
│ ├── draw.cljs
│ ├── math.cljs
│ ├── nav.cljs
│ └── state.cljs
└── title.png
/.gitignore:
--------------------------------------------------------------------------------
1 | pom.xml
2 | *jar
3 | /lib/
4 | /classes/
5 | /out/
6 | /target/
7 | .lein-deps-sum
8 | .lein-repl-history
9 | .lein-plugins/
10 | figwheel_server.log
11 |
12 | resources/public/js/out
13 | resources/public/js/out-adv
14 | resources/public/js/*.js
15 |
16 | *.swp
17 |
18 | hosted/
19 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2011-2015 Shaun Williams
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy of
4 | this software and associated documentation files (the "Software"), to deal in
5 | the Software without restriction, including without limitation the rights to
6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7 | of the Software, and to permit persons to whom the Software is furnished to do
8 | so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19 | SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](http://shaunlebron.github.io/solar-system-of-js/)
2 | [](http://shaunlebron.github.io/solar-system-of-js/)
3 |
4 | __[>> View the "Solar System of JS"](http://shaunlebron.github.io/solar-system-of-js/)__
5 |
6 | I made this to visualize the __current state of languages on the JS platform__,
7 | because I am frankly overwhelmed by all the different activity happening in
8 | this space. It was originally motivated by a desire to bring ClojureScript
9 | into this view, as a language that I think has simple, fundamental solutions to
10 | a lot of the problems that are fragmenting JS.
11 |
12 | ## Implementation
13 |
14 | The code is written in
\*
16 | ClojureScript. It implements its own animated presentation architecture that
17 | hopefully serves as an example for interesting things that can be done with the
18 | language.
19 |
20 | ###### Some features:
21 |
22 | - frames are drawn to a Canvas using the 2D api
23 | - all animations tap a `core.async` channel to receive a delta-time `dt` value every frame.
24 | - slide transitions are driven by an [actions spec] to animate [state data]
25 | - the incremental actions spec allows us to jump ahead to specific slides (for URL routing)
26 | - the [drawing] is a pure function of the application state (basically)
27 |
28 | ###### File Descriptions:
29 |
30 | - [`core.cljs`](src/solar_system_of_js/core.cljs)- entry point
31 | - [`actions.cljs`](src/solar_system_of_js/actions.cljs)- slide transition state actions
32 | - [`animate.cljs`](src/solar_system_of_js/animate.cljs)- animation utility (using core.async)
33 | - [`canvas.cljs`](src/solar_system_of_js/canvas.cljs)- canvas initialization and api wrapper
34 | - [`caption.cljs`](src/solar_system_of_js/caption.cljs)- caption setter in markdown
35 | - [`control.cljs`](src/solar_system_of_js/control.cljs)- touch/key controls
36 | - [`draw.cljs`](src/solar_system_of_js/draw.cljs)- all drawing functions
37 | - [`math.cljs`](src/solar_system_of_js/math.cljs)- math aliases
38 | - [`nav.cljs`](src/solar_system_of_js/nav.cljs)- slide navigation and routing
39 | - [`state.cljs`](src/solar_system_of_js/state.cljs)- state of the application
40 |
41 | _\* The [logo] is part of a branding effort for a proper ClojureScript
42 | website that we are building this year, with docs pages and tutorials._
43 |
44 | ## Development Setup
45 |
46 | 1. Install [leiningen]
47 | 2. Run this to get a live-reload compiler, http server, and REPL:
48 |
49 | ```
50 | $ lein figwheel dev
51 | ```
52 |
53 | 3. Open
54 |
55 | ## Contact
56 |
57 | - email:
58 | - twitter: [@shaunlebron](http://twitter.com/shaunlebron)
59 |
60 | ## License
61 |
62 | MIT
63 |
64 | ----
65 |
66 | [](https://raw.githubusercontent.com/shaunlebron/solar-system-of-js/master/sketch.jpg)
67 |
68 | [actions spec]:https://github.com/shaunlebron/solar-system-of-js/blob/master/src/solar_system_of_js/actions.cljs#L160
69 | [state data]:https://github.com/shaunlebron/solar-system-of-js/blob/master/src/solar_system_of_js/state.cljs#L5
70 | [drawing]:https://github.com/shaunlebron/solar-system-of-js/blob/master/src/solar_system_of_js/draw.cljs#L459
71 | [leiningen]:http://leiningen.org/
72 | [logo]:https://github.com/oakmac/cljs.info/blob/master/00-scrap/cljs_logo-01.svg
73 |
74 |
--------------------------------------------------------------------------------
/preview.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shaunlebron/solar-system-of-js/a5c53c9727622ac023963766323b6eb5d03f5226/preview.gif
--------------------------------------------------------------------------------
/project.clj:
--------------------------------------------------------------------------------
1 | (defproject solar-system-of-js "0.1.0-SNAPSHOT"
2 | :description "Visualizing the state of JS as a solar system"
3 | :url "https://github.com/shaunlebron/solar-system-of-js"
4 |
5 | :dependencies [[org.clojure/clojure "1.6.0"]
6 | [org.clojure/clojurescript "0.0-3058"]
7 | [org.clojure/core.async "0.1.346.0-17112a-alpha"]
8 | [figwheel "0.2.5"]
9 | [cljsjs/hammer "2.0.4-4"]
10 | [markdown-clj "0.9.65"]
11 | [hiccups "0.3.0"]]
12 |
13 | :node-dependencies [[source-map-support "0.2.8"]]
14 |
15 | :plugins [[lein-cljsbuild "1.0.4"]
16 | [lein-npm "0.4.0"]
17 | [lein-figwheel "0.2.5"]]
18 |
19 | :source-paths ["src" "target/classes"]
20 |
21 | :clean-targets ["resources/public/js/out"
22 | "resources/public/js/out-adv"]
23 |
24 | :figwheel {:http-server-root "public" ;; this will be in resources/
25 | :server-port 3449 ;; default
26 | :css-dirs ["resources/public/css"]
27 | }
28 |
29 | :cljsbuild {
30 | :builds [{:id "dev"
31 | :source-paths ["src" "src-dev"]
32 | :compiler {
33 | :output-to "resources/public/js/solar_system_of_js.js"
34 | :output-dir "resources/public/js/out"
35 | :optimizations :none
36 | :cache-analysis true
37 | :source-map true}}
38 | {:id "release"
39 | :source-paths ["src"]
40 | :compiler {
41 | :output-to "resources/public/js/solar_system_of_js.min.js"
42 | :output-dir "resources/public/js/out-adv"
43 | :optimizations :advanced
44 | :pretty-print false}}]})
45 |
--------------------------------------------------------------------------------
/publish.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | set -e
4 |
5 | cd `dirname $0`
6 |
7 | if [ ! -d hosted ]; then
8 | git clone git@github.com:shaunlebron/solar-system-of-js.git hosted
9 | fi
10 |
11 | cd hosted
12 |
13 | # checkout gh-pages branch
14 | git checkout gh-pages
15 |
16 | # make sure gh-pages is up-to-date
17 | git pull
18 |
19 | # remove all files
20 | git rm -rf .
21 |
22 | # add new report files
23 | lein cljsbuild once release
24 | cp -r ../resources/public/* .
25 |
26 | # clean out unneeded
27 | rm -rf js/out-adv \
28 | js/out \
29 | js/solar_system_of_js.js
30 |
31 | # choose production page
32 | mv index_release.html index.html
33 |
34 | git add .
35 | git commit -m "auto-update"
36 |
37 | # publish to website
38 | git push origin gh-pages
39 |
--------------------------------------------------------------------------------
/resources/public/cljs.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
28 |
--------------------------------------------------------------------------------
/resources/public/css/keys.css:
--------------------------------------------------------------------------------
1 | /**
2 | * KEYS.css
3 | *
4 | * A simple stylesheet for rendering beautiful keyboard-style elements.
5 | *
6 | * Author: Michael Hüneburg
7 | * Website: http://michaelhue.com/keyscss
8 | * License: MIT License (see LICENSE.txt)
9 | */
10 |
11 | /* Base style, essential for every key. */
12 | kbd, .key {
13 | display: inline;
14 | display: inline-block;
15 | min-width: 1em;
16 | padding: .2em .3em;
17 | font: normal .85em/1 "Lucida Grande", Lucida, Arial, sans-serif;
18 | text-align: center;
19 | text-decoration: none;
20 | -moz-border-radius: .3em;
21 | -webkit-border-radius: .3em;
22 | border-radius: .3em;
23 | border: none;
24 | cursor: default;
25 | -moz-user-select: none;
26 | -webkit-user-select: none;
27 | user-select: none;
28 | }
29 | kbd[title], .key[title] {
30 | cursor: help;
31 | }
32 |
33 | /* Dark style for display on light background. This is the default style. */
34 | kbd, kbd.dark, .dark-keys kbd, .key, .key.dark, .dark-keys .key {
35 | background: rgb(80, 80, 80);
36 | background: -moz-linear-gradient(top, rgb(60, 60, 60), rgb(80, 80, 80));
37 | background: -webkit-gradient(linear, left top, left bottom, from(rgb(60, 60, 60)), to(rgb(80, 80, 80)));
38 | color: rgb(250, 250, 250);
39 | text-shadow: -1px -1px 0 rgb(70, 70, 70);
40 | -moz-box-shadow: inset 0 0 1px rgb(150, 150, 150), inset 0 -.05em .4em rgb(80, 80, 80), 0 .1em 0 rgb(30, 30, 30), 0 .1em .1em rgba(0, 0, 0, .3);
41 | -webkit-box-shadow: inset 0 0 1px rgb(150, 150, 150), inset 0 -.05em .4em rgb(80, 80, 80), 0 .1em 0 rgb(30, 30, 30), 0 .1em .1em rgba(0, 0, 0, .3);
42 | box-shadow: inset 0 0 1px rgb(150, 150, 150), inset 0 -.05em .4em rgb(80, 80, 80), 0 .1em 0 rgb(30, 30, 30), 0 .1em .1em rgba(0, 0, 0, .3);
43 | }
44 |
45 | /* Light style for display on dark background. */
46 | kbd.light, .light-keys kbd, .key.light, .light-keys .key {
47 | background: rgb(250, 250, 250);
48 | background: -moz-linear-gradient(top, rgb(210, 210, 210), rgb(255, 255, 255));
49 | background: -webkit-gradient(linear, left top, left bottom, from(rgb(210, 210, 210)), to(rgb(255, 255, 255)));
50 | color: rgb(50, 50, 50);
51 | text-shadow: 0 0 2px rgb(255, 255, 255);
52 | -moz-box-shadow: inset 0 0 1px rgb(255, 255, 255), inset 0 0 .4em rgb(200, 200, 200), 0 .1em 0 rgb(130, 130, 130), 0 .11em 0 rgba(0, 0, 0, .4), 0 .1em .11em rgba(0, 0, 0, .9);
53 | -webkit-box-shadow: inset 0 0 1px rgb(255, 255, 255), inset 0 0 .4em rgb(200, 200, 200), 0 .1em 0 rgb(130, 130, 130), 0 .11em 0 rgba(0, 0, 0, .4), 0 .1em .11em rgba(0, 0, 0, .9);
54 | box-shadow: inset 0 0 1px rgb(255, 255, 255), inset 0 0 .4em rgb(200, 200, 200), 0 .1em 0 rgb(130, 130, 130), 0 .11em 0 rgba(0, 0, 0, .4), 0 .1em .11em rgba(0, 0, 0, .9);
55 | }
56 |
--------------------------------------------------------------------------------
/resources/public/css/main.css:
--------------------------------------------------------------------------------
1 | body {
2 | background: #1a1a1a;
3 | margin: 0;
4 | font: 16px "Open Sans", Helvetica, sans-serif;
5 | }
6 |
7 | body, html {
8 | min-width: 1024px;
9 | min-height: 660px;
10 | height: 100%;
11 | }
12 |
13 | #jsLogo {
14 | display: none;
15 | }
16 |
17 | #cljsLogo {
18 | height: 40px;
19 | opacity: 0.5;
20 | vertical-align: middle;
21 | transition: opacity 0.2s linear;
22 | }
23 |
24 | #container {
25 | display: -webkit-box;
26 | display: -moz-box;
27 | display: -ms-flexbox;
28 | display: -webkit-flex;
29 | display: flex;
30 |
31 | flex-direction: column;
32 | justify-content: center;
33 | align-items: center;
34 | align-content: center;
35 | -webkit-flex-direction: column;
36 | -webkit-justify-content: center;
37 | -webkit-align-items: center;
38 | -webkit-align-content: center;
39 |
40 | width: 100%;
41 | height: 100%;
42 | }
43 |
44 | #canvas-container {
45 | width: 100%;
46 | height: 70%;
47 | text-align: center;
48 | }
49 |
50 | #canvas {
51 | width: auto;
52 | height: auto;
53 | max-width: 100%;
54 | max-height: 100%;
55 | border-radius: 5px;
56 | }
57 |
58 | #caption {
59 | transition: opacity 1s;
60 | opacity: 0;
61 |
62 | margin: 20px auto;
63 | padding: 20px 40px;
64 | max-width: 900px;
65 | color: #AAA;
66 | background: #2a2a2a;
67 | text-align: center;
68 | border-radius: 5px;
69 | }
70 |
71 | #canvas-footer {
72 | position: relative;
73 | height: 80px;
74 | opacity: 0;
75 | transition: opacity 1s;
76 | }
77 |
78 | #keys {
79 | color: #AAA;
80 | position: absolute;
81 | text-align: right;
82 | display: block;
83 | opacity: 0.2;
84 | right: 10px;
85 | top: 10px;
86 | transition: opacity 0.2s;
87 | cursor: default;
88 | }
89 |
90 | #keys:hover {
91 | opacity: 0.5;
92 | }
93 |
94 | #keys tr td:last-child {
95 | text-align: left;
96 | }
97 |
98 | #caption p {
99 | margin: 5px;
100 | }
101 |
102 | #caption a {
103 | color: #EEE;
104 | text-decoration: none;
105 | border-bottom: 2px solid #666;
106 | }
107 |
108 | #caption i.fa {
109 | font-size: 1.2em;
110 | vertical-align: middle;
111 | }
112 |
113 | #github {
114 | padding: 20px;
115 | color: #444;
116 | display: block;
117 | width: 260px;
118 | margin: 0 auto;
119 | text-align: center;
120 | }
121 |
122 | a {
123 | color: inherit;
124 | text-decoration: none;
125 | }
126 |
127 | #github:hover #cljsLogo {
128 | opacity: 1;
129 | }
130 |
--------------------------------------------------------------------------------
/resources/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Solar System of JS
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/resources/public/index_release.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Solar System of JS
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/resources/public/js.svg:
--------------------------------------------------------------------------------
1 |
32 |
--------------------------------------------------------------------------------
/sketch.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shaunlebron/solar-system-of-js/a5c53c9727622ac023963766323b6eb5d03f5226/sketch.jpg
--------------------------------------------------------------------------------
/src-dev/solar_system_of_js/dev.cljs:
--------------------------------------------------------------------------------
1 | (ns solar-system-of-js.dev
2 | (:require
3 | solar-system-of-js.core
4 | [figwheel.client :as fw]))
5 |
6 | ;; start figwheel
7 | (fw/start {})
8 |
9 |
--------------------------------------------------------------------------------
/src/solar_system_of_js/actions.cljs:
--------------------------------------------------------------------------------
1 | (ns solar-system-of-js.actions
2 | (:require-macros
3 | [cljs.core.async.macros :refer [go go-loop]])
4 | (:require
5 | [cljs.core.async :refer [> (filter immediate? step-map) (skip-step!))
136 | anim-chan (->> (filter animation? step-map) (multi-animate!))
137 | go-block-chans (->> (filter custom? step-map) (map second) (map :go-block) (mapv #(%)))
138 | wait-chans (cons anim-chan go-block-chans)]
139 | (go
140 | (doseq [c wait-chans]
141 | (")
419 | [:cam :x] {:a :_ :b 0 :duration 2}
420 | }
421 |
422 | ]))
423 |
424 | ;;----------------------------------------------------------------------
425 | ;; Action Loops
426 | ;;----------------------------------------------------------------------
427 |
428 | (defn update-orbit!
429 | [name- dt]
430 | (let [v (* dt (get-in @state [name- :angle-speed]))]
431 | (swap! state update-in [name- :angle] + v)))
432 |
433 | (defn tick-orbits!
434 | [dt]
435 | (when (:enable-orbits? @state)
436 | (update-orbit! :coffeescript dt)
437 | (update-orbit! :dart dt)
438 | (update-orbit! :clojurescript dt)))
439 |
440 | (defn tick-radar!
441 | [dt]
442 | (when (:enable-orbits? @state)
443 | (swap! state update-in [:radar :offset] + (* dt 400))))
444 |
445 | (defn start-loops!
446 | "These are looping animations that cannot be represented as slide transitions.
447 | (Slide transitions have to end at some point.)"
448 | []
449 | (let [c (chan)]
450 | (tap tick-tap c)
451 | (go-loop []
452 | (let [dt ( tween
57 | (keyword? tween) tweens))
58 |
59 | (defn animate!
60 | "Pass given animation values to the given callback.
61 | Returns a channel that closes when done."
62 | [state-path {:keys [a b duration tween] :or {tween :swing} :as opts}]
63 | (let [tween (resolve-tween tween)
64 | c (chan)
65 | resolve-var #(if (= % :_) (get-in @state state-path) %)
66 | a (resolve-var a)
67 | dv (- b a)]
68 | (tap tick-tap c)
69 | (go-loop [t 0]
70 | (let [dt ( (/ t duration)
73 | (min 1)
74 | (tween))
75 | v (+ a (* percent dv))]
76 | (swap! state assoc-in state-path v)
77 | (when (< t duration)
78 | (recur t)))
79 | (untap tick-tap c))))
80 |
81 | (defn multi-animate!
82 | "Helper for concurrent animations with `animate!`.
83 | Returns a channel that closes when all are done."
84 | [pairs]
85 | (let [anims (mapv #(apply animate! %) pairs)]
86 | (go
87 | (doseq [a anims]
88 | (html]]))
5 |
6 | (defn set-caption!
7 | [caption]
8 | (let [el (.getElementById js/document "caption")]
9 |
10 | (let [html (md->html caption)]
11 | (aset el "style" "opacity" "1")
12 | (aset el "innerHTML" html))
13 |
14 | ;; make all links open in new tab
15 | (let [elements (.querySelectorAll el "a")]
16 | (dotimes [i (aget elements "length")]
17 | (let [el (aget elements i)]
18 | (.setAttribute el "target" "_blank"))))))
19 |
20 | (defn on-caption-change
21 | [_key _atom {old-caption :caption} {new-caption :caption}]
22 | (when (not= old-caption new-caption)
23 | (set-caption! new-caption)))
24 |
25 | (add-watch state :caption-watcher on-caption-change)
26 |
--------------------------------------------------------------------------------
/src/solar_system_of_js/control.cljs:
--------------------------------------------------------------------------------
1 | (ns solar-system-of-js.control
2 | (:require
3 | [solar-system-of-js.actions :refer [slide-actions]]
4 | [solar-system-of-js.nav :refer [skip-slide! next-slide! prev-slide!]]
5 | [solar-system-of-js.canvas :refer [canvas]]
6 | cljsjs.hammer))
7 |
8 | (def key-names
9 | {37 :left
10 | 38 :up
11 | 39 :right
12 | 40 :down
13 | 32 :space})
14 |
15 | (def key-name #(-> % .-keyCode key-names))
16 |
17 | (defn key-down [e]
18 | (let [kname (key-name e)
19 | shift (.-shiftKey e)]
20 | (case kname
21 | :space (do
22 | (next-slide!)
23 | (.preventDefault e))
24 | :left (do
25 | (prev-slide!)
26 | (.preventDefault e))
27 | :right (do
28 | (if shift
29 | (skip-slide!)
30 | (next-slide!))
31 | (.preventDefault e))
32 | nil)))
33 |
34 | (defn on-swipe!
35 | [type-]
36 | (case type-
37 | "tap" (next-slide!)
38 | "swipeleft" (next-slide!)
39 | "swiperight" (prev-slide!)
40 | nil))
41 |
42 | (defn init-touch!
43 | []
44 | (doto (js/Hammer. canvas)
45 | (.on "swipeleft swiperight tap"
46 | #(on-swipe! (aget % "type")))))
47 |
48 | (defn init-controls!
49 | []
50 | (init-touch!)
51 | (.addEventListener js/window "keydown" key-down))
52 |
53 |
--------------------------------------------------------------------------------
/src/solar_system_of_js/core.cljs:
--------------------------------------------------------------------------------
1 | (ns solar-system-of-js.core
2 | (:require-macros
3 | [cljs.core.async.macros :refer [go go-loop]]
4 | [hiccups.core :refer [html]])
5 | (:require
6 | hiccups.runtime
7 | [cljs.core.async :refer [js {:google {:families families} :active #(close! c)}))
41 | c))
42 |
43 | (defn main
44 | []
45 |
46 | (let [loading-fonts (load-fonts! ["Roboto:100,300,400,700" "Open Sans"])]
47 | (init-page!)
48 |
49 | ;; initialize drawing canvas
50 | (init-canvas!)
51 |
52 | ;; initialize touch and key controls
53 | (init-controls!)
54 |
55 | ;; start animation heartbeat
56 | (start-ticking!)
57 |
58 | ;; do some state setup for the first slide
59 | (init-first-slide!)
60 |
61 | ;; go to slide listed in the url hash
62 | (sync-slide-to-hash!)
63 |
64 | ;; start any animation loops
65 | (start-loops!)
66 |
67 | ;; wait until fonts have loaded to start drawing (self-schedules after first draw)
68 | (go
69 | ( @state :js-face :r)
140 | dr (get-core-dr r)
141 | cr (- (get-core-radius i r) (/ dr 2))]
142 |
143 | ;; highlight the layer
144 | (line-width! dr)
145 | (circle! 0 0 cr)
146 | (stroke-style! "#1EA")
147 | (stroke!))))
148 |
149 | (defn draw-es-captions!
150 | [{:keys [es3 es5 es6 es7 es8]}]
151 | (save!)
152 | (doseq [[i es] (map-indexed vector [es3 es5 es6 es7 es8])]
153 | (when-not (zero? (:alpha es))
154 | (save!)
155 | (global-alpha! (:alpha es))
156 | (let [angle (/ PI 4)
157 | dx (cos angle)
158 | dy (sin angle)
159 | r (-> @state :js-face :r)
160 | dr (get-core-dr r)
161 | cr (- (get-core-radius i r) (/ dr 2))
162 | y (- (* dy cr))
163 | x0 (* dx cr)
164 | x1 250
165 | a 10
166 | at (/ a 3)]
167 |
168 | ;; highlight the layer
169 | (draw-highlight-layer! i)
170 |
171 | ;; point at layer
172 | (line-width! 5)
173 | (line-cap! "round")
174 | (stroke-line! x0 y x1 y)
175 | (stroke!)
176 |
177 | ;; draw caption
178 | (let [layer (get core-layers i)]
179 | (text-baseline! "middle")
180 | (text-align! "left")
181 | (fill-style! "#EEE")
182 | (font! "100 40px Roboto")
183 | (fill-text! (:name layer) (+ 10 x1) y)
184 | (fill-style! "#AAA")
185 | (font! "300 14px Roboto")
186 | (fill-text! (:desc layer) (+ 15 x1) (+ 30 y)))
187 |
188 | )
189 | (restore!)
190 | ))
191 | (restore!))
192 |
193 | (defn draw-sign!
194 | [{:keys [x y alpha font-alpha highlight]} caption offset]
195 | (when-not (zero? alpha)
196 | (save!)
197 | (translate! x y)
198 | (let [a 10
199 | w 100
200 | h 30]
201 | (translate! (- (* 2 a (* 1.2 offset))) 0)
202 |
203 | (save!)
204 | (global-alpha! alpha)
205 | (begin-path!)
206 | (move-to! (- a w) (- h))
207 | (line-to! (+ a w) (- h))
208 | (line-to! (- w a) (+ h))
209 | (line-to! (- (+ w a)) (+ h))
210 | (fill-style! (if highlight "#1EA" "#EEE"))
211 | (fill!)
212 | (restore!)
213 |
214 | (global-alpha! font-alpha)
215 | (text-baseline! "middle")
216 | (text-align! "center")
217 | (font! "300 24px Roboto")
218 | (fill-style! "#222")
219 | (fill-style! (if highlight "#222" "#222"))
220 | (fill-text! caption 0 0))
221 | (restore!)))
222 |
223 | (defn draw-transpiler!
224 | [opts]
225 | (draw-sign! opts "TRANSPILER" 0))
226 |
227 | (defn draw-linter!
228 | [opts]
229 | (draw-sign! opts "LINTER" 1))
230 |
231 | (defn draw-modulesys!
232 | [opts]
233 | (draw-sign! opts "MODULE SYS" 2))
234 |
235 | (defn draw-static-arc!
236 | [start-a da r-percent color]
237 | (save!)
238 |
239 | (let [max-r (- (-> @state :static :sphere :r) 24)
240 | min-r (-> @state :js-face :r)
241 | dr (- max-r min-r)
242 | r (+ min-r (* r-percent dr))]
243 |
244 | (fill-style! color)
245 | (begin-path!)
246 | (move-to! 0 0)
247 | (arc! 0 0 r start-a (+ start-a da) false)
248 | (close-path!)
249 | (fill!)
250 | )
251 | (restore!))
252 |
253 | (defn draw-static-text!
254 | [{:keys [alpha]} text y]
255 | (when-not (zero? alpha)
256 | (save!)
257 | (global-alpha! alpha)
258 | (text-baseline! "middle")
259 | (text-align! "left")
260 | (font! "300 100px Roboto")
261 | (fill-style! "#FFF")
262 | (fill-text! text 800 y)
263 | (restore!)))
264 |
265 | (defn draw-typescript!
266 | [opts]
267 | (draw-static-text! opts "TYPESCRIPT" -100))
268 |
269 | (defn draw-flow!
270 | [opts]
271 | (draw-static-text! opts "FLOW" 0))
272 |
273 | (defn draw-closure!
274 | [opts]
275 | (draw-static-text! opts "CLOSURE" 100))
276 |
277 | (defn draw-soundscript!
278 | [opts]
279 | (draw-static-text! opts "SOUNDSCRIPT" 200))
280 |
281 |
282 | (defn draw-staticsphere!
283 | [{:keys [arcs alpha r]}]
284 | (save!)
285 | (when-not (zero? alpha)
286 | (global-alpha! alpha)
287 | (circle! 0 0 r)
288 | (fill-style! "#BCC")
289 | (fill!)
290 |
291 | (let [max-arcs 4
292 | full-arcs (Math/floor arcs)
293 | partial-arc (- arcs full-arcs)
294 | top-a (/ PI -2)
295 | da (/ (* 2 PI) max-arcs)
296 | num-greyed-arcs (cond-> full-arcs
297 | (and (pos? full-arcs)
298 | (zero? partial-arc)) dec)
299 | ]
300 |
301 | ;; draw full arcs
302 | (doseq [i (range full-arcs)]
303 | (let [color (if (< i num-greyed-arcs) "#CDD" "#FFF")]
304 | (draw-static-arc! (+ top-a (* i da)) da 1 color)))
305 |
306 | ;; draw partial arc
307 | (when (pos? partial-arc)
308 | (draw-static-arc! (+ top-a (* full-arcs da)) da partial-arc "#FFF"))
309 |
310 | (when (= 1 alpha)
311 | (line-width! 24)
312 | (stroke-style! "#BCC")
313 | (let [r (-> @state :static :sphere :r)]
314 | (dotimes [i max-arcs]
315 | (stroke-line! 0 0 0 r)
316 | (rotate! da))))
317 | )
318 | )
319 | (restore!))
320 |
321 | (defn draw-static!
322 | [{:keys [title sphere angle typescript soundscript flow closure]}]
323 | (when-not (zero? (:alpha sphere))
324 | (save!)
325 |
326 | (save!)
327 | (global-alpha! (:alpha title))
328 | (font! "300 90px Roboto")
329 | (text-baseline! "middle")
330 | (text-align! "center")
331 | (fill-style! "#677")
332 | (fill-text! "STATIC TYPING" 0 -600)
333 | (restore!)
334 |
335 | (draw-staticsphere! sphere)
336 | (draw-typescript! typescript)
337 | (draw-soundscript! soundscript)
338 | (draw-flow! flow)
339 | (draw-closure! closure)
340 |
341 | (restore!)))
342 |
343 | (defn draw-orbit!
344 | [{:keys [r]}]
345 | (line-width! 10)
346 | (circle! 0 0 r)
347 | (stroke-style! "#566")
348 | (stroke!))
349 |
350 | (defn draw-radar!
351 | [{:keys [orbit offset]}]
352 | (when orbit
353 | (save!)
354 | (let [o (-> @state orbit)
355 | r (:r o)
356 | dx (cos (:angle o))
357 | dy (sin (:angle o))
358 | gap 40
359 | offset (mod offset (* 2 gap))
360 | x (* (:r o) dx)
361 | y (* (:r o) dy)
362 | lx (- dy)
363 | ly dx
364 | rx dy
365 | ry (- dx)
366 | ]
367 | (global-alpha! (/ (:alpha o) 2))
368 | (line-width! 20)
369 | (stroke-style! "#f7df1e")
370 |
371 | (loop [i 0]
372 | (let [r0 (+ offset (* i gap))
373 | w (* (+ 0.2 (/ r0 r)) gap)
374 | x0 (+ (- x (* dx r0)) (* lx w))
375 | y0 (+ (- y (* dy r0)) (* ly w))
376 | x1 (+ (- x (* dx r0)) (* rx w))
377 | y1 (+ (- y (* dy r0)) (* ry w))]
378 | (when (<= r0 r)
379 | (stroke-line! x0 y0 x1 y1)
380 | (recur (+ i 1)))))
381 | )
382 | (restore!)))
383 |
384 | (defn draw-planet!
385 | [{:keys [size angle r highlight]}]
386 | (let [x (* r (cos angle))
387 | y (* r (sin angle))]
388 |
389 | (circle! x y size)
390 | (fill-style! (if highlight "#FFF" "#566"))
391 | (fill!)
392 | ))
393 |
394 | (defn draw-coffeescript!
395 | [{:keys [alpha highlight] :as opts}]
396 | (when-not (zero? alpha)
397 | (save!)
398 | (global-alpha! alpha)
399 | (draw-orbit! opts)
400 | (draw-planet! opts)
401 | (when highlight
402 | (font! "100 200px Roboto")
403 | (text-baseline! "middle")
404 | (text-align! "center")
405 | (fill-style! "#566")
406 | (fill-text! "COFFEESCRIPT" 0 -1400))
407 | (restore!)))
408 |
409 | (defn draw-dart!
410 | [{:keys [alpha highlight] :as opts}]
411 | (when-not (zero? alpha)
412 | (save!)
413 | (global-alpha! alpha)
414 | (draw-orbit! opts)
415 | (draw-planet! opts)
416 | (when highlight
417 | (font! "100 200px Roboto")
418 | (text-baseline! "middle")
419 | (text-align! "center")
420 | (fill-style! "#566")
421 | (fill-text! "DART" 0 -1700))
422 | (restore!)))
423 |
424 | (defn draw-clojurescript!
425 | [{:keys [alpha highlight] :as opts}]
426 | (when-not (zero? alpha)
427 | (save!)
428 | (global-alpha! alpha)
429 | (draw-orbit! opts)
430 | (draw-planet! opts)
431 | (when highlight
432 | (font! "100 300px Roboto")
433 | (text-baseline! "middle")
434 | (text-align! "center")
435 | (fill-style! "#566")
436 | (fill-text! "CLOJURESCRIPT" 3800 0))
437 | (restore!)))
438 |
439 | (defn draw-other-langs!
440 | [opts]
441 | (save!)
442 | (font! "100 400px Roboto")
443 | (fill-style! "#DEE")
444 | (let [names [[:gwt "GWT"]
445 | [:websharper "WebSharper"]
446 | [:objective-j "Objective-J"]
447 | [:scala.js "Scala.js"]
448 | [:elm "Elm"]
449 | [:purescript "PureScript"]
450 | [:js_of_ocaml "Js_of_ocaml"]
451 | [:asm "Emscripten -> asm.js"]]
452 | rs (iterate (partial + 600) 2800)
453 | ys (iterate (partial + 600) -1800)]
454 | (doseq [[[name- text] r y] (map vector names rs ys)]
455 | (save!)
456 | (let [obj (get opts name-)
457 | alpha (:alpha obj)]
458 | (global-alpha! alpha))
459 | (draw-orbit! {:r r})
460 | (fill-text! text 7600 y)
461 | (restore!))
462 | (restore!)))
463 |
464 | ;;--------------------------------------------------------------------------------
465 | ;; Drawing Dispatch
466 | ;;--------------------------------------------------------------------------------
467 |
468 | (defn set-cam!
469 | "Set camera's position, zoom, and rotation."
470 | [{:keys [x y zoom angle]}]
471 | (translate! (/ width 2) (/ height 2))
472 | (translate! (- x) (- y))
473 | (scale! zoom zoom)
474 | (rotate! angle))
475 |
476 | (defn draw-progress!
477 | []
478 | (save!)
479 | (let [i (:slide @state)
480 | end-i (dec num-slides)
481 | len (* width (/ i end-i))]
482 |
483 | (begin-path!)
484 | (move-to! 0 0)
485 | (line-to! len 0)
486 | (line-width! 10)
487 | (stroke-style! "#ACC")
488 | (stroke!))
489 | (restore!))
490 |
491 | (def prev-state nil)
492 |
493 | (defn draw!
494 | "Draw the current state of the application."
495 | []
496 | (when (not= prev-state @state)
497 | (set! prev-state @state)
498 | (save!)
499 | (fill-style! "#222")
500 | (fill-rect! 0 0 width height)
501 | (set-cam! (:cam @state))
502 |
503 | (draw-title! (:title @state))
504 | (draw-js-core! (:js-core @state))
505 | (draw-es-captions! (:es-captions @state))
506 | (draw-highlight-layer! (:highlight-layer @state))
507 |
508 | (draw-transpiler! (:transpiler @state))
509 | (draw-linter! (:linter @state))
510 | (draw-modulesys! (:modulesys @state))
511 |
512 | (draw-static! (:static @state))
513 |
514 | (draw-radar! (:radar @state))
515 |
516 | (draw-js-face! (:js-face @state))
517 |
518 | (draw-coffeescript! (:coffeescript @state))
519 | (draw-dart! (:dart @state))
520 | (draw-clojurescript! (:clojurescript @state))
521 |
522 | (draw-other-langs! @state)
523 |
524 | (restore!)
525 | (draw-progress!))
526 |
527 | ;; self-schedule the next frame to draw
528 | (.requestAnimationFrame js/window draw!))
529 |
530 |
--------------------------------------------------------------------------------
/src/solar_system_of_js/math.cljs:
--------------------------------------------------------------------------------
1 | (ns solar-system-of-js.math)
2 |
3 | (def PI (aget js/Math "PI"))
4 | (def sin (aget js/Math "sin"))
5 | (def cos (aget js/Math "cos"))
6 |
--------------------------------------------------------------------------------
/src/solar_system_of_js/nav.cljs:
--------------------------------------------------------------------------------
1 | (ns solar-system-of-js.nav
2 | (:require-macros
3 | [cljs.core.async.macros :refer [go]])
4 | (:require
5 | [cljs.core.async :refer [ (aget js/location "hash") (subs 1) (js/parseInt))]
26 | (when (<= 0 i (dec num-slides))
27 | i)))
28 |
29 | (defn init-first-slide!
30 | "Do some setup that the first slide requires."
31 | []
32 | ;; execute first slide action
33 | (let [action (first slide-actions)]
34 | (skip-action! action))
35 |
36 | ;; save state of first slide
37 | (save-slide-state!)
38 |
39 | ;; set initial hash
40 | (when-not (hash-slide)
41 | (aset js/location "hash" 0)))
42 |
43 | (def in-transition?
44 | "True if we are in the middle of a slide's actions (i.e. animation)"
45 | (atom false))
46 |
47 | (defn sync-hash-to-slide!
48 | "Make url hash equal to current slide"
49 | []
50 | (aset js/location "hash" (:slide @state)))
51 |
52 | (defn next-slide!
53 | "Go to next slide if we can."
54 | []
55 | (when-not @in-transition?
56 | (when-let [action (get slide-actions (inc (:slide @state)))]
57 | (save-slide-state!)
58 | (swap! state update-in [:slide] inc)
59 | (sync-hash-to-slide!)
60 | (reset! in-transition? true)
61 | (go
62 | ( i cur)
94 | (dotimes [_ (- i cur)]
95 | (skip-slide! false))))
96 | (sync-hash-to-slide!))))
97 |
98 | (defn sync-slide-to-hash!
99 | "Make sure the slide reflects the current url hash."
100 | []
101 | (when-let [i (hash-slide)]
102 | (seek-slide! i)))
103 |
104 | (.addEventListener js/window "hashchange" sync-slide-to-hash!)
105 |
106 |
107 |
--------------------------------------------------------------------------------
/src/solar_system_of_js/state.cljs:
--------------------------------------------------------------------------------
1 | (ns solar-system-of-js.state
2 | (:require
3 | [solar-system-of-js.math :refer [PI]]))
4 |
5 | (def initial-state
6 | "Initial state of the application."
7 | {:slide 0
8 | :caption ""
9 | :title {:x 0
10 | :y -100
11 | :alpha 1}
12 | :cam {:x 0
13 | :y 0
14 | :zoom 1
15 | :angle 0}
16 | :js-face {:x 900
17 | :y 0
18 | :r 200
19 | :alpha 1
20 | :angle 0}
21 | :js-core {:x 0
22 | :y 0
23 | :r 200
24 | :alpha 0}
25 | :es-captions {:es3 {:alpha 0}
26 | :es5 {:alpha 0}
27 | :es6 {:alpha 0}
28 | :es7 {:alpha 0}
29 | :es8 {:alpha 0}}
30 | :highlight-layer nil
31 | :transpiler {:x 900
32 | :y 0
33 | :alpha 0
34 | :font-alpha 0}
35 | :linter {:x 900
36 | :y 70
37 | :alpha 0
38 | :font-alpha 0}
39 | :modulesys {:x 900
40 | :y 140
41 | :alpha 0
42 | :font-alpha 0}
43 | :static {:title {:alpha 0}
44 | :sphere {:alpha 0
45 | :r 200
46 | :arcs 0}
47 | :typescript {:alpha 0}
48 | :soundscript {:alpha 0}
49 | :closure {:alpha 0}
50 | :flow {:alpha 0}}
51 | :coffeescript {:alpha 0
52 | :size 50
53 | :highlight false
54 | :angle 0
55 | :angle-speed (/ PI 5)
56 | :r 900
57 | }
58 | :dart {:alpha 0
59 | :size 100
60 | :highlight false
61 | :angle 0
62 | :angle-speed (/ PI 10)
63 | :r 1400
64 | }
65 | :clojurescript {:alpha 0
66 | :size 100
67 | :highlight false
68 | :angle 0
69 | :angle-speed (/ PI 15)
70 | :r 2100
71 | }
72 | :radar {:orbit nil
73 | :offset 0}
74 | :gwt {:alpha 0}
75 | :websharper {:alpha 0}
76 | :objective-j {:alpha 0}
77 | :scala.js {:alpha 0}
78 | :elm {:alpha 0}
79 | :purescript {:alpha 0}
80 | :js_of_ocaml {:alpha 0}
81 | :asm {:alpha 0}})
82 |
83 | ;; Current state of the application.
84 | (defonce state
85 | (atom initial-state))
86 |
87 |
--------------------------------------------------------------------------------
/title.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/shaunlebron/solar-system-of-js/a5c53c9727622ac023963766323b6eb5d03f5226/title.png
--------------------------------------------------------------------------------