웹솔루션개발 22년 노하우! 해피CGI의 모든것

[해피CGI][cgimall] SVG를 이용한 백그라운드 애니메이션 Generative Morphing Soft Gradients [Experiment, SVG] 본문

웹프로그램밍 자료실/HTML 자료

[해피CGI][cgimall] SVG를 이용한 백그라운드 애니메이션 Generative Morphing Soft Gradients [Experiment, SVG]

해피CGI윤실장 2024. 7. 1. 09:06



SVG를 이용한 백그라운드 애니메이션입니다.

 
색상이 계속 변경되며 동적인 효과를 줄 수 있습니다.

 

CSS 소스

* {

  margin: 0;

  padding: 0;

  box-sizing: border-box;

}

 

svg {

  position: absolute;

  top: 0;

  left: 0;

  width: 100vw;

  height: 100vh;

  z-index: -1;

  transform: translate3d(0px, 0px, 0px);

}

 



JS 소스

import tinycolor from "https://cdn.skypack.dev/tinycolor2@1.4.2";

import SimplexNoise from "https://cdn.skypack.dev/simplex-noise@2.4.0";

 

const simplex = new SimplexNoise(Math.random);

 

console.clear();

 

class GradientBG {

  constructor(width = 1920, height = 1080, target = document.body) {

    this.width = width;

    this.height = height;

    this.target = target;

 

    this.svgns = "http://www.w3.org/2000/svg";

    this.SVGElement = this._createSVGElement();

 

    this.id = Math.random();

  }

 

  generate() {

    this.SVGElement.innerHTML = "";

    this.defs = this._createDefs();

    this.backgroundGradient = this._createBackgroundGradient();

    this.blurFilter = this._createBlurFilter();

 

    let baseColor = tinycolor(

      `hsl(${~~this._random(0, 360)}, ${this._random(75, 100)}, ${this._random(

        80,

        92

      )}%)`

    );

 

    let combinations = baseColor.splitcomplement();

    let secondaryColor = combinations[~~this._random(1, combinations.length)];

 

    let secondaryCombinations = secondaryColor.splitcomplement();

 

    const stopOffset1 = this._random(0, 25);

    const stopOffset2 = 100 - this._random(0, 25);

 

    this.backgroundGradient.stop1.setAttribute(

      "stop-color",

      baseColor.toHslString()

    );

    this.backgroundGradient.stop2.setAttribute(

      "stop-color",

      secondaryColor.toHslString()

    );

 

    this.backgroundGradient.stop1.setAttribute("offset", `${stopOffset1}%`);

    this.backgroundGradient.stop2.setAttribute("offset", `${stopOffset2}%`);

 

    const maxBlobSize = Math.min(this.width, this.height);

 

    this._blob({

      x: `${this._random(0, this.width / 4)}`,

      y: `${this._random(0, this.height / 4)}`,

      r: `${this._random(maxBlobSize / 2, maxBlobSize)}`,

      fill: combinations[~~this._random(0, combinations.length)]

    });

 

    this._blob({

      x: `${this._random(this.width - this.width / 4, this.width)}`,

      y: `${this._random(0, this.height / 4)}`,

      r: `${this._random(maxBlobSize / 2, maxBlobSize)}`,

      fill: combinations[~~this._random(0, combinations.length)]

    });

 

    this._blob({

      x: `${this._random(0, this.width / 4)}`,

      y: `${this._random(this.height - this.height / 4, this.height)}`,

      r: `${this._random(maxBlobSize / 2, maxBlobSize)}`,

      fill:

        secondaryCombinations[~~this._random(0, secondaryCombinations.length)]

    });

 

    this._blob({

      x: `${this._random(this.width - this.width / 4, this.width)}`,

      y: `${this._random(this.height - this.height / 4, this.height)}`,

      r: `${this._random(maxBlobSize / 2, maxBlobSize)}`,

      fill:

        secondaryCombinations[~~this._random(0, secondaryCombinations.length)]

    });

  }

 

  _createSVGElement() {

    const el = document.createElementNS(this.svgns, "svg");

 

    el.setAttribute("viewBox", `0 0 ${this.width} ${this.height}`);

    el.setAttribute("preserveAspectRatio", "xMidYMid slice");

 

    this.target.appendChild(el);

 

    return el;

  }

 

  _createDefs() {

    const el = document.createElementNS(this.svgns, "defs");

 

    this.SVGElement.appendChild(el);

 

    return el;

  }

 

  _createBackgroundGradient() {

    const el = document.createElementNS(this.svgns, "linearGradient");

 

    el.id = "bgGradient" + this.id;

    el.setAttribute("gradientTransform", "rotate(90)");

 

    const stop1 = document.createElementNS(this.svgns, "stop");

    stop1.setAttribute("offset", `${~~this._random(0, 25)}%`);

 

    const stop2 = document.createElementNS(this.svgns, "stop");

    stop2.setAttribute("offset", `${~~this._random(75, 100)}%`);

 

    el.appendChild(stop1);

    el.appendChild(stop2);

 

    this.defs.appendChild(el);

 

    const rect = document.createElementNS(this.svgns, "rect");

 

    rect.setAttribute("x", "0");

    rect.setAttribute("y", "0");

    rect.setAttribute("width", "100%");

    rect.setAttribute("height", "100%");

    rect.setAttribute("fill", `url(#bgGradient${this.id})`);

 

    this.SVGElement.appendChild(rect);

 

    return {

      rect: rect,

      stop1: stop1,

      stop2: stop2

    };

  }

 

  _createBlurFilter() {

    const el = document.createElementNS(this.svgns, "filter");

 

    el.id = "blur" + this.id;

    el.setAttribute("x", "-100%");

    el.setAttribute("y", "-100%");

    el.setAttribute("width", "300%");

    el.setAttribute("height", "300%");

 

    const gaussianBlur = document.createElementNS(this.svgns, "feGaussianBlur");

 

    gaussianBlur.setAttribute("in", "SourceGraphic");

    gaussianBlur.setAttribute("stdDeviation", this._random(100, 110));

 

    el.appendChild(gaussianBlur);

 

    this.defs.appendChild(el);

 

    return el;

  }

 

  _blob({ x, y, r, fill, filter = `url(#blur${this.id})` }) {

    const circle = document.createElementNS(this.svgns, "circle");

 

    circle.setAttribute("cx", x);

    circle.setAttribute("cy", y);

    circle.setAttribute("r", r);

 

    circle.setAttribute("fill", fill);

    circle.setAttribute("filter", filter);

 

    this.SVGElement.appendChild(circle);

 

    return circle;

  }

 

  _random(min, max) {

    return Math.random() * (max - min) + min;

  }

}

 

const gradient = new GradientBG();

gradient.generate();

 

const circles = [];

 

document.querySelectorAll("circle").forEach((el) => {

  circles.push({

    el,

    x: ~~el.getAttribute("cx"),

    y: ~~el.getAttribute("cy"),

    originX: ~~el.getAttribute("cx"),

    originY: ~~el.getAttribute("cy"),

    xOff: Math.random(),

    yOff: Math.random()

  });

});

 

function map(n, start1, end1, start2, end2) {

  return ((n - start1) / (end1 - start1)) * (end2 - start2) + start2;

}

 

(function animate() {

  circles.forEach((c) => {

    const noiseX = simplex.noise2D(c.xOff, c.xOff);

    const noiseY = simplex.noise2D(c.yOff, c.yOff);

 

    const x = map(noiseX, 0, 1, 0, 1920);

    const y = map(noiseY, 0, 1, 0, 1080);

 

    c.el.setAttribute("cx", x);

    c.el.setAttribute("cy", y);

 

    c.xOff += 0.002;

    c.yOff += 0.002;

  });

  requestAnimationFrame(animate);

})();

 



첨부파일을 다운로드 받거나 해당 사이트로 이동해서 전체 소스를 확인하실 수 있습니다.

 

 

Comments