gooey effect 란 겹치는 영역에 선을 이어 마치 끈적끈적하게 보이도록 하는 이펙트이다.
위 배열에서 주대각선의 점들을 보면 블러가 처리됨에도 불구하고 대비가 높으면 아무것도 적용되지 않은 점과 크게 다른 점이 없다.
그러나 이 점들을 연결하여 똑같은 현상을 줬을 땐 다른 모양이 된다. blur 처리된 부분들의 대비를 빡쎄게 주면 이 블러된 부분들 까지도 대비가 높아서 마치 이어져있는 듯 한 모양이 되는 것이다.
그래서 사실 css의 filter 속성으로 blur 와 contrast 만 높게 주면 gooey effect 를 줄 순 있다.
filter: blur(50px) contrast(50);
background-color: #fff;
그러나 이는 css를 기반으로 돌아가기 때문에 2가지 치명적인 단점이 있다.
따라서 사용하는 것이 바로 SVG 이다.
CSS 필터가 아닌 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> 태그는 재사용할 수 있도록 요소들을 정의할 때 사용된다.
따라어 이 내부에 blur 나 다른 filter 들을 적용할 수 있고 또 클리핑 마스크 처럼 SVG 를 마스킹 처리할 때 마스킹 처리할 영역을 정의할 수 있다.
또 circle, rect 와 같은 태그들을 svg 내부에 직접 넣어서 바로사용하는 것이 아니라 마치 react.js 의 재사용 컴포넌트들을 정의하듯 다양한 모양의 형태들을 미리 정의만 해둘 수 있는 곳이라 생각하면 된다.
또 convention 하게 filter 태그의 filter 속성은 fe-
접두어가 붙는다.
또 css 와는 다르게 filter 태그는 stdDeviation 이라는 속성으로 관리된다. 이때 2가지의 인자를 받는데 첫번째는 가로 블러 두번째는 세로 블러이다.
또 in 속성은 작성한 filter 가 어디에 적용될 것인가를 정하는 속성값이다.
SourceGraphic 즉, Original Graphic 에 주면 이 필터가 적용되어 사용되는 Canvas Element에 바로 적용된다고 보면 된다.
또 result 속성은 작성한 filter 의 이름을 정의하는 것이라고 보면 된다.
in 속성에는 특정 필터에 color matrix 를 적용하겠다는 대상이 들어간다.
type 속성에는 4가지 중 matrix 속성을 수정한다
value 속성은 아래 표를 바탕으로 가장 마지막 알파값을 수정하여 선명도를 조절한다.
그리고 이제 작성이 완료된 필터를 적용하려면 아래처럼 작성한 필터를 url 로 적용해주면 된다.
filter: url('#gooey');
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()}`);
})
굉장히 간단하다. 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();
});