(function(d3) { 'use strict' var height, width, count, duration, array, columns, parameters, grid, scales, svg, control, slider, item, loop, factory, shape, collect height = 500 width = 960 count = 200 duration = 1000 array = Array.apply(this, {length: count}) // store variable names in a single data structure // so we can work with them programmatically parameters = { 'hue': [0, 360], 'saturation': [0, 1], 'lightness': [0, 1], 'opacity': [0, 1], 'rotation': [-360, 360], 'skew': [-360, 360], 'size': [0, 2] } columns = Object.keys(parameters).length grid = width / columns scales = {} Object.keys(parameters).forEach(function(parameter) { var scale scale = d3.scaleLinear() .domain([height, 0]) .range(parameters[parameter]) scales[parameter] = scale }) factory = function() { var f, hue, saturation, lightness, opacity, rotation = 0, skew = 0, size = 0 f = function(selection) { var color, transform color = d3.hsl(hue, saturation, lightness, opacity) transform = 'rotate(' + rotation + ')skewX(' + skew + ')scale(' + size + ')' selection .append('rect') .style('fill', color.toString()) .style('stroke', 'black') .attr('height', 0) .attr('width', 0) .transition('rotate') .duration(duration) .attr('transform', transform) } // configuration methods Object.defineProperties(f, { hue: { get: () => hue, set: _ => (hue = _, f), }, saturation: { get: () => saturation, set: _ => (saturation = _, f), }, lightness: { get: () => lightness, set: _ => (lightness = _, f), }, opacity: { get: () => opacity, set: _ => (opacity = _, f), }, rotation: { get: () => rotation, set: _ => (rotation = _, f), }, skew: { get: () => skew, set: _ => (skew = _, f), }, size: { get: () => size, set: _ => (size = _, f), } }); return f } shape = factory() svg = d3.select('div.target') .append('svg') .attr('height', height) .attr('width', width) item = svg.append('g') .classed('items', true) .attr('transform', 'translate(' + grid * 0.5 + ',0)') .selectAll('g.item') .data(array) .enter() .append('g') .classed('item', true) item.attr('transform', function(d, i) { var x, y x = i % columns * grid y = Math.floor(i / columns) * grid return 'translate(' + x + ',' + y + ')' }) slider = function(selection) { var slider_width, x_offset slider_width = grid x_offset = (grid - slider_width) * 0.5 selection.append('rect') .attr('x', x_offset) .attr('height', 10) .attr('width', slider_width) .style('stroke', 'white') .style('stroke-width', 3) } control = svg.append('g') .classed('controls', true) .selectAll('g.control') .data(Object.keys(parameters)) .enter() .append('g') .attr('class', 'control') .attr('data-control', function(d) { return d }) .attr('transform', function(d, i) { var x x = grid * i return 'translate(' + x + ',0)' }) control .append('g') .classed('slider', true) .each(function() { d3.select(this).call(slider) }) control .append('rect') .attr('height', height) .attr('width', grid) .style('opacity', 0) control .append('text') .classed('label', true) .attr('transform', 'translate(' + (grid * 0.5) + ',' + (height * 0.5) + ') rotate(270)') .text(function(d) { return d.replace(/_/g, ' ') }) control.on('mousemove', function() { var new_y new_y = d3.mouse(this)[1] d3.select(this).select('g.slider rect') .transition() .ease(d3.easeLinear) .attr('y', new_y) }) collect = function() { control.each(function(d) { var y, value y = d3.select(this).select('g.slider rect') .node() .getBBox() .y value = scales[d](y) shape[d] = value }) } loop = function(selection) { collect() selection .call(shape) .select('rect') .transition('change') .duration(duration) .delay(function(d, i) { return duration / count * i }) .attr('height', grid) .attr('width', grid) .transition('remove') .duration(duration) .attr('height', 0) .attr('width', 0) .remove() } item.call(loop) setInterval(function() { item.selectAll('rect').remove() item.call(loop) }, duration * 3) }).call(this, d3)