13 053 Codepen

Низко полигональный генератор фона

Генератор фона из треугольников с возможностью настройки глубины, вариативности, цветов, градиента. Полученный результат можно скачать, как изображение любого выбранного размера


HTML
<!-- Topbar -->
<header class="topbar">
    <ul>
        <li><h1>Low Poly Background</h1></li>
    <li><a href="https://cojdev.github.io/lowpoly" target="_top">Full Version</a></li>
        <li><a class="github-button" href="https://github.com/cojdev/lowpoly" data-icon="octicon-star" data-show-count="true" aria-label="Star cojdev/lowpoly on GitHub">Star</a></li>
    </ul>
</header>
<!-- Canvas Display -->
<section class="display">
    <div class="wrap">
        <div id="loader" class="loader">
            <div class="circle"></div>
            <div class="circle"></div>
            <div class="circle"></div>
            <div class="circle"></div>
        </div>
    <canvas id="canvas" class="canvas1"></canvas>
    </div>
    <canvas id="canvas2" class="canvas2"></canvas>
  <div class="footer">
    Developed by <a href="https://cojdev.github.io" target="_top">Charles Ojukwu</a>
  </div>
</section>
<!-- Controls -->
<section class="controls">

    <h3>Dimensions</h3>
    <div class="fieldgroup">
        <label for="preset-size">Presets</label>
        <select id="preset-size">
            <option value="1280-720">1280x720</option>
            <option value="1366-768">1366x768</option>
            <option value="1920-1080">1920x1080</option>
            <option value="3840-2160">3840x2160 (4K)</option>
            <option value="640-1152">iPhone 5/5s</option>
            <option value="750-1334">iPhone 6/7/8</option>
            <option value="1080-1920">iPhone 6+/7+/8+</option>
        </select>
        <div class="row">
            <div class="_50">
                <label for="width">Width</label>
                <input type="number" id="width">
            </div>
            <div class="_50">
                <label for="height">Height</label>
                <input type="number" id="height">
            </div>
        </div>
        <button id="size-btn">Set</button>
    </div>

    <h3>Geometry</h3>
    <div class="fieldgroup">
        <label for="variance">Variance</label>
        <input type="range" id="variance" value="40">

        <label for="cell-size">Cell Size</label>
        <input type="range" id="cell-size" value="40">

        <label for="oamount">Depth</label>
        <input type="range" id="oamount" value="10">
    </div>
    

    <h3>Colour</h3>
    <div class="fieldgroup">
        <label for="numColours">No. of colours: <span id="num-colours"></span></label>
        <input type="range" id="numColours" value="2" max="4" min="1" step="1"><br>
        <!-- Container for dynamically generated color input elememets -->
        <div id="colour-div"></div>
    </div>
    <div class="section">
        <a id="save" class="button" download="lowpoly.png">Download Image</a>
    </div>
    
</section>
<!-- Loader -->
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
    <defs>
    <filter id="goo">
        <feGaussianBlur in="SourceGraphic" stdDeviation="5" result="blur" />
        <feColorMatrix in="blur" mode="matrix" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 18 -7" result="goo" />
        <feBlend in="SourceGraphic" in2="goo" />
    </filter>
    </defs>
</svg>

CSS

@charset "UTF-8";
*, *:before, *:after {
  -webkit-box-sizing: border-box;
  -moz-box-sizing: border-box;
  box-sizing: border-box; }

body {
  padding: 0;
  margin: 0;
font-size: 16px;}

ul, li {
  list-style: none;
  margin: 0;
  padding: 0; }

button {
  outline: none;
  cursor: pointer;
  border: none; }

*, *::before, *::after {
  box-sizing: border-box; }

html,
body {
  margin: 0;
  padding: 0; }

html {
  font-family: 'Barlow', sans-serif;
  color: #bbb;
  background: #ddd;
  font-size: 16px; }

a {
  color: #5ad;
  text-decoration: none; }
  a:hover {
    text-decoration: underline; }

.topbar {
  height: 40px;
  background: #333; }
  .topbar li {
    display: inline; }
  .topbar li:not(:last-child)::after {
    content: " •";
    color: #666;
    margin: 0 .25rem; }
  .topbar h1 {
    display: inline;
    font-size: 1rem;
    font-weight: 400;
    color: #f8f8f8;
    margin: 0;
    line-height: 40px;
    padding-left: 1rem; }

.display {
  width: calc(100% - 200px);
  text-align: center;
  bottom: 0;
  position: fixed;
  left: 0;
  top: 40px;
  bottom: 0; }

.row {
  margin: 0 -0.5rem; }
  .row:before, .row:after {
    content: '';
    display: table; }
  .row:after {
    clear: both; }

._50 {
  float: left;
  padding: 0 0.5rem;
  width: 50%; }

.wrap {
  position: relative;
  display: inline-block;
  height: calc(100vh - 40px);
  display: flex;
  align-items: center;
  justify-content: center;
  text-align: center; }

.canvas1 {
  max-width: 95%;
  max-height: 95%;
  box-shadow: 0 2px 20px rgba(0, 0, 0, 0.1); }

.canvas2 {
  display: none; }

button,
.button {
  display: inline-block;
  text-decoration: none;
  background: linear-gradient(to bottom right, #D84155, #882963);
  padding: 0.6rem;
  color: #fff;
  border-radius: 2px;
  width: 100%; }
  button:hover,
  .button:hover {
    background: linear-gradient(to bottom right, #e16b7b, #af3580); }

.section {
  padding: 1rem; }

input[type=text],
input[type=number],
input[type=range],
select {
  display: block;
  width: 100%; }

input[type=text],
input[type=number],
select {
  height: 2rem;
  line-height: 2rem;
  padding: 0 .5rem;
  background: #fff;
  border: none;
  margin-bottom: .75rem; }

.controls {
  position: fixed;
  top: 40px;
  right: 0;
  bottom: 0;
  width: 200px;
  background: #333;
  padding: 0;
  box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
  overflow-y: auto;
  overflow-x: hidden;
  border-top: 1px solid #444; }
  .controls h3 {
    color: #f8f8f8;
    padding: .5rem 1rem;
    margin: 0;
    font-weight: 600; }

.fieldgroup {
  padding: 1rem;
  background: #222; }

label {
  display: block; }

.footer {
  position: absolute;
  color: #888;
  bottom: 2rem;
  width: 100%;
  font-size: .9em;
  text-align: center;
}

.loader {
  position: absolute;
  top: 50%; }

.loader {
  position: absolute;
  filter: url("#goo");
  top: calc(50% - 15px);
  left: calc(50% - 45px);
  height: 30px;
  width: 90px; }

.circle {
  width: 30px;
  height: 30px;
  border-radius: 50%;
  background: #fff;
  position: absolute;
  left: 30px; }
  .circle:first-child {
    animation: a1 1200ms ease-out infinite; }
  .circle:last-child {
    animation: a2 1200ms ease-out infinite; }
  .circle:nth-child(2) {
    animation: a3 1200ms ease-out infinite; }
  .circle:nth-child(3) {
    animation: a4 1200ms ease-out infinite; }

@keyframes a1 {
  0% {
    transform: translateX(0); }
  6% {
    transform: translateX(-3.75px) scale(0.75); }
  24%, 30% {
    transform: translateX(-15px) scale(0.65); }
  36% {
    transform: translateX(-15px) scale(0.5); }
  60%, 70% {
    transform: translateX(-15px) scale(0.4); }
  85% {
    transform: translateX(-3.75px) scale(0.9); }
  90% {
    transform: translateX(0); } }

@keyframes a2 {
  0% {
    transform: translateX(0); }
  6% {
    transform: translateX(-3.75px) scale(0.75); }
  24%, 30% {
    transform: translateX(-15px) scale(0.65); }
  36% {
    transform: translateX(-22.5px) scale(0.5); }
  60%, 70% {
    transform: translateX(-45px) scale(0.4); }
  85% {
    transform: translateX(-3.75px) scale(0.9); }
  90% {
    transform: translateX(0); } }

@keyframes a3 {
  0% {
    transform: translateX(0); }
  6% {
    transform: translateX(3.75px) scale(0.75); }
  24%, 30% {
    transform: translateX(15px) scale(0.65); }
  36% {
    transform: translateX(22.5px) scale(0.5); }
  60%, 70% {
    transform: translateX(45px) scale(0.4); }
  85% {
    transform: translateX(3.75px) scale(0.9); }
  90% {
    transform: translateX(0); } }

@keyframes a4 {
  0% {
    transform: translateX(0); }
  6% {
    transform: translateX(3.75px) scale(0.75); }
  24%, 30% {
    transform: translateX(15px) scale(0.65); }
  36% {
    transform: translateX(15px) scale(0.5); }
  60%, 70% {
    transform: translateX(15px) scale(0.4); }
  85% {
    transform: translateX(3.75px) scale(0.9); }
  90% {
    transform: translateX(0); } }

JS

/**
 * Author: Charles Ojukwu
 */
(function () {

'use strict';

var cvs,//canvas
    ctx,//canvas context
    gridWidth,//draw width (2 cells wider than the actual canvas)
    gridHeight,//draw height (2 cells taller than the actual canvas)
    vRange,
    cRange,
    maxCols,
    maxRows,
    oAmount,
    imgd,imge,
    base_image,
    saveBtn,
    cvs2,
    ctx2,
    loader,
    numColours,
    preset;
var points = [];
var cellSize = 50;//size of a single grid square
var variance = 0.2;
var ovA = 0.5;
var colours = ["#22bbee","#8855cc","#ee2266","#ee7722"];

function init () {
    //Add on load scripts
    cvs = document.getElementById("canvas");
    cRange = document.getElementById("cell-size");
    vRange = document.getElementById("variance");
    oAmount = document.getElementById("oamount");
    saveBtn = document.getElementById('save');
    ctx = cvs.getContext("2d");
    cvs2 = document.getElementById("canvas2");
    ctx2 = cvs2.getContext("2d");
    numColours = document.getElementById("numColours");

    preset = document.getElementById("preset-size");

    preset.addEventListener("change",function () {
        var temp5 = preset.value.split("-");
        console.log(temp5);

        document.getElementById('width').value = parseInt(temp5[0]);
        document.getElementById('height').value = parseInt(temp5[1]);
    },false);


    var setSize = function(w,h) {
        cvs.width = w;
        cvs.height = h;

        gridWidth = cvs.width+cellSize*2;
        gridHeight = cvs.height+cellSize*2;

        cvs2.width = cvs.width;
        cvs2.height = cvs.height;
        //Repopulate inputs
        document.getElementById('width').value = w;
        document.getElementById('height').value = h;
    }

    var setCols = function() {
        for (var i = 0; i < numColours.value; i++) {
            var temp = document.createElement("input");
            temp.type = "color";
            temp.id = "colour" +(i+1);

            document.getElementById("colour-div").appendChild(temp);
            document.getElementById("num-colours").innerHTML = numColours.value;
            temp.value = colours[i];
            temp.addEventListener("change",function () {
                //document.getElementById("colour-div").innerHTML = "";
                //setCols();
                drawBG(ctx2,cvs2);
                loader.style.display = "";
                var blob = window.setTimeout(function () {
                    pointFun();
                },2);
            },false);
        }
    }

    setCols();

    setSize(1280,720);
    //base_image = new Image();
    //base_image.crossOrigin = "Anonymous";
    //base_image.src = 'download.png';
    //base_image.onload = function () {
        drawBG(ctx2,cvs2);
        loader = document.getElementById("loader");
        pointFun();
    //}
    var tout = 0;
    numColours.addEventListener("change",function () {
        document.getElementById("colour-div").innerHTML = "";
        setCols();
        drawBG(ctx2,cvs2);
        loader.style.display = "";
        var blob = window.setTimeout(function () {
            pointFun();
        },tout);
    },false);
    cRange.addEventListener("change",function () {
        loader.style.display = "";
        var blob = window.setTimeout(function () {
            pointFun();
        },tout);
    },false);
    vRange.addEventListener("change",function () {
        loader.style.display = "";
        var blob = window.setTimeout(function () {
            pointFun();
        },tout);
    },false);
    oAmount.addEventListener("change",function () {
        loader.style.display = "";
        var blob = window.setTimeout(function () {
            pointFun(true);
        },tout);
    },false);

    var sizeBtn = document.getElementById('size-btn');
    sizeBtn.addEventListener("click",function () {
        var tempw = document.getElementById('width').value;
        var temph = document.getElementById('height').value;
        setSize(tempw,temph);
        drawBG(ctx2,cvs2);
        console.log(tempw +" : "+ temph)
        pointFun();
    })

    //saveBtn.addEventListener("click",saveImage,false);
}
//Particle constructor
function point () {
    /*this.x = Math.random()*cvs.width;;
    this.y = Math.random()*cvs.height;
    this.vx = (Math.random()*2)-1;
    this.vy = (Math.random()*2)-1;*/
    //points.push(this);
}

function drawBG(context,canvas) {
    context.clearRect(0, 0, cvs.width, cvs.height);
    context.globalCompositeOperation = "multiply";
    var bg = context.createLinearGradient(0,0,canvas.width,0);

    //Duskish gradient
    /*bg.addColorStop(0,"#0e1b32");
    bg.addColorStop(0.8,"#c28993");
    bg.addColorStop(1,"#ffc7af");*/

    //Pink/Purple Gradient
    /*bg.addColorStop(0,"#85c");
    bg.addColorStop(1,"#d14");*/

    //Whitish
    /*bg.addColorStop(0,"#f8f8f8");
    bg.addColorStop(1,"#ddd");*/
    var inputs = document.getElementById("colour-div").getElementsByTagName('input');
    //console.log(inputs.length);
    for (var i = 0; i < inputs.length; i++) {
        if (inputs.length > 1) {
            bg.addColorStop(i/(inputs.length-1),inputs[i].value);
            console.log(inputs[i].value);
        }else{
            bg = inputs[i].value;
        }
        colours[i] = inputs[i].value;
    }

    context.fillStyle = bg;
    context.beginPath();
    context.fillRect(0,0,canvas.width,canvas.height);
    context.closePath();
    context.fill();

    context.beginPath();
    var bg2 = context.createLinearGradient(0,0,0,cvs.height);
    bg2.addColorStop(0,"#fff");
    bg2.addColorStop(1,"#ccc");
    context.fillStyle = bg2;
    context.fillRect(0,0,cvs.width,cvs.height);
    context.closePath();
    context.fill();
    context.globalCompositeOperation = "source-over";
    //ctx.drawImage(base_image, 0, 0);
    if (context == ctx2) {imgd = context;}
    else if (imgd.getImageData(0, 0, 1, 1).data[3] < 255 || imgd.getImageData(canvas.width-1, canvas.height-1, 1, 1).data[3] < 255) {
        context.clearRect(0,0,canvas.width,canvas.height);
    }
    

}

function saveImage() {
    // body...
    var dataURL = canvas.toDataURL();
    saveBtn.href = dataURL;
}

function draw (obj,rand) {

    var red,blue,green,alpha;

    var setColour = function (xpos,ypos) {
        if (xpos < 0) {
            xpos = 0;
        }
        if (xpos > cvs.width-1) {
            xpos = cvs.width-1;
        }
        if (ypos < 0) {
            ypos = 0;
        }
        if (ypos > cvs.height-1) {
            ypos = cvs.height-1;
        }
        red = imgd.getImageData(xpos, ypos, 1, 1).data[0];
        green = imgd.getImageData(xpos, ypos, 1, 1).data[1];
        blue = imgd.getImageData(xpos, ypos, 1, 1).data[2];
        alpha = imgd.getImageData(xpos, ypos, 1, 1).data[3];
        
    }

    var filler = function () {
        if(rand){
            var temp = (Math.random()*2*ovA)-ovA;
            ctx.fillStyle = "rgba("+Math.round(red-red*temp)+", "+Math.round(green-green*temp)+", "+Math.round(blue-blue*temp)+", "+alpha/255+")";
        } else {
            ctx.fillStyle = "rgba("+red+", "+green+", "+blue+", "+alpha/255+")";
        }
    }
    
    for (var i = 0; i < points.length; i++) {
        if (obj == points[i] && obj.r % 2 == 0 && points[i+maxCols+1] && obj.c < maxCols-1) {
            //setColour(obj.x,obj.y+2*cellSize/3);
            setColour(Math.round(obj.c/maxCols*gridWidth),Math.round(obj.r/maxRows*gridHeight+2*cellSize/3));
            filler();
            //console.log(obj.c);
            ctx.beginPath();
            ctx.moveTo(obj.x,obj.y);
            ctx.lineTo(points[i+maxCols].x,points[i+maxCols].y);
            ctx.lineTo(points[i+maxCols+1].x,points[i+maxCols+1].y);
            ctx.closePath();
            ctx.fill();
            //ctx.stroke();

            //setColour(obj.x+cellSize/2,obj.y+cellSize/3);
            setColour(Math.round(obj.c/maxCols*gridWidth+cellSize/2),Math.round(obj.r/maxRows*gridHeight+cellSize/3));
            filler();
            //ctx.fillStyle = "green";
            ctx.beginPath();
            ctx.moveTo(obj.x,obj.y);
            ctx.lineTo(points[i+1].x,points[i+1].y);
            ctx.lineTo(points[i+maxCols+1].x,points[i+maxCols+1].y);
            ctx.closePath();
            ctx.fill();
            //console.log("YES");
        }
        if (obj == points[i] && obj.r % 2 != 0 && points[i+maxCols+1] && obj.c > 0) {

            //setColour(obj.x-cellSize/2,obj.y+cellSize/3);
            setColour(Math.round((obj.c-1)/maxCols*gridWidth),Math.round(obj.r/maxRows*gridHeight+cellSize/3));
            //if (!setColour(Math.round((obj.c-1)/maxCols*gridWidth),200)) {console.log(obj)}
            filler();
            ctx.beginPath();
            ctx.moveTo(obj.x,obj.y);
            ctx.lineTo(points[i-1].x,points[i-1].y);
            ctx.lineTo(points[i+maxCols-1].x,points[i+maxCols-1].y);
            ctx.closePath();
            ctx.fill();
            //ctx.stroke();

            //setColour(obj.x,obj.y+2*cellSize/3);
            setColour(Math.round((obj.c-1)/maxCols*gridWidth+cellSize/2),Math.round(obj.r/maxRows*gridHeight+2*cellSize/3));
            filler();
            //ctx.fillStyle = "green";
            ctx.beginPath();
            ctx.moveTo(obj.x,obj.y);
            ctx.lineTo(points[i+maxCols-1].x,points[i+maxCols-1].y);
            ctx.lineTo(points[i+maxCols].x,points[i+maxCols].y);
            ctx.closePath();
            ctx.fill();
            //console.log("NO");
        }
    }
}
//Point generator
function generatePoints (amount) {
    points = [];
    var temp;
    var row = 0;
    var col = 0;
    for (var i = 0; i < amount; i++) {
        temp = new point();
        if (row % 2 == 0) {
            temp.x = (col*cellSize)-cellSize;
            temp.x = temp.x + (Math.random()-.5)*variance*cellSize*2;
        } else {
            temp.x = (col*cellSize)-cellSize-cellSize/2;
            temp.x = temp.x + (Math.random()-.5)*variance*cellSize*2;
        }
        temp.y = (row*cellSize*0.866)-cellSize;
        temp.y = temp.y + (Math.random()-.5)*variance*cellSize*2;
        temp.r = row;
        temp.c = col;
        points.push(temp);
        col = col + 1;
        if ((i+1) % maxCols == 0) {
            row = row + 1;
            col = 0;
        }
    }
    //console.log(points);
}

function addRand (a,b) {
    return (Math.random()*b*a-(b/2))
}

function pointFun (drawOnly) {
    if(drawOnly === true){
        //do nothing
    }else{
        cellSize = (cRange.value*3)+30;
        variance = vRange.value/100;
        gridWidth = cvs.width+cellSize*2;
        gridHeight = cvs.height+cellSize*2;
        maxCols = Math.ceil(gridWidth / cellSize)+2;
        maxRows = Math.ceil(gridHeight / (cellSize*0.865))
        //console.log(maxCols);
        var x = maxCols;
        var y = maxRows;
        generatePoints(x*y);
    }
    ovA = oAmount.value/100;
    ctx.clearRect(0, 0, cvs.width, cvs.height);
    drawBG(ctx,cvs);
    for (var i = 0; i < points.length; i++) {
        draw(points[i],true);
    };
    saveImage();
    loader.style.display = "none";
}

//Execute when DOM has loaded
document.addEventListener('DOMContentLoaded',init,false);

})();

Комментарии

  • Facebook
  • Вконтакте

Похожие статьи