block by nbremer 7ab7995a7ebd48ad424f218a5b01d30a

Random gradients and orientations - Hexagons - 1 palette

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 uses the dark rainbow color palette. Another version has the option to choose between the dark rainbow and a green color 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 - 10;
            var height = 960 - margin.top - margin.bottom - 20;

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

                //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", colors[Math.round(Math.random()*(colors.length-1))] )
                    .attr("stop-opacity", alpha );

                linearGradient.append("stop")
                    .attr("offset", "100%")
                    .attr("stop-color", colors[Math.round(Math.random()*(colors.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>