CSS&JS/👀Study and Copy

[CSS] Gooey effect 끈적거리며 달라붙는 효과 (SVG:filter)

arancia_ 2023. 1. 10. 13:25

코드펜의 이 예제를 보고, 원하는 만큼 푸른 공을 추가하고 마우스 커서를 따라다니는 민트색 공으로 변경해봄

  • svg의 <filter id="아이디">기능을 쓴다.
    • css에서 푸른공들을 묶어놓은 DOM(이하  $balls)에 filter : url(#아이디) 이런식으로 지정을 해준다.
    • 커서를 따라다니는 DOM은 총 2개여야 한다.
      • 만일 box-shadow효과가 필요 없다면 1개여도 충분하다! box-shadow효과가 필요한 $cursorBack은 $balls 밖, 이전 형제로 넣어준다.
      • 실제로 찐득찐득 달라붙는 필터가 입혀지는 $cursor는 $balls 안에 가장 마지막 자식으로 넣어준다
  • svg의 gooey 효과에 대해선 이 문서 참조 : https://css-tricks.com/gooey-effect/
 

The Gooey Effect | CSS-Tricks

The following is a post by Lucas Bebber. Lucas the originator of some of the most creative effects I've ever seen on the web. So much so I couldn't resist

css-tricks.com

 

HTML

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>gooey effect mine</title>
    <link rel="stylesheet" href="./style.css">
    <script src="./main.js" type="module"></script>
</head>
<body>
    <!-- wrap -->
    <div id="wrap">
        <!-- cursor -->
        <div class="cursor back"></div>
        <div id="balls"></div>
    </div><!-- wrap -->
 
    <!-- SVG GOOEY FILTER -->
    <svg>
        <filter id="goo">
            <feGaussianBlur in="SourceGraphic" stdDeviation="10" 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" />
        </filter>
    </svg>
</body>
</html>
cs

CSS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
@charset "utf-8";
:root{
    --bg:#001E31;
    --ball:#4DB9FF;
    --cursor:#36ffc6;
    --cursor-shadow: 
    0 0 10px var(--cursor),
    0 0 30px var(--cursor),
    0 0 100px var(--cursor);
    --shadow:0 0 1rem #031b2a;
}
*{margin:0;padding:0;box-sizing:border-box;}
html,body{width:100%;}
body{
    overflow:hidden;
    display:flex;flex-flow:column nowrap;
    justify-content:center; align-items:center;
    min-height:100vh;
    background:var(--bg);
}
svg{display:none;}
 
/*  */
#wrap{
    position:relative;
    width:90vmin; aspect-ratio:1/1;
}
#balls{
    display:flex;flex-flow:row wrap;
    justify-content:center;align-items:center;
    align-content:center;
    gap:5vmin;
    position:relative;
    width:100%; height:100%;
    filter:url(#goo);
}
 
/*  */
.ball{
    position:relative;
    width:10vmin; aspect-ratio:1/1;
    background:var(--ball);
    border-radius:50%;
    transition:transform .3s .3s linear;
}
.ball:hover{
    transform:scale(1.15);
    transition:transform .3s 0s linear;
}
 
/*  */
.cursor{
    --wid:10vmin;
    --center : calc(var(--wid) / 2 * -1);
    position:absolute;
    top:0;left:0;
    width:var(--wid);aspect-ratio:1/1;
    background:var(--cursor);
    border-radius:50%;
    pointer-events:none;
}
.cursor.back{
    box-shadow:var(--cursor-shadow);
}
cs

JS

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
function make_balls(num){
    const $balls = document.getElementById('balls');
    const $frag = document.createDocumentFragment();
    for(let i=0; i<num; i++){
        const $ball = document.createElement('DIV');
        $ball.classList.add('ball');
        $frag.appendChild($ball);
    }//for
    const $cursor = document.createElement('DIV');
    $cursor.classList.add('cursor');
    $cursor.style.position = 'fixed';
    $cursor.style.top = 0;
    $cursor.style.left = 0;
    $frag.appendChild($cursor);
    $balls.appendChild($frag);
}//make_balls
 
function move_cursor(){
    const $$cursor = document.getElementsByClassName('cursor');
    const $balls = document.getElementById('balls');
 
    window.addEventListener('mousemove',e => {
        const {clientX,clientY} = e;
        const {left,top} = $balls.getBoundingClientRect();
        const half = $$cursor[0].offsetWidth / 2;
        const pos = `translate(${clientX - left - half}px, ${clientY - top - half}px)`
        $$cursor[0].style.transform = pos;
        $$cursor[1].style.transform = pos;
    });
}//move_cursor
 
/* =================================================== */
make_balls(30);
move_cursor();
cs