Skip to content

Instantly share code, notes, and snippets.

@nbremer
Last active June 19, 2016 13:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nbremer/a633e364466bfe1ac5fe5e594266e2f2 to your computer and use it in GitHub Desktop.
Save nbremer/a633e364466bfe1ac5fe5e594266e2f2 to your computer and use it in GitHub Desktop.
Random gradients and orientations - Hexagons - 2 palettes
height: 960

An experiment where I wanted to randomly fill a page with hexagons. Each hexagon has its own random gradient that is randomly oriented along 3 possible angles. The colors at both ends are then also randomly chosen.

This example has the option to choose between a dark rainbow or green color palette. Another version uses only the dark rainbow palette.

Built with blockbuilder.org

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Hexagonal SVG gradient experiment</title>
<style>
body {
/*background-color: #262626;*/
text-align: center;
}
</style>
<script type="text/javascript" src="http://d3js.org/d3.v3.js"></script>
</head>
<body>
<div id="hexmap"></div>
<script>
///////////////////////////////////////////////////////////////////////////
//////////////////// Set up and initiate svg containers ///////////////////
///////////////////////////////////////////////////////////////////////////
var hexRadius = 10,
SQRT3 = Math.sqrt(3),
hexWidth = SQRT3 * hexRadius,
hexHeight = 2 * hexRadius;
var margin = {
top: hexHeight / 2 + 5,
right: 0,
bottom: 0,
left: hexWidth + 5
};
var width = 960 - margin.left - margin.right;
var height = 960 - margin.top - margin.bottom;
//SVG container
var svg = d3.select('#hexmap')
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var defs = svg.append("defs");
var colors = ["#EFB605", "#E9A501", "#E48405", "#E34914", "#DE0D2B", "#CF003E", "#B90050", "#A30F65", "#8E297E", "#724097", "#4F54A8", "#296DA4", "#0C8B8C", "#0DA471", "#39B15E", "#7EB852"];
var colors2 = ['#1B676B', '#519548', '#88C425', "#BEF202", "#EAFDE6"];
var gradRotations = [0, 60, 120];
var animationID,
animationLoop = 1,
extraStep = 0,
alpha = 1,
reduceAlpha = false,
keepGrowing = true;
///////////////////////////////////////////////////////////////////////////
/////////////////////// Calculate hexagon variables ///////////////////////
///////////////////////////////////////////////////////////////////////////
var SQRT3 = Math.sqrt(3),
hexWidth = SQRT3 * hexRadius,
hexHeight = 2 * hexRadius;
var hexagonPoly = [[0,-1],[SQRT3/2,0.5],[0,1],[-SQRT3/2,0.5],[-SQRT3/2,-0.5],[0,-1],[SQRT3/2,-0.5]];
var hexagonPath = "m" + hexagonPoly.map(function(p){ return [p[0]*hexRadius, p[1]*hexRadius].join(','); }).join('l') + "z";
var mapColumns = Math.floor(width/(SQRT3 * hexRadius)) + 1,
mapRows = Math.floor(height/(2 * hexRadius * 0.75));
///////////////////////////////////////////////////////////////////////////
//////////////// Calculate hexagon centers and put into array /////////////
///////////////////////////////////////////////////////////////////////////
var points = [];
var pointsByIndex = [];
var counter = 0;
for (var i = 0; i < mapRows; i++) {
for (var j = 0; j < mapColumns; j++) {
var a;
var b = (3 * i) * hexRadius / 2;
if (i % 2 === 0) {
a = Math.sqrt(3) * j * hexRadius;
} else {
a = Math.sqrt(3) * (j - 0.5) * hexRadius;
}//else
points.push({
x: a, y: b, rowIndex: i, colIndex: j, fill: false
});
pointsByIndex[i + "-" + j] = counter;
counter += 1;
}//for j
}//for i
///////////////////////////////////////////////////////////////////////////
///////////////////////////// Place hexagons //////////////////////////////
///////////////////////////////////////////////////////////////////////////
svg.append("g")
.selectAll(".hexagon")
.data(points)
.enter().append("path")
.attr("class", "hexagon")
.attr("d", function (d) { return "M" + d.x + "," + d.y + hexagonPath; })
.style("stroke", "none")
.style("fill", "none")
.style("stroke-opacity", 0)
.style("fill-opacity", 0);
///////////////////////////////////////////////////////////////////////////
///////////////////////////// Fill hexagons ///////////////////////////////
///////////////////////////////////////////////////////////////////////////
//Start it off in the near middle
var centerRow = Math.round(mapRows/2),
centerCol = Math.round(mapColumns/2),
filledHexs = [];
filledHexs[centerRow+"-"+centerCol] = {x: centerRow, y: centerCol};
createGradient(filledHexs[centerRow+"-"+centerCol]);
fillHex(filledHexs[centerRow+"-"+centerCol]);
function animate() {
//Loop over all already filled hexagons
for(var hex in filledHexs) {
//Find its neighbours and append them to the already existing ones
//make sure to have only the unique one
var newNeighbours = findNeighbours(filledHexs[hex]);
newNeighbours.forEach(function(d) {
//With a probability add the hexagon to the filled set
if(Math.random() < 0.05) {
if(d.x >= 0 && d.x < mapRows && d.y >= 0 && d.y < mapColumns) {
if(filledHexs[d.x+"-"+d.y] === undefined) {
filledHexs[d.x+"-"+d.y] = d;
//Create a gradient for the hexagon
createGradient(d);
fillHex(d);
}//if
//If the growth is almost at the outside, let the alpha shrink
if( d.x < mapRows*0.25 || d.x > mapRows*0.75 || d.y < mapColumns*0.25 || d.y > mapColumns*0.75 ) {
reduceAlpha = true;
}//if
} else {
//Stop growing once one hexagon neighbour would lie outside the box
keepGrowing = false;
}//else
}//if
});
}//for hex
//Exponential decay of alpha
if(reduceAlpha) {
extraStep = extraStep + 0.075;
alpha = Math.min(1, (1 - Math.exp(-5+extraStep)) );
}
//Set the found neighbours to have a fill
for(var item in filledHexs) {
points[pointsByIndex[item]].fill = true;
}//for item
if(keepGrowing) {
animationID = requestAnimationFrame(animate);
}//if
}//animate
//Start the run
requestAnimationFrame(animate);
///////////////////////////////////////////////////////////////////////////
////////////////////////// Helper functions ///////////////////////////////
///////////////////////////////////////////////////////////////////////////
//Create a linear gradient for the hexagon d
function createGradient(d) {
var chosenColors = Math.random() > 0.33 ? colors : colors2;
//Create a gradient
var linearGradient = defs.append("linearGradient")
.attr("id", "gradient-"+d.x+"-"+d.y)
.attr("x1", "0%").attr("y1", "0%")
.attr("x2", "0%").attr("y2", "100%")
.attr("gradientTransform", "rotate("+ (gradRotations[Math.round(Math.random()*(gradRotations.length-1))]) +" "+ 0.5 +" "+ 0.5 +")");
linearGradient.append("stop")
.attr("offset", "0%")
.attr("stop-color", chosenColors[Math.round(Math.random()*(chosenColors.length-1))] )
.attr("stop-opacity", alpha );
linearGradient.append("stop")
.attr("offset", "100%")
.attr("stop-color", chosenColors[Math.round(Math.random()*(chosenColors.length-1))])
.attr("stop-opacity", alpha );
}//createGradient
function fillHex(d) {
//Fill the chosen hexagon with its color
d3.selectAll(".hexagon")
.filter(function(h,i) { return h.rowIndex === d.x && h.colIndex === d.y; })
.style("fill", "url(#gradient-"+d.x+"-"+d.y+")" )
.style("stroke", "url(#gradient-"+d.x+"-"+d.y+")" )
.transition().duration(500)
.style("fill-opacity", 1)
.style("stroke-opacity", alpha > 0.9 ? 1 : alpha*0.35 );
}//fillHex
//Find the row and column locations of the 6 neighbours around a hexagon
function findNeighbours(currentHex) {
var i = currentHex.y,
j = currentHex.x;
//http://stackoverflow.com/questions/6661169/finding-adjacent-neighbors-on-a-hexagonal-grid
if(j % 2 === 0) {
//For a cell (X,Y) where Y is even, the neighbors are
var neighbours = [
{y: i, x: (j-1)},
{y: (i+1), x: (j-1)},
{y: (i-1), x: j},
{y: (i+1), x: j},
{y: i, x: (j+1)},
{y: (i+1), x: (j+1)} ];
} else {
//For a cell (y,x) where x is odd, the neighbors are
var neighbours = [
{y: (i-1), x: (j-1)},
{y: i, x: (j-1)},
{y: (i-1),x: j},
{y: (i+1), x: j},
{y: (i-1), x: (j+1)},
{y: i, x: (j+1)} ];
}//else
return neighbours;
}//findNeighbours
//http://stackoverflow.com/questions/3689903/how-to-create-a-2d-array-of-zeroes-in-javascript
function zeros(dimensions) {
var array = [];
for (var i = 0; i < dimensions[0]; ++i) {
array.push(dimensions.length == 1 ? 0 : zeros(dimensions.slice(1)));
}
return array;
// zeros([5, 3]);
// [[0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0], [0, 0, 0]]
}//zeros
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment