Skip to content

Instantly share code, notes, and snippets.

@HarryStevens
Last active February 17, 2023 15:22
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save HarryStevens/cc5af3c0f0bf434e6623a88b51c7cf51 to your computer and use it in GitHub Desktop.
Save HarryStevens/cc5af3c0f0bf434e6623a88b51c7cf51 to your computer and use it in GitHub Desktop.
Radial Gradient Voronoi
license: gpl-3.0

Combine a radial gradient and Voronoi diagram to make beautiful patterns.

<!DOCTYPE html>
<html>
<head>
<style>
body {
margin: 0;
}
</style>
</head>
<body>
<script src="https://unpkg.com/geometric@2.5.0/build/geometric.min.js"></script>
<script type="module">
import { range } from "https://cdn.skypack.dev/d3-array@3";
import { Delaunay } from "https://cdn.skypack.dev/d3-delaunay@6";
import { randomUniform } from "https://cdn.skypack.dev/d3-random@3";
import { select } from "https://cdn.skypack.dev/d3-selection@3";
import { interval } from "https://cdn.skypack.dev/d3-timer@3";
import { transition } from "https://cdn.skypack.dev/d3-transition@3";
const width = window.innerWidth, height = window.innerHeight;
// Generate some random data
const xr = randomUniform(0, width);
const yr = randomUniform(0, height);
const data = range(200).map(() => [ xr(), yr() ]);
const polygons = Array
.from(
Delaunay
.from(data)
.voronoi([0, 0, width, height])
.cellPolygons(),
(p, i) => Object.assign(p, { data: data[i] })
)
.map(d => {
// To avoid a thin black line between each circle,
// increase the size of each voronoi polygon by .99px
const o = d.map(p => {
return geometric.pointTranslate(
p,
geometric.lineAngle([d.data, p]),
0.99
);
});
o.data = d.data;
return o;
});
// Lists of colors to loop through
const colorArray = [
["#fff7f3", "#fde0dd", "#fcc5c0", "#fa9fb5", "#f768a1", "#dd3497", "#ae017e", "#7a0177", "#49006a"],
["#ffffd9", "#edf8b1", "#c7e9b4", "#7fcdbb", "#41b6c4", "#1d91c0", "#225ea8", "#253494", "#081d58"],
["#ffffcc", "#ffeda0", "#fed976", "#feb24c", "#fd8d3c", "#fc4e2a", "#e31a1c", "#bd0026", "#800026"]
];
let currArray = 0;
let colors = colorArray[currArray].map((d, i, e) => ({ color: d, pct: i / (e.length - 1) * 100 }));
const svg = select("body").append("svg")
.attr("width", width)
.attr("height", height);
const rect = svg.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", colors[colors.length - 1].color);
const defs = svg.append("defs");
const gradient = defs.append("radialGradient")
.attr("id", "gradient");
updateColors();
defs.selectAll("clipPath")
.data(polygons)
.enter().append("clipPath")
.attr("id", (d, i) => `clip-path-${i}`)
.append("polygon")
.attr("points", d => d);
svg.selectAll("circle")
.data(polygons)
.enter().append("circle")
.attr("r", 50)
.attr("cx", d => d.data[0])
.attr("cy", d => d.data[1])
.style("clip-path", (d, i) => `url(#clip-path-${i})`)
.style("fill", "url(#gradient)");
interval(updateColors, 2000);
function updateColors(){
colors = colorArray[currArray].map((d, i, e) => ({color: d, pct: i / (e.length - 1) * 100}));
let stops = gradient.selectAll("stop")
.data(colors);
stops.transition().duration(1900)
.attr("stop-color", d => d.color);
stops.enter().append("stop")
.attr("offset", d => `${d.pct}%`)
.attr("stop-color", d => d.color);
rect.transition().duration(1900)
.style("fill", colors[colors.length - 1].color);
++currArray;
if (currArray === colorArray.length) currArray = 0;
}
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment