JS gooey effect, dat.GUI

강정우·2024년 4월 13일
0

JavaScript

목록 보기
52/53
post-thumbnail

gooey effect

gooey effect 란 겹치는 영역에 선을 이어 마치 끈적끈적하게 보이도록 하는 이펙트이다.

기본원리

https://css-tricks.com/

위 배열에서 주대각선의 점들을 보면 블러가 처리됨에도 불구하고 대비가 높으면 아무것도 적용되지 않은 점과 크게 다른 점이 없다.

그러나 이 점들을 연결하여 똑같은 현상을 줬을 땐 다른 모양이 된다. blur 처리된 부분들의 대비를 빡쎄게 주면 이 블러된 부분들 까지도 대비가 높아서 마치 이어져있는 듯 한 모양이 되는 것이다.

그래서 사실 css의 filter 속성으로 blur 와 contrast 만 높게 주면 gooey effect 를 줄 순 있다.

filter: blur(50px) contrast(50);
background-color: #fff;

그러나 이는 css를 기반으로 돌아가기 때문에 2가지 치명적인 단점이 있다.

  1. 반드시 background-color 가 있어야함.
  2. 배경색이 변하면 내가 지정한 값이 아니라 색이 변 함.

따라서 사용하는 것이 바로 SVG 이다.
CSS 필터가 아닌 SVG 안에 갖고있는 filter 속성을 이용하여 구현하면 된다.

SVG filter

<canvas></canvas>
<svg>
    <defs>
        <filter id="gooey">
            <feGaussianBlur stdDeviation="40" in="SourceGraphic" result="blur1"/>
            <feColorMatrix in="blur1" type="matrix" values="1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 100 -23"/>
        </filter>
    </defs>
</svg

<defs>

참고로 <defs> 태그는 재사용할 수 있도록 요소들을 정의할 때 사용된다.
따라어 이 내부에 blur 나 다른 filter 들을 적용할 수 있고 또 클리핑 마스크 처럼 SVG 를 마스킹 처리할 때 마스킹 처리할 영역을 정의할 수 있다.

또 circle, rect 와 같은 태그들을 svg 내부에 직접 넣어서 바로사용하는 것이 아니라 마치 react.js 의 재사용 컴포넌트들을 정의하듯 다양한 모양의 형태들을 미리 정의만 해둘 수 있는 곳이라 생각하면 된다.

<feGaussianBlur>

또 convention 하게 filter 태그의 filter 속성은 fe- 접두어가 붙는다.

또 css 와는 다르게 filter 태그는 stdDeviation 이라는 속성으로 관리된다. 이때 2가지의 인자를 받는데 첫번째는 가로 블러 두번째는 세로 블러이다.

  • 참고로 여기서 이 가로 인자를 이용하여 image-slider 를 만들 수도 있다.

또 in 속성은 작성한 filter 가 어디에 적용될 것인가를 정하는 속성값이다.
SourceGraphic 즉, Original Graphic 에 주면 이 필터가 적용되어 사용되는 Canvas Element에 바로 적용된다고 보면 된다.

또 result 속성은 작성한 filter 의 이름을 정의하는 것이라고 보면 된다.

<feColorMatrix>

in 속성에는 특정 필터에 color matrix 를 적용하겠다는 대상이 들어간다.
type 속성에는 4가지 중 matrix 속성을 수정한다
value 속성은 아래 표를 바탕으로 가장 마지막 알파값을 수정하여 선명도를 조절한다.

  • 참고로 이를 편하테 테스트 해볼 수 있는 사이트가 있는데 여기서 테스트 해보고 나중에 적용하면 된다.

그리고 이제 작성이 완료된 필터를 적용하려면 아래처럼 작성한 필터를 url 로 적용해주면 된다.

filter: url('#gooey');

dat.GUI

dat.GUI는 웹 기반 프로젝트에서 사용자 인터페이스를 쉽게 만들기 위한 경량 JavaScript 라이브러리이다.
주로 시각화, 그래픽, 게임 개발 등에서 매개변수를 실시간으로 조정하며 결과를 바로 확인할 수 있는 인터페이스를 제공하고자 할 때 사용된다.

dat.GUI를 통해 개발자는 색상 선택기, 체크박스, 슬라이더 등 다양한 형태의 컨트롤을 웹 페이지에 쉽게 추가할 수 있다.

dat.GUI는 주로 Three.js와 같은 3D 그래픽스 라이브러리와 함께 사용되어, 3D 시각화나 그래픽 작업 시 사용자가 직접 매개변수를 조정하며 시각적 피드백을 받을 수 있도록 한다. 하지만 여기서는 간단히 filter 태그를 조작할 때 사용해보록 하자.

우선 npm 에서 dat.GUI 와 해당하는 타입까지 받아주고 우리가 컨트롤 하기위한 class 를 우선 선언하자.

export default class Controls {
    blurValue: number = 40;
    alphaChannel: number = 100;
    alphaOffset: number = -23;
}

여기서는 단순히 blur 와 contrast 값을 수정하기 위한 속성값들을 선언하였다. 만약 다른 값들도 수정하고 싶으면 추가하면 되겠다.

다음 공식문서대로 아래 형태로 라이브러리를 로드해주고

import * as dat from 'dat.gui';

이제 우리가 조정할 fe-filter 태그를 가져와서 해당 컨트롤, 조정할 컬럼, 최소, 최대 값들을 넣어주면 된다.
이 add 메서드가 우리가 통제할 패널을 더하는 메서드이다.
그리고 .onChange() 메서드로 값이 변할 때 동작할 콜백함수를 정의해주면 된다.

const feGaussianBlur = document.querySelector("feGaussianBlur");

const controls = new Controls();

const gui = new dat.GUI()

const f1 = gui.addFolder("gooey effect");
f1.open()

f1.add(controls, 'blurValue', 0, 100).onChange((value:number) => {
    feGaussianBlur!.setAttribute("stdDeviation", value.toString());
})
f1.add(controls, 'alphaChannel', 1, 200).onChange((value:number) => {
    feColorMatrix!.setAttribute("values", `1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 ${value.toString()} ${controls.alphaOffset}`);
})
f1.add(controls, 'alphaOffset', -40, 40).onChange((value:number) => {
    feColorMatrix!.setAttribute("values", `1 0 0 0 0  0 1 0 0 0  0 0 1 0 0  0 0 0 ${controls.alphaChannel} ${value.toString()}`);
})

reactive 하게 변경하기

굉장히 간단하다. css 가 아닌 js 로 window 객체가 resize 될 때 그냥 함수를 다시 실행해버리는 것이다.

function init() {
    canvasWidth = innerWidth;
    canvasHeight = innerHeight;

    canvas.style.width = canvasWidth + "px";
    canvas.style.height = canvasHeight + "px";

    canvas.width = canvasWidth * dpr;
    canvas.height = canvasHeight * dpr;

    ctx.scale(dpr, dpr);

    particles = [];
    total = canvasWidth / 10;

    for (let i = 0; i < total; i++) {
        const x = randomNumBetween(0, canvasWidth);
        const y = randomNumBetween(0, canvasHeight);
        const radius = randomNumBetween(50, 100);
        const vx = randomNumBetween(1, 2);
        const vy = randomNumBetween(1, 5);
        const particle = new Particle(x, y, radius, vx, vy);
        particles.push(particle);
    }
}

init 이라는 함수로 생성해주고 그 안에 들어갈 사이즈들을 동적으로 지정해준다.
다음 window 객체의 addEventListener 함수에 "load", "resize" 속성으로 함수들을 지정해주면

window.addEventListener('load', () => {
    init();
    animation();
});
window.addEventListener('resize', () => {
    init();
});
profile
智(지)! 德(덕)! 體(체)!

0개의 댓글