d3.time.clock()
clock.speed([speed])
clock.extent([extent])
clock.init()
clock.run()
clock.pause()
clock.seek
d3.svg.dial()
dial.clock([clock])
| d3.time.clock = function () { | |
| return d3_time_clock(0, 1, null); | |
| }; | |
| function d3_time_clock(t, speed, interval) { | |
| var dispatch = d3.dispatch('tick', 'run', 'pause', 'complete'); | |
| function clock() {} | |
| clock.speed = function(x) { | |
| if (!arguments.length) return speed; | |
| speed = x; | |
| return clock; | |
| }; | |
| // Tie to a scale instead, with default linear? | |
| clock.extent = function(x) { | |
| if (!arguments.length) return extent; | |
| extent = x; | |
| return clock; | |
| }; | |
| clock.run = function() { | |
| interval = window.setInterval(function() { | |
| dispatch.tick(++t); | |
| if (t >= extent) { | |
| clock.pause(); | |
| dispatch.complete(); | |
| } | |
| }, 1000 / speed); | |
| dispatch.run(); | |
| return clock; | |
| }; | |
| clock.pause = function() { | |
| window.clearInterval(interval); | |
| interval = null; | |
| dispatch.pause(); | |
| return clock; | |
| }; | |
| clock.init = function() { | |
| dispatch.tick(t = 0); | |
| return clock; | |
| }; | |
| clock.seek = function(dt) { | |
| t += Math.round(dt * speed); | |
| if (t >= extent) { | |
| clock.pause(); | |
| dispatch.complete(); | |
| } | |
| t = Math.max(0, Math.min(extent, t)); | |
| dispatch.tick(t); | |
| return clock; | |
| }; | |
| return d3.rebind(clock, dispatch, 'on'); | |
| } | |
| d3.svg.dial = function() { | |
| var clock = d3.time.clock(), | |
| state = null; | |
| function pop(x) { | |
| x.attr('transform', 'scale(0)') | |
| .transition() | |
| .ease('elastic') | |
| .attr('transform', 'scale(1)'); | |
| } | |
| function dial(g) { | |
| g.each(function() { | |
| var g = d3.select(this); | |
| g.call(d3.behavior.drag() | |
| .origin(function() { return { x: 0, y: 0}; }) | |
| .on('drag', function() { | |
| console.log(d3.event) | |
| clock.pause(); | |
| clock.seek(Math.floor(d3.event.dx / 5)); | |
| }) | |
| ); | |
| g.style('cursor', 'e-resize'); | |
| g.append('circle') | |
| .attr('r', 42) | |
| .attr('fill', '#f6f6f6'); | |
| g.append('path') | |
| .attr('fill', '#cccccc') | |
| .attr('class', 'arc'); | |
| var btn = g.append('g') | |
| .attr('class', 'btn') | |
| .attr('transform', 'translate(0, 22)') | |
| .style('cursor', 'pointer') | |
| .on('click', function() { | |
| switch (state) { | |
| case 'running': | |
| clock.pause(); | |
| break; | |
| case 'paused': | |
| clock.run(); | |
| break; | |
| case 'complete': | |
| clock.init().run(); | |
| break; | |
| } | |
| }); | |
| btn.append('rect') | |
| .attr('width', 12) | |
| .attr('height', 12) | |
| .style('fill', 'transparent') | |
| .attr('transform', 'translate(-6,-6)'); | |
| var btn_d = { | |
| pause: 'M-5 -6V6H-1V-6H-5M5 -6V6H1V-6H5', | |
| play: 'M-4 -7V7L7 0Z', | |
| replay: 'M1 -7L7 -3L1 1V-2A4 4 0 1 0 4 1H 6A6 6 0 1 1 1 -5Z' | |
| }; | |
| var path = btn.append('path').attr('d', btn_d.play); | |
| clock.on('run.dial', function() { | |
| state = 'running'; | |
| path.attr('d', btn_d.pause).call(pop); | |
| }); | |
| clock.on('pause.dial', function() { | |
| if (state == 'paused') return; | |
| state = 'paused'; | |
| path.attr('d', btn_d.play).call(pop); | |
| }); | |
| clock.on('complete.dial', function() { | |
| state = 'complete'; | |
| path.attr('d', btn_d.replay).call(pop); | |
| }); | |
| clock.on('tick.dial', function(t) { | |
| g.select('path') | |
| .datum(t) | |
| .attr('d', d3.svg.arc() | |
| .outerRadius(42) | |
| .innerRadius(0) | |
| .startAngle(0) | |
| .endAngle(function(d) { | |
| return 2 * Math.PI * d / clock.extent(); | |
| }) | |
| ); | |
| }); | |
| }); | |
| } | |
| dial.clock = function(x) { | |
| if (!arguments.length) return clock; | |
| clock = x; | |
| return dial; | |
| }; | |
| return dial; | |
| }; |