JSFiddle - React, Tailwind, and code Playground
by knrm720
HTML
<script src="https://cdnjs.cloudflare.com/ajax/libs/bacon.js/0.7.53/Bacon.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/stats.js/r11/Stats.js"></script>
<script src="https://cdn.jsdelivr.net/kefir/3.1.0/kefir.js"></script>
<p>
In this demo each branch of the tree represented by two streams
(Top and Angle) and has a parent branch. The Angle stream depends on
parent's Angle and on additional Oscillation stream.
A Top stream depends on parent's Top and self Angle streams.
Having parent's Top and child's Top we can draw a line,
all these lines form the tree, that you can see right now.
</p>
<label>
Library:
<select id="lib">
<option value="Kefir">Kefir</option>
<option value="Bacon">Bacon</option>
</select>
</label>
<br>
<label>
Depth:
<select id="depth">
<option value="1">1</option>
<option value="2">2</option>
<option value="3">3</option>
<option value="4">4</option>
<option value="5">5</option>
<option value="6">6</option>
<option value="7">7</option>
<option value="8">8</option>
<option value="9">9</option>
<option value="10">10</option>
<option value="11">11</option>
<option value="12">12</option>
<option value="13">13</option>
<option value="14">14</option>
<option value="15">15</option>
<option value="16">16</option>
<option value="17">17</option>
<option value="18">18</option>
</select>
</label>
<br>
<label>
<input type="checkbox" id="partial-render"> Do not render all branches, just calculate
</label>
<br>
Total branches: <strong id="total"></strong>
<br>
<canvas id="canvas" width="500" height="300"></canvas>
CoffeeScript
# parse URL query
window.queryParams = do ->
result = {}
for param in location.search.slice(1).split('&')
[key, value] = param.split('=')
result[key] = value
result
window.getParam = (name, notFound) ->
if (name of queryParams)
queryParams[name]
else
notFound
DEFAULT_DEPTH = 7
DEFAULT_LIB = 'Kefir'
# Setup selects
$('#lib').val(getParam('lib', DEFAULT_LIB)).change ->
location.search = "?lib=#{ $(this).val() }&depth=#{ getParam('depth', DEFAULT_DEPTH) }&partial_render=#{ getParam('partial_render', 'no') }"
$('#depth').val(getParam('depth', DEFAULT_DEPTH)).change ->
location.search = "?lib=#{ getParam('lib', DEFAULT_LIB) }&depth=#{ $(this).val() }&partial_render=#{ getParam('partial_render', 'no') }"
$('#partial-render').prop('checked', getParam('partial_render') == 'yes').change ->
val = if $(this).prop('checked') then 'yes' else 'no'
location.search = "?lib=#{ getParam('lib', DEFAULT_LIB) }&depth=#{ getParam('depth', DEFAULT_DEPTH) }&partial_render=#{ val }"
PARTIAL_RENDER = (getParam('partial_render') == 'yes')
# Setup canvas
lines = []
canvasEl = document.getElementById('canvas')
ctx = canvasEl.getContext('2d')
ctx.strokeStyle = "green"
stats = new Stats()
document.body.appendChild( stats.domElement )
renderLoop = ->
stats.begin()
stats.end()
ctx.clearRect(0, 0, 500, 300)
for line in lines
if !PARTIAL_RENDER || line.width >= 1
ctx.beginPath()
ctx.moveTo(line.x1, line.y1)
ctx.lineTo(line.x2, line.y2)
ctx.lineWidth = line.width;
ctx.stroke()
requestAnimationFrame(renderLoop)
renderLoop()
# Utils
randomize = (x, a = .1) ->
x * (Math.random() * a * 2 + 1 - a)
oscillationGenerator = (ticks) ->
(frequency) ->
min = 0
max = Math.PI * 2
cur = 0
add = Math.PI / 25 * frequency
ticks.map ->
cur += add
Math.sin(cur)
# Branch represents tree branch
class BranchKefir
oscillation: oscillationGenerator(Kefir.interval(1000 /...