๊ธ€ ์ž‘์„ฑ์ž: ๋˜ฅํด๋ฒ .
๋ฐ˜์‘ํ˜•

 ๋ณธ ํฌ์ŠคํŒ…์€ ์•„๋ž˜ ์ฝ”๋”ฉ์• ํ”Œ๋‹˜์˜ ๋™์˜์ƒ '๋ฐ”๋กœ ์ฟ ํŒก ์ทจ์—…๊ฐ€๋Šฅํ•œ ํฌํŠธํด๋ฆฌ์˜ค ๋งŒ๋“ค๊ธฐ'๋ฅผ ์ฐธ๊ณ ํ•˜์—ฌ ์ž‘์„ฑํ•˜์˜€์Šต๋‹ˆ๋‹ค.


https://youtu.be/YDCCauu4lIk?si=hYyzxZVK5CIwoprZ


 

0. ์ค€๋น„

  • ์นด๋“œ๋กœ ์‚ฌ์šฉํ•  ์ด๋ฏธ์ง€ 1์žฅ(images/3vx2.webp)
  • HTML ํŒŒ์ผ(index.html)

๋””๋ ‰ํ† ๋ฆฌ ๊ตฌ์กฐ

 

 

1.  ์นด๋“œ ์ด๋ฏธ์ง€ ํŒŒ์ผ ์ถœ๋ ฅ

<div class="container">
<div class="card"></div>
</div>
<style>
.container {
width: 220px;
height: 310px;
}
.card {
width: 220px;
height: 310px;
background-image: url(images/3vx2.webp);
background-size: cover;
}
</style>

1. ์นด๋“œ ์ด๋ฏธ์ง€ ์ถœ๋ ฅ ๊ฒฐ๊ณผ

 

 

2. ๋งˆ์šฐ์Šค ์›€์ง์ž„์— ๋”ฐ๋ผ ์นด๋“œ ์ด๋ฏธ์ง€ ํšŒ์ „

<script>
var container = document.querySelector('.container')
container.addEventListener('mousemove', function(e) {
var x = e.offsetX
var y = e.offsetY
var rotateX = 4/30 * y - 20
var rotateY = -1/5 * x + 20
container.style = `transform: perspective(350px) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`
})
</script>

 

2. ์›€์ง์ด๋Š” ์นด๋“œ ํšจ๊ณผ ๊ฒฐ๊ณผ

 

 container์˜์—ญ์— ๋งˆ์šฐ์Šค์˜ ์ขŒํ‘œ(e.offsetX, e.offsetY)์— ๋”ฐ๋ผ์„œ transform ์†์„ฑ์„ ์ค˜์„œ ์นด๋“œ ์ด๋ฏธ์ง€๋ฅผ ํšŒ์ „์‹œํ‚จ๋‹ค.

transform: ์š”์†Œ์˜ ๋ณ€ํ˜•์„ ์ •์˜ํ•˜๋Š” CSS ์†์„ฑ์ž…๋‹ˆ๋‹ค.
ํšŒ์ „, ์ด๋™, ํฌ๊ธฐ ์กฐ์ ˆ, ๊ธฐ์šธ์ž„ ๋“ฑ์˜ ํšจ๊ณผ๋ฅผ ์ ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

perspective(350px): 3D ๋ณ€ํ˜• ํšจ๊ณผ๋ฅผ ์ค„ ๋•Œ ์›๊ทผ๊ฐ์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
๊ฐ’์ด ์ž‘์„์ˆ˜๋ก ์›๊ทผ๊ฐ์ด ๊ฐ•ํ•ด์ ธ์„œ ๋” ๊ทน์ ์ธ ํšจ๊ณผ๋ฅผ ๋‚ผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

rotateX(${rotateX}deg): ์š”์†Œ๋ฅผ X์ถ•์„ ๊ธฐ์ค€์œผ๋กœ ํšŒ์ „์‹œํ‚ต๋‹ˆ๋‹ค.
์ฆ‰, ๋งˆ์šฐ์Šค๊ฐ€ ์œ„์•„๋ž˜๋กœ ์›€์ง์ผ ๋•Œ ์š”์†Œ๊ฐ€ X์ถ•์„ ๊ธฐ์ค€์œผ๋กœ ํšŒ์ „ํ•ฉ๋‹ˆ๋‹ค.

rotateY(${rotateY}deg): ์š”์†Œ๋ฅผ Y์ถ•์„ ๊ธฐ์ค€์œผ๋กœ ํšŒ์ „์‹œํ‚ต๋‹ˆ๋‹ค.
์ฆ‰, ๋งˆ์šฐ์Šค๊ฐ€ ์ขŒ์šฐ๋กœ ์›€์ง์ผ ๋•Œ ์š”์†Œ๊ฐ€ Y์ถ•์„ ๊ธฐ์ค€์œผ๋กœ ํšŒ์ „ํ•ฉ๋‹ˆ๋‹ค..

 

 

3. ์นด๋“œ ์ด๋ฏธ์ง€์— ๊ด‘์›(๋น›) ํšจ๊ณผ ์ฃผ๊ธฐ

<div class="container">
<div class="overlay"></div>
<div class="card"></div>
</div>
<script>
var container = document.querySelector('.container')
var overlay = document.querySelector('.overlay')
container.addEventListener('mousemove', function(e) {
var x = e.offsetX
var y = e.offsetY
var rotateX = 4/30 * y - 20
var rotateY = -1/5 * x + 20
overlay.style = `background-position: ${x/5 + y/5}%`
container.style = `transform: perspective(350px) rotateX(${rotateX}deg) rotateY(${rotateY}deg)`
})
container.addEventListener('mouseout', function() {
overlay.style = 'filter: opacity(0)'
container.style = 'transform: perspective(350px) rotateY(0deg) rotateX(0deg)'
})
</script>
<style>
.container {
width: 220px;
height: 310px;
}
.overlay {
position: absolute;
width: 220px;
height: 310px;
background: linear-gradient(105deg,
transparent 40%,
rgba(255, 219, 112, 0.8) 45%,
rgba(32, 50, 255, 0.6) 50%,
transparent 54%);
filter: brightness(1.2) opacity(0.8);
mix-blend-mode: color-dodge;
background-size: 150% 150%;
background-position: 100%;
transition: all 0.1s;
}
.card {
width: 220px;
height: 310px;
background-image: url(images/3vx2.webp);
background-size: cover;
}
</style>

 

3. ๊ด‘์› ํšจ๊ณผ ์ ์šฉ ๊ฒฐ๊ณผ

 ์นด๋“œ ์ด๋ฏธ์ง€์— ๊ด‘์› ํšจ๊ณผ๋ฅผ ์ฃผ๋Š” ์›๋ฆฌ๋Š” ์นด๋“œ ์ด๋ฏธ์ง€ ์œ„์— ๊ด‘์› ์ด๋ฏธ์ง€(overlay)๋ฅผ ํˆฌ๋ช…ํ•˜๊ฒŒ ๋ฎ๋Š” ์›๋ฆฌ๋‹ค.


position: absolute;:
์ด ์†์„ฑ์€ .overlay ์š”์†Œ๋ฅผ ๋ถ€๋ชจ ์š”์†Œ(.container)๋ฅผ ๊ธฐ์ค€์œผ๋กœ ์ ˆ๋Œ€์ ์ธ ์œ„์น˜์— ๋ฐฐ์น˜ํ•ฉ๋‹ˆ๋‹ค.
์ฆ‰, .overlay๋Š” .container ๋‚ด๋ถ€์—์„œ ์ž์œ ๋กญ๊ฒŒ ์ด๋™ํ•˜๊ณ  ๋ฐฐ์น˜๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

background: linear-gradient(105deg, transparent 40%, rgba(255, 219, 112, 0.8) 45%, rgba(32, 50, 255, 0.6) 50%, transparent 54%);:
์ด ์†์„ฑ์€ .overlay์— ์„ ํ˜• ๊ทธ๋ผ๋ฐ์ด์…˜ ๋ฐฐ๊ฒฝ์„ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.105deg๋Š” ๊ทธ๋ผ๋ฐ์ด์…˜์˜ ๋ฐฉํ–ฅ์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
transparent 40% : ํˆฌ๋ช…ํ•œ ์ƒ‰์ƒ์ด 40%์ง€์ ๊นŒ์ง€ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.

rgba(255, 219, 112, 0.8) 45% :
๋…ธ๋ž€์ƒ‰์— ๊ฐ€๊นŒ์šด ์ƒ‰์ƒ์ด 45%์ง€์ ๊นŒ์ง€ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. alpha๊ฐ’์ด 0.8์ด๊ธฐ ๋•Œ๋ฌธ์— ์•ฝ๊ฐ„ ํˆฌ๋ช…ํ•œ ์ƒํƒœ์ž…๋‹ˆ๋‹ค.

rgba(32, 50, 255, 0.6) 50% : ํŒŒ๋ž€์ƒ‰์— ๊ฐ€๊นŒ์šด ์ƒ‰์ƒ์ด 50%์ง€์ ๊นŒ์ง€ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค.
alpha๊ฐ’์ด 0.6์ด๊ธฐ ๋•Œ๋ฌธ์— ์•ฝ๊ฐ„ ํˆฌ๋ช…ํ•œ ์ƒํƒœ์ž…๋‹ˆ๋‹ค.

transparent 54% : ํˆฌ๋ช…ํ•œ ์ƒ‰์ƒ์ด 54%์ง€์ ๊นŒ์ง€ ์ ์šฉ๋ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ์„ค์ •์„ ํ†ตํ•ด, .overlay๋Š” ํŠน์ • ๊ฐ๋„๋กœ ์ƒ‰์ƒ์ด ๋ณ€ํ™”ํ•˜๋Š” ๋น›๋‚˜๋Š” ํšจ๊ณผ๋ฅผ ๊ฐ€์ง€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

filter: brightness(1.2) opacity(0.8);:
brightness(1.2)๋Š” ์š”์†Œ์˜ ๋ฐ๊ธฐ๋ฅผ 1.2๋ฐฐ๋กœ ์ฆ๊ฐ€์‹œ์ผœ ๋” ๋ฐ๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค.
opacity(0.8)์€ ์š”์†Œ์˜ ํˆฌ๋ช…๋„๋ฅผ 0.8๋กœ ์„ค์ •ํ•˜์—ฌ ์•ฝ๊ฐ„ ํˆฌ๋ช…ํ•˜๊ฒŒ ๋งŒ๋“ญ๋‹ˆ๋‹ค.

mix-blend-mode: color-dodge;:
์ด ์†์„ฑ์€ ๋ฐฐ๊ฒฝ์ƒ‰๊ณผ ์š”์†Œ์˜ ์ƒ‰์ƒ์„ ํ˜ผํ•ฉํ•˜๋Š” ๋ฐฉ์‹์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
color-dodge๋Š” ๋ฐ์€ ์ƒ‰์ƒ์„ ๋” ๋ฐ๊ฒŒ ๋งŒ๋“ค๊ณ  ์–ด๋‘์šด ์ƒ‰์ƒ์€ ๊ฑฐ์˜ ๋ณ€ํ™”์‹œํ‚ค์ง€ ์•Š์•„ ๋น›๋‚˜๋Š” ํšจ๊ณผ๋ฅผ ์ค๋‹ˆ๋‹ค.

background-size: 150% 150%;:
๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€(์ด ๊ฒฝ์šฐ์—๋Š” ๊ทธ๋ผ๋ฐ์ด์…˜)์˜ ํฌ๊ธฐ๋ฅผ ์š”์†Œ์˜ ํฌ๊ธฐ๋ณด๋‹ค 150% ํฌ๊ฒŒ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
์ด๊ฒƒ์€ ๊ทธ๋ผ๋ฐ์ด์…˜์ด ์š”์†Œ ์•ˆ์—์„œ ์›€์ง์ด๋Š” ํšจ๊ณผ๋ฅผ ๋‚ด๋Š”๋ฐ ์‚ฌ์šฉ๋ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

background-position: 100%;:
๋ฐฐ๊ฒฝ ์ด๋ฏธ์ง€์˜ ์ดˆ๊ธฐ ์œ„์น˜๋ฅผ ๊ฐ€๋กœ ๋ฐฉํ–ฅ์œผ๋กœ 100% ์œ„์น˜์— ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

transition: all 0.1s;:
์ด ์†์„ฑ์€ ์š”์†Œ์˜ ์†์„ฑ ๋ณ€ํ™”์— 0.1์ดˆ์˜ ์ „ํ™˜ ํšจ๊ณผ๋ฅผ ์ ์šฉํ•ฉ๋‹ˆ๋‹ค.
์ฆ‰, .overlay์˜ ์Šคํƒ€์ผ์ด ๋ณ€๊ฒฝ๋  ๋•Œ ๋ถ€๋“œ๋Ÿฌ์šด ์• ๋‹ˆ๋ฉ”์ด์…˜ ํšจ๊ณผ๊ฐ€ ๋‚˜ํƒ€๋‚ฉ๋‹ˆ๋‹ค.
๋ฐ˜์‘ํ˜•

๋Œ“๊ธ€