console.clear() var width = innerWidth, height = innerHeight, walks = d3.range(width + 1).map(x => ({x, b: true, n: 0, updated: true}) ), y = 0, isDown = true, color = d3.scaleOrdinal().range(d3.schemeCategory10) var canvas = d3.select('body').html('') .append('canvas') .at({width, height}) .on('mousemove', function(){ var pos = d3.mouse(this) minSize = d3.scaleLinear().domain([0, width]).range([.1, 2])(pos[0]) change = d3.scaleLinear().domain([0, height]).range([.1, 10])(pos[1]) }) .node() var ctx = canvas.getContext('2d') var sizeScale = d3.scaleLinear().domain([0, width]).range([.5, 5]) if (window.timer) window.timer.stop() window.timer = d3.interval(t => { ctx.fillStyle = 'rgba(255,255,255,0.03)'; ctx.fillRect(0, 0, width, height); d3.range(1).forEach(update) }, 1) function update(){ d3.nestBy(walks.filter(d => d.updated), d => d.n).forEach(colors => { ctx.beginPath() ctx.fillStyle = color(colors.key) // ctx.fillStyle = colors.key % 2 ? '#f0f' : '#000' colors.forEach(drawRect) ctx.fill() }) function drawRect(d){ var x0 = Math.round(Math.cos(y/100)*(d.x)) var y0 = Math.round(Math.sin(y/100)*(d.x)) var size = sizeScale(d.x) if (d.n % 2) size = size*1 ctx.rect(x0 + width/2, y0 + height/2, size, size) } // update data walks.filter(d => d.updated).forEach(d => { d.x = d3.clamp(0, d.x + (Math.random() - .5)*2, width) d.y = y d.updated = false }) // merge d3.nestBy(walks.filter(d => d.y == y), d => Math.round(d.x)) .map(d => { var x = +d.key var next = _.minBy(d, d => d.n*10 + Math.abs(d.x - x)) walks[x].x = next.x walks[x].n = next.n walks[x].b = next.b walks[x].updated = true }) var run = [] walks.forEach((d, i) => { if (d.updated){ if (run.length > 30){ var n = d.n + 1 run.slice(0, 1).forEach(d => { d.updated = true // d.x = i d.n = n d.b = !d.b }) } run = [] } else { d.y = -1000 run.push(d) } }) y++ // y = y + (isDown ? 1 : -1) if (y < 0 || y > height){ // y = 0 // isDown = !isDown } }