Infinite argyle with random ColorBrewer and d3 4.0 color scales.
Fork of https://bl.ocks.org/veltman/f24fba4f6549639cacfd4d0a50e9d4b8 but drawing into a pattern.
Infinite argyle with random ColorBrewer and d3 4.0 color scales.
Fork of https://bl.ocks.org/veltman/f24fba4f6549639cacfd4d0a50e9d4b8 but drawing into a pattern.
(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 = 3, // 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> | |
html, body { | |
height: 100% | |
} | |
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> | |
"use strict"; | |
var angle = Math.PI / 6, | |
rx = 30, | |
ry = rx / Math.tan(angle), | |
diagonal = Math.sqrt(rx * rx + ry * ry); | |
var svg = d3.select("body").append("svg") | |
.attr("width", "100%") | |
.attr("height", "100%"); | |
var diamond = d3.symbol() | |
.type(d3.symbolDiamond) | |
.size(ry * rx * 2); | |
// Array of [row, column] positions | |
var positions = (function () { | |
var arr = []; | |
for (var i = 0; i < 5; ++i) { | |
for (var j = 0; j < 5; ++j) { | |
arr.push([i, j]); | |
} | |
} | |
return arr; | |
}()); | |
var mainG = svg.append("g"); | |
var defs = svg.append("defs"); | |
var pattern = defs.append("pattern") | |
.attr("id", "argyle") | |
.attr("patternUnits", "userSpaceOnUse") | |
.attr("x", 0) | |
.attr("y", 0) | |
.attr("width", rx * 4) | |
.attr("height", ry * 4); | |
var g = pattern.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)); | |
mainG.append("circle") | |
.attr("fill", "url(#argyle)") | |
.attr("cx", "50%") | |
.attr("cy", "50%") | |
.attr("r", "25%"); | |
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)]); | |
}); | |
} | |
</script> | |
</body> | |
</html> |