Infinite argyle with random ColorBrewer and d3 4.0 color scales.
| (function(){ | |
| var runs = 0, | |
| scheme; | |
| var colorSchemes = d3.shuffle([ | |
| ["#e0f3db", "#a8ddb5", "#43a2ca"], // GnBu | |
| ["#ece2f0", "#a6bddb", "#1c9099"], // PuBuGn | |
| ["#fde0dd", "#fa9fb5", "#c51b8a"], // RdPu | |
| ["#f7fcb9", "#addd8e", "#31a354"], // YlGn | |
| ["#edf8b1", "#7fcdbb", "#2c7fb8"], // YlGnBlue | |
| ["#ffeda0", "#feb24c", "#f03b20"], // YlOrRd | |
| ["#d8b365", "#f6e8c3", "#c7eae5", "#5ab4ac"], // BrBG | |
| ["#fc8d59", "#ffffbf", "#99d594"], // Spectral3 | |
| ["#d7191c", "#fdae61", "#abdda4", "#2b83ba"], // Spectral4 | |
| ["#fc8d59", "#ffffbf", "#91bfdb"], // RdYlBu | |
| ["#fc8d59", "#fee090", "#e0f3f8", "#91bfdb"], // RdYlBu | |
| d3.interpolateViridis, | |
| d3.interpolatePlasma, | |
| d3.interpolateWarm, | |
| d3.interpolateCool, | |
| d3.interpolateRainbow | |
| ]); | |
| var greys = ["#222", "#666", "#ccc", "#f7f7f7"]; | |
| var patterns = [standard, checker, wave, bowtie, columns]; | |
| function getPattern() { | |
| var r = Math.random(), | |
| totalColors = r < 0.1 ? 5 : r < 0.5 ? 4 : 3, | |
| colors = scheme.slice(0), | |
| stitchColors; | |
| // Pad with greys | |
| if (colors.length < totalColors) { | |
| d3.shuffle(greys); | |
| colors = colors.concat(greys.slice(0, totalColors - colors.length)); | |
| } else { | |
| colors = d3.shuffle(colors).slice(0, totalColors); | |
| // Mix in a grey sometimes anyway | |
| if (Math.random() < 0.3) { | |
| colors[0] = choose(greys); | |
| } | |
| } | |
| stitchColors = getStitchColors(colors); | |
| d3.shuffle(colors); | |
| return { | |
| stitch: stitchColors, | |
| color: choose(patterns)(colors), | |
| }; | |
| } | |
| // Set stitch coloring based on highest contrast ratio | |
| // https://www.w3.org/TR/2008/REC-WCAG20-20081211/#contrast-ratiodef | |
| function getStitchColors(colors) { | |
| var l = colors.map(relativeLuminance), | |
| stitchColors = {}, | |
| splice = colors.length === 5 || (colors.length === 4 && Math.random() < 0.35), | |
| primary, | |
| ratios; | |
| // Calculate contrast ratios for other colors | |
| ratios = colors.map(function(color, i){ | |
| return colors.map(function(d, j){ | |
| return (Math.max(l[i], l[j]) + 0.05) / (Math.min(l[i], l[j]) + 0.05); | |
| }); | |
| }); | |
| // Color with highest average contrast | |
| primary = d3.scan(ratios, function(a, b) { return 1 / d3.mean(a) - 1 / d3.mean(b); }); | |
| // Dedicated stitch color | |
| if (splice) { | |
| colors.forEach(function(color, i){ | |
| if (i !== primary) { | |
| stitchColors[color] = colors[primary]; | |
| } | |
| }); | |
| colors.splice(primary, 1); | |
| // Background colors as stitch colors on each other | |
| } else { | |
| colors.forEach(function(color, i){ | |
| var secondary; | |
| if (i === primary) { | |
| secondary = d3.scan(ratios[i], function(a, b) { return 1 / a - 1 / b; }); | |
| stitchColors[color] = colors[secondary]; | |
| } else { | |
| stitchColors[color] = colors[primary]; | |
| } | |
| }); | |
| } | |
| return stitchColors; | |
| } | |
| // sRGB relative luminance | |
| // https://www.w3.org/TR/2008/REC-WCAG20-20081211/#relativeluminancedef | |
| function relativeLuminance(color) { | |
| var rgb = d3.rgb(color); | |
| var weights = ["r", "g", "b"].map(function(key){ | |
| var val = rgb[key] / 255; | |
| return val <= 0.03928 ? | |
| val / 12.92 : | |
| Math.pow((val + 0.055) / 1.055, 2.4); | |
| }); | |
| return weights[0] * 0.2126 + weights[1] * 0.7152 + weights[2] * 0.0722; | |
| } | |
| function nextColorScheme() { | |
| var offset, | |
| numColors; | |
| // Move first to last | |
| colorSchemes.push(scheme = colorSchemes.shift()); | |
| // For d3 scales, get random evenly spaced colors | |
| if (typeof scheme === "function") { | |
| offset = Math.random(); | |
| numColors = choose([3, 4]); | |
| scheme = d3.range(numColors).map(function(d){ | |
| return scheme((offset + d / (numColors)) % 1); | |
| }); | |
| } | |
| } | |
| function standard(colors){ | |
| return function(d){ | |
| return d[0] % 2 ? | |
| colors[0] : | |
| colors[1 + d[1] % (colors.length - 1)]; | |
| }; | |
| } | |
| function checker(colors){ | |
| return function(d) { | |
| return d[0] % 2 ? | |
| colors[0] : | |
| colors[1 + ((d[1] + d[0] / 2) % (colors.length - 1))]; | |
| }; | |
| } | |
| function wave(colors){ | |
| return function(d){ | |
| return d[0] % 2 ? | |
| colors[0] : | |
| d[0] % 4 ? | |
| colors[2 + Math.floor(d[0] / 4) % (colors.length - 2)] : | |
| colors[1]; | |
| }; | |
| } | |
| function bowtie(colors){ | |
| if (colors.length < 4) { | |
| return standard(colors); | |
| } | |
| return function(d){ | |
| return d[0] % 2 ? | |
| colors[d[1] % 2] : | |
| colors[2 + (d[0] / 2) % 2]; | |
| }; | |
| } | |
| function columns(colors) { | |
| if (colors.length < 4) { | |
| return checker(colors); | |
| } | |
| return function(d) { | |
| return d[0] % 2 ? | |
| colors[d[1] % 2] : | |
| colors[2 + d[1] % 2]; | |
| }; | |
| } | |
| function choose(arr) { | |
| return arr[Math.floor(Math.random() * arr.length)]; | |
| } | |
| window.argyle = function(){ | |
| if (runs++ % 3 === 0) { | |
| nextColorScheme(); | |
| } | |
| return getPattern(); | |
| }; | |
| })(); |
| <!DOCTYPE html> | |
| <html lang="en"> | |
| <head> | |
| <meta charset="utf-8" /> | |
| <style> | |
| path { | |
| stroke: none; | |
| } | |
| line { | |
| stroke: #444; | |
| stroke-width: 1px; | |
| stroke-linecap: round; | |
| opacity: 0.8; | |
| } | |
| @media (-webkit-min-device-pixel-ratio: 1.5), | |
| (min-resolution: 1.5dppx) { | |
| line { | |
| stroke-width: 0.6px; | |
| opacity: 0.95; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <script src="https://d3js.org/d3.v4.min.js"></script> | |
| <script src="argyle.js"></script> | |
| <script> | |
| var angle = Math.PI / 6, | |
| rx = 30, | |
| ry = rx / Math.tan(angle), | |
| diagonal = Math.sqrt(rx * rx + ry * ry); | |
| width = 960, | |
| height = Math.ceil(500 / ry) * ry; | |
| var svg = d3.select("body").append("svg") | |
| .attr("width", width) | |
| .attr("height", height); | |
| var diamond = d3.symbol() | |
| .type(d3.symbolDiamond) | |
| .size(ry * rx * 2); | |
| // Array of [row, column] positions | |
| var positions = d3.merge(d3.range(Math.ceil(height / ry) + 1).map(function(r){ | |
| return d3.range(Math.ceil(width / (rx * 2)) + 1).map(function(c){ | |
| return [r, c]; | |
| }); | |
| })); | |
| var g = svg.selectAll(".diamond") | |
| .data(positions) | |
| .enter() | |
| .append("g") | |
| .attr("transform", function(d){ | |
| // Odd-r coordinates | |
| var x = (d[0] % 2 ? rx : 0) + d[1] * rx * 2, | |
| y = d[0] * ry; | |
| return "translate(" + x + " " + y + ")"; | |
| }); | |
| var background = g.append("path") | |
| .attr("d", diamond); | |
| g.append("line") | |
| .attr("x1", rx / 2) | |
| .attr("x2", -rx / 2); | |
| g.append("line") | |
| .attr("x1", -rx / 2) | |
| .attr("x2", rx / 2); | |
| // Separate lines + calculated dasharray to prevent cuts | |
| var stitch = g.selectAll("line") | |
| .attr("y1", -ry / 2) | |
| .attr("y2", ry / 2) | |
| .attr("stroke-dasharray",(diagonal / 30) + "," + (diagonal / 30)) | |
| .attr("stroke-dashoffset",(-diagonal / 60)); | |
| recolor(true); | |
| function recolor(first) { | |
| var pattern = argyle(); | |
| var t = d3.transition() | |
| .delay(first ? 0 : 1000) | |
| .duration(first ? 0 : 1000) | |
| .on("end", recolor); | |
| background.transition(t) | |
| .styleTween("fill", function(d){ | |
| return d3.interpolateLab(getComputedStyle(this).getPropertyValue("fill"), pattern.color(d)); | |
| }); | |
| stitch.transition(t) | |
| .styleTween("stroke", function(d){ | |
| return d3.interpolateLab(getComputedStyle(this).getPropertyValue("stroke"), pattern.stitch[pattern.color(d)]); | |
| }); | |
| } | |
| d3.select(self.frameElement).style("height", height + "px"); | |
| </script> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
