block by nbremer a633e364466bfe1ac5fe5e594266e2f2

Random gradients and orientations - Hexagons - 2 palettes

Full Screen

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

index.html

<!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="//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;
                ////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

            ////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>