CSS&JS/👀Study and Copy

[Hyperplexed]opacity, blur 효과 매직완드 갤러리

arancia_ 2023. 7. 12. 10:19

https://www.youtube.com/watch?v=DeHRDNRQhNg 

데모 : https://ohikmyeong.github.io/hpp-wand-reveal/

 

hpp-wand-reveal

[Hyperplexed]Wand reveal gallery (opacity, blur)

ohikmyeong.github.io

깃허브 : https://github.com/OhIkmyeong/hpp-wand-reveal

마우스의 좌표값에 따라 마술봉이 움직이며, 갤러리의 사진들이 마술봉이 지나간 자리에선 opacity가 1, blur가 0이 되며, 지나가기전엔 opacity가 0, blur가 n값이 된다. 마술봉이 지나가고 나면 보이지 않았고 흐리했던 사진들이 스르륵 보여지는 재밌는 효과...

js의 animate API를 사용하여 단순 css효과가 아닌 부드러운 마술봉의 움직임과,
마우스의 위치보다, 마우스의 위치에 따라 좀 더 왼쪽/오른쪽에 마술봉이 위치하여, 마우스가 아닌 마술봉의 위치에 따라 밑의 사진들이 보이고/안보이고 효과가 전환되는 것 처럼 보이게 함.

마술봉의 위치와 각도를 어떻게 수학적으로 계산할 것인가가 중요한 포인트였음

예를들어 현재 마우스의 좌표를 clientX라고 하고, 화면의 넓이를 winWid라고 할 때,
마술봉이 화면 왼쪽보다 -15%, 화면 오른쪽보다 15% 더 움직이게 하고 싶다면, (단 마우스커서가 중앙일땐 마술봉도 중앙에 있어야함)

/**
 * 마술봉의 좌표. 화면 넓이의 -15%를 빼고, 현재 마우스 좌표에서 30%를 더하면 의도한 마술봉의 좌표값이 된다.
 * 예) 화면의 넓이가 800px이고, 15%씩 추가/감소 마술봉의 위치를 조절하고 싶을때
 * mouseX  |   계산      |  wandX
 * 0       | -120 + 0    |  -120
 * 400     | -120 + 520  |  400
 * 800     | -120 + 1040 |  920   
 * */
const wandX = (winWid * -0.15) + (clientX * 1.3);

 

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
28
<!DOCTYPE html>
<html lang="ko">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Wand Reveal Gallery(opacity,blur)</title>
    <link rel="stylesheet" href="./css/main.css">
    <script src="./js/main.js" type="module"></script>
</head>
<body>
    <div id="wrapper">
        <div id="wand"></div>
        <a href="https://www.youtube.com/watch?v=DeHRDNRQhNg" target="_blank">[Hyperplexed]This Website Theme Hasn't Been Perfected Since 1996</a>
        <ul id="gall">
            <li class="gall-item">
                <img src="https://images.unsplash.com/photo-1689005046866-9586288969a3?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHw1fHx8ZW58MHx8fHx8&auto=format&fit=crop&w=500&q=60" class="gall-item-photo"/>
            </li><!--  -->
            <li class="gall-item">
                <img src="https://images.unsplash.com/photo-1687941336891-1c46fac86075?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHwxN3x8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=500&q=60" class="gall-item-photo"/>
            </li><!--  -->
            <li class="gall-item">
                <img src="https://images.unsplash.com/photo-1688607932382-f01b0987c897?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxlZGl0b3JpYWwtZmVlZHw5M3x8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=500&q=60" class="gall-item-photo"/>
            </li><!--  -->
        </ul>
    </div><!-- wrapper -->
 
</body>
</html>
cs

main.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import { Gall } from "./Gall.js";
import { Wand } from "./Wand.js";
 
const WAND = new Wand();
const GALL = new Gall();
 
window.addEventListener('mousemove',e=>{
    WAND.move_wand(e);
    GALL.toggle_reveal(e);
});
 
window.addEventListener('resize', ()=>{
    WAND.get_width();
});
cs

Wans.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
export class Wand {
    constructor() {
        this.$wand = document.getElementById('wand');
        this.get_width();
    }//constructor
 
    get_width() {
        this.winWid = window.innerWidth;
        this.winHei = window.innerHeight;
        this.winWidHalf = window.innerWidth / 2;
        this.width = this.$wand.getBoundingClientRect().width;
    }//get_width
 
    move_wand = (e) => {
        const { clientX, clientY } = e;
        // this.$wand.style.transform = `translate(${clientX - (this.width / 2)}px, ${clientY}px)`;
        const per = clientX / this.winWid;
        const deg = (per * 50- 25//-10 ~ 10이라 20 곱하고 -10 뺀거임
        const wandX = (this.winWid * -0.15+ clientX * 1.3;
        console.log(wandX);
        const wandY = (this.winHei * 0.1+ clientY * 0.4;
        this.$wand.animate([
            { transform: `translate(${wandX}px, ${wandY}px) rotate(${deg}deg)` }
        ], {
            duration: 500,
            fill: "forwards"
        });
    }//move_wand
}//class-Wand
cs

Gall.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
export class Gall{
    constructor(){
        this.$gall = document.getElementById('gall');
        this.$$item = this.$gall.querySelectorAll('.gall-item');
 
        this.init();
    }//constructor
    
    init(){
        /* 각자 부채처럼 회전시키기 */
    }//init
 
    toggle_reveal(e){
        const {clientX,clientY} = e;
        const wandX = (window.innerWidth * -0.5+ clientX * 2.0;
 
        this.$$item.forEach($item=>{
            const $img = $item.querySelector('.gall-item-photo');
            
            const {left,width} = $item.getBoundingClientRect();
            const relX = wandX - left;
            const relXDec = relX / width;
 
            const opacity = relXDec; 
            const blur = (1 - relXDec) * 10;
 
            $item.style.setProperty('--opacity',opacity);
            $item.style.setProperty('--blur',`${blur * 10}px`);
        });
    }//toggle_reveal
}//class-Gall
cs

css는 특별할거 없어서 생략함. 어차피 Gall.js에서 각 요소의 위치는 getBoundingClientRect()로 가져오기 때문에, 갤러리 요소의 배치를 flex box로 하든 absolute로 하든 상관이 없다.