๋ณธ๋ฌธ ๋ฐ”๋กœ๊ฐ€๊ธฐ
๐Ÿ’ป ํ”„๋กœ์ ํŠธ/๐Ÿฐํ† ๋ผ์™€ ๊ฑฐ๋ถ์ด ๊ฒฝ์ฃผ๊ฒŒ์ž„๐Ÿข

[Vue, VITE] ํ† ๋ผ์™€ ๊ฑฐ๋ถ์ด ๊ฒฝ์ฃผ๊ฒŒ์ž„_ ๊ธฐ์กด ์ฝ”๋“œ ์—…๊ทธ๋ ˆ์ด๋“œ ์‹œํ‚ค๊ณ  ์‚ฌ์šฉ์ž ํ™”๋ฉด๋„ ๋ฐ”๊พธ๊ธฐ.

by hyeong._.ing 2026. 5. 28.

 

 

์›๋ž˜ ํ™”๋ฉด์€ ๋ฐคํ‹ฐ์˜€๋‹ค.
Vue๋ฅผ ๊ณต๋ถ€ํ•˜๊ธฐ ์œ„ํ•ด์„œ ๋งŒ๋“ ๊ฑฐ๋ผ ๊ทธ๋‹ค์ง€ ์ด์˜์ง€ ์•Š์•˜๋‹ค.
์ด๋• ํ”„๋ก ํŠธ๊ฐ€ ์ฒ˜์Œ์ด์—ˆ๋˜์ง€๋ผ ๋ถ€์กฑํ•œ ์ ์ด ๋งŽ์•˜๋Š”๋ฐ
๊ทธ๋™์•ˆ ์„ฑ์žฅํ•œ ๋งŒํผ ๊ธฐ๋Šฅ์„ ์ถ”๊ฐ€ํ•ด์„œ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•ด๋ณด์ž

 

 

 

 

1. ์ถฉ๊ฒฉ์ ์ธ ๋ฐคํ‹ฐ ๊ธฐ์กด ํ™”๋ฉด

 

๋‹ค์‹œ๋ด๋„ ๋Œ€์ถฉ๊ฒฉ ๋น„์ฃผ์–ผ์ด๋‹ค. ๋งˆ์น˜ ๊ณต์‚ฐ๋‹น์—์„œ ๋งŒ๋“  ๋น„์ฃผ์–ผ์ด๋‹ค. ์ƒ๊ฐํ•ด๋ณด๋ฉด ํ† ๋ผ๋ž‘ ๊ฑฐ๋ถ์ด๋Š” ์›๋ž˜ ํ™์—์„œ ๊ฒฝ์ฃผํ–ˆ๋˜ ๊ฒƒ ๊ฐ™์€๋ฐ... ๊ณผ๊ฑฐ์˜ ๋‚˜๋Š” ๊ณต์ •์„ฑ์„ ์œ„ํ•ด ๊ฑฐ๋ถ์ด๋ฅผ ๋ฐ”๋‹ค์—์„œ ๊ฒฝ์ฃผ์‹œํ‚จ๊ฑธ๊นŒ?ใ…‹ใ…‹ใ…‹ใ…‹ ์‚ฌ์‹ค ๋†๋‹ด์ด๊ณ  ์ด๋• ๊ทธ๋Ÿฐ ์ƒ๊ฐ ์—†์—ˆ๋‹ค. ํ”„๋ก ํŠธ๊ฐ€ ์‹ ๊ธฐํ•ด์„œ ์ด๊ฒƒ์ €๊ฒƒ ํ•ด๋ณด๋Š” ๊ทธ๋ƒฅ ์ด์ƒํ•œ ์‚ฌ๋žŒ์ด์—ˆ๋˜ ๊ฒƒ! 

์•„๋ฌดํŠผ ์ด์ œ ํฌํŠธํด๋ฆฌ์˜ค๋ฅผ ์ž‘์„ฑํ•ด์•ผํ•ด์„œ ์ด ๋ฐคํ‹ฐ ํ”„๋กœ์ ํŠธ๋ฅผ ๋„ฃ์–ด์•ผํ•˜๋Š”๋ฐ ์ด๋ ‡๊ฒŒ ๋„ฃ์„ ์ˆœ ์—†์„ ๊ฒƒ ๊ฐ™๋‹ค. ๋น„์ฃผ์–ผ ์—…๊ทธ๋ ˆ์ด๋“œ ์‹œํ‚ค๊ณ , ๊ธฐ๋Šฅ๋„ ๊ฐœ์„ ํ•ด๋ณด๊ฒ ๋‹ค.

 

 

 

 


 

 

 

 

2. ์™ธ๊ด€ ๊ฐœ์„ 

  • ์ˆ˜์ • ์ „

ํ™ˆํ™”๋ฉด
๊ฒŒ์ž„ํ™”๋ฉด

๋‚ด ์ถ”์–ต์˜ ํ”„๋กœ์ ํŠธ๊ฐ€ ๋  ๊ฒƒ ๊ฐ™๋‹ค. ๋‚˜์˜ ๋ฏธ๊ฐ ํ˜„์‹ค์„ ์•Œ๋ ค์ฃผ๋Š”...ใ…‹ (๋‚˜๋ฆ„ ๊ท€์—ฌ์šด ๊ฒƒ ๊ฐ™๊ธฐ๋„ ํ•˜๊ณ )

 

 

  • ์ˆ˜์ • ํ›„

ํ™ˆํ™”๋ฉด
๊ฒŒ์ž„ํ™”๋ฉด

๋‚ด ๋ฏธ๊ฐ์˜ ํ•œ๊ณ„๋‹ค. ์ตœ๋Œ€ํ•œ ๋…ธ๋ ฅํ–ˆ๋‹ค. ์•ฝ๊ฐ„ ์†œ์‚ฌํƒ•๊ณผ ๋ฌด์ง€๊ฐœ๋–ก ์‚ฌ์ด์˜ ์ƒ‰๊ฐ ๊ฐ™๋‹ค ใ…‹ใ…‹ใ…‹ ์ด๋ฏธ์ง€๋Š” GPTํ•œํ…Œ ๋งŒ๋“ค์–ด๋‹ฌ๋ผ๊ณ  ํ–ˆ๋‹ค. ์ „์—๋„ GPT๊ฐ€ ๋งŒ๋“ค์–ด์ค€๊ฑด๋ฐ ์ž‘๋…„๋ณด๋‹ค ์˜ฌํ•ด ๊ธฐ๋Šฅ์ด ๋” ์ข‹์•„์ ธ์„œ ๊ท€์—ฝ๊ฒŒ ๋งŒ๋“ค์–ด์คฌ๋‹ค. (๊ทผ๋ฐ ๋†€๋ผ์šด๊ฑด ์ €๊ฑฐ ๋งŒ๋“ค๋•Œ ์ง„์งœ ์—„์ฒญ ๋ฟŒ๋“ฏํ•ดํ–ˆ์Œใ…‹ใ…‹ใ…‹ใ…‹ใ…‹ GPT ๊ทธ๋ฆผ ๊ทธ๋ ค์ฃผ๋Š”๊ฒƒ๋„ ๋งŒ์กฑํ–ˆ๋‹ค๋Š”๊ฒŒ ์›ƒ๊น€ ใ…‹ใ…‹) ๊ทธ๋ฆฌ๊ณ  ์ด์ œ ๊ฑฐ๋ถ์ด๋„ ๋•…์—์„œ ๊ฒฝ์ฃผํ•œ๋‹ค. ์–ต๊นŒ์˜ ์ธ์ƒ์ด ์‹œ์ž‘๋œ ์…ˆ์ด๋‹ค. 

 

 

 

 


 

 

 

 

3. ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ ์ถ”๊ฐ€

"canvas-confetti": "^1.9.4",
"gsap": "^3.15.0"
๋” ์ƒ๋™๊ฐ์žˆ๋Š” ํ‘œํ˜„์„ ์œ„ํ•ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ถ”๊ฐ€ํ–ˆ๋‹ค. (์†”์งํžˆ ์—„์ฒญ๋‚œ ์ฐจ์ด๊ฐ€ ์žˆ์ง„ ์•Š๋‹ค)

 canvas-confetti๋Š” ์Šน๋ฆฌ ํ–ˆ์„ ๋•Œ ํญ์ฃฝ ํšจ๊ณผ๋ฅผ ๋„ฃ๊ธฐ ์œ„ํ•ด ์ถ”๊ฐ€ํ–ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์›๋ž˜ ํ† ๋ผ์™€ ๊ฑฐ๋ถ์ด ์ด๋™์„ css keyframe์œผ๋กœ ์ง„ํ–‰ํ–ˆ๋Š”๋ฐ gsap์„ ์ถ”๊ฐ€ํ•ด์„œ JS ์• ๋‹ˆ๋ฉ”์ด์…˜์œผ๋กœ ์ œ์–ดํ–ˆ๋‹ค. 

 

 

 

 


 

 

 

4. ๊ฒฝ์ฃผ ๋กœ์ง ๋ณ€๊ฒฝ

  • ๊ธฐ์กด CSS keyframe ๋ฐฉ์‹
data() {
  return {
    rabbitRunning: false,
    rabbitStopping: false,
    turtleRunning: false,
  }
}
๊ฑฐ๋ถ์ด๋Š” ํ•ญ์ƒ ์ผ์ •ํ•œ ์†๋„๋กœ ๋‹ฌ๋ฆฐ๋‹ค. ๊ฒŒ์ž„์˜ ์Šน๋ฆฌ์—ฌ๋ถ€๋Š” ํ† ๋ผ์—๊ฒŒ ๋‹ฌ๋ ค์žˆ๋Š” ์…ˆ์ด๋‹ค. rabbitRunnig์€ ํ† ๋ผ๊ฐ€ ๋‚˜๋ฌด์—์„œ ์ ๋‹นํžˆ ์‰ฌ๋Š” ๋ฒ„์ „์ด๊ณ  rabbitStopping์€ ํ† ๋ผ๊ฐ€ ๋‚˜๋ฌด์—์„œ ๋งŽ์ด ์‰ฌ์—ˆ์„ ๋•Œ ๋ฒ„์ „์ด๋‹ค. ์ด๋•Œ ์ด ์…‹์˜ ๊ธฐ๋ณธ ์ƒํƒœ๊ฐ’์„ false๋กœ ์›€์ง์ž„์ด ์—†๋„๋ก ํ•˜๊ณ , ์‹œ์ž‘ ๋ฒ„ํŠผ์„ ๋ˆ„๋ฅด๋ฉด ์ƒํƒœ๊ฐ’์ด ๋ฐ”๋€Œ๋„๋ก ํ–ˆ๋‹ค.
gameStart() {
  const currentSeconds = new Date().getSeconds()

  if (currentSeconds % 2 === 0) {
    this.rabbitRunning = true
  } else {
    this.rabbitStopping = true
  }

  this.turtleRunning = true
}
ํ˜„์žฌ ์ดˆ๋ฅผ ๊ฐ€์ ธ์™€์„œ ์ดˆ๋ฅผ 2๋กœ ๋‚˜๋ˆˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๋‚˜๋จธ์ง€๊ฐ€ 0์ด๋ฉด(์ง์ˆ˜) ํ† ๋ผ๊ฐ€ ์ด๊ธฐ๋„๋ก ํ–ˆ๋‹ค. 0์ด ์•„๋‹ˆ๋ฉด ํ† ๋ผ๊ฐ€ ์ง€๋„๋ก ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ–ˆ๋‹ค.

 

@keyframes rabbit-run {
  0% {
    left: -10%;
  }
  40% {
    left: 50%;
  }
  60% {
    left: 50%;
  }
  100% {
    left: 90%;
  }
}

.running-rabbit.run {
  animation: rabbit-run 5s linear forwards;
}

.running-rabbit.stop {
  animation: rabbit-run 7s linear forwards;
}
์ฒซ ์‹œ์ž‘์€ ์™ผ์ชฝ์—์„œ -10 ์œ„์น˜์—์„œ ์‹œ์ž‘ํ•œ๋‹ค. ํ™”๋ฉด์ƒ์œผ๋ก  ๊ทธ๋ƒฅ ์™ผ์ชฝ ๋์— ์žˆ๋„๋ก ๋ณด์ธ๋‹ค. ํ† ๋ผ๊ฐ€ ์ค‘๊ฐ„์ฏค ์˜ค๋ฉด ๋ฉˆ์ถ˜๋‹ค. ์ด ๋ถ€๊ทผ์ด ๋‚˜๋ฌด๊ฐ€ ์žˆ๋Š” ๋ถ€๊ทผ์ด๋‹ค. ํ† ๋ผ๋Š” 60%์—์„œ ๋‹ค์‹œ ์›€์ง์ด๋Š”๋ฐ running-rabbit.run์ธ ๊ฒฝ์šฐ ์ด ๋ชจ๋“  ์ง„ํ–‰์„ 5์ดˆ์— ๋๋‚ด๋„๋ก ํ–ˆ๊ณ  running-rabbit.stop์€ 7์ดˆ์— ๋๋‚ด๋„๋ก ํ–ˆ๋‹ค.

 

@keyframes turtle-run {
  0% {
    left: -10%;
  }
  100% {
    left: 90%;
  }
}

.running-turtle.run {
  animation: turtle-run 6s linear forwards;
}
๊ฑฐ๋ถ์ด ์ฝ”๋“œ๋Š” ๋” ๊ฐ„๋‹จํ•˜๋‹ค. ๋ฌด์กฐ๊ฑด left -10์—์„œ 90๊นŒ์ง€ ๊ฐ€๋Š” ๊ฑธ 6์ดˆ์— ๋๋‚ด๋„๋ก ์„ค์ •ํ–ˆ๋‹ค.

 

 

  • GSAP
<img ref="rabbitEl" src="@/assets/rabbit2.png" class="runner runner-rabbit" />
<img ref="turtleEl" src="@/assets/turtle2.png" class="runner runner-turtle" />
const rabbitEl = ref(null)
const turtleEl = ref(null)

const {
  countdown,
  message,
  reset,
  running,
  start,
  winner,
} = useRaceGame({ rabbitEl, turtleEl })
ํ† ๋ผ, ๊ฑฐ๋ถ์ด ์ด๋ฏธ์ง€์— ref๋ฅผ ๋ถ™์ด๊ณ  ์ด DOM ์š”์†Œ๋ฅผ useRaceGame(jsํŒŒ์ผ)์— ๋„˜๊ฒผ๋‹ค. ์ด์ œ ์• ๋‹ˆ๋ฉ”์ด์…˜์€ CSS ํด๋ž˜์Šค๊ฐ€ ์•„๋‹ˆ๋ผ JS์—์„œ ์ง์ ‘ ์‹คํ–‰ํ•˜๊ฒŒ ๋œ๋‹ค. 
function pickWinner() {
  const currentSeconds = new Date().getSeconds()
  return currentSeconds % 2 === 0 ? 'rabbit' : 'turtle'
}
๋จผ์ € ์Šน์ž๋ฅผ ๋ฝ‘์ž. ๋กœ์ง์€ ์ฒ˜์Œ ์ง ๊ฑฐ๋ž‘ ๋˜‘๊ฐ™๋‹ค. ํ˜„์žฌ ์ดˆ๊ฐ€ ์ง์ˆ˜๋ฉด rabbit์˜ ์Šน๋ฆฌ, ํ™€์ˆ˜๋ฉด turtle์˜ ์Šน๋ฆฌ์ด๋‹ค. 
function run(nextWinner) {
    const finishX = () => window.innerWidth * 0.82

    timeline = gsap.timeline({
      defaults: {
        ease: 'power1.inOut',
      },
    })
๊ฒฐ์Šน์„  ์œ„์น˜๋Š” finishX, ํ™”๋ฉด ์ „์ฒด ๋„ˆ๋น„(window.innerWidth)์—์„œ 82% ์ง€์ ์„ ๊ฒฐ์Šน์„ ์œผ๋กœ ์„ค์ •ํ–ˆ๋‹ค. ํ•จ์ˆ˜ () => ํ˜•ํƒœ๋กœ ์ž‘์„ฑํ•ด์„œ ์ฐฝ์˜ ํฌ๊ธฐ๊ฐ€ ๋ณ€ํ•ด๋„ ํ•ญ์ƒ ๋™์ ์œผ๋กœ ๋งž์ถฐ์ง€๋„๋ก ํ–ˆ๋Š”๋ฐ, ์ž˜ ๋ ์ง€ ๋ชจ๋ฅด๊ฒ ๋‹ค. (๋‚ด๊ฒ ํ™”๋ฉด์ด ํ•˜๋‚˜์ด๋‹ค!)

gsap.timeline( )์€ ์—ฌ๋Ÿฌ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์—ฐ๊ฒฐํ•ด์„œ ์žฌ์ƒํ•˜๊ธฐ ์œ„ํ•ด ํƒ€์ž„๋ผ์ธ์„ ๋งŒ๋“ค์—ˆ๋‹ค. ๊ธฐ๋ณธ ์›€์ง์ž„์ธ ease ์†์„ฑ๊ฐ’์ธ power1.inOut์„ ์‚ฌ์šฉํ–ˆ๋‹ค. ์ด๊ฑด ๋‚˜์ค‘์— gsap ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์ž‘์„ฑํ•˜๋ฉด์„œ ๋” ์ž์„ธํžˆ ์„ค๋ช…ํ•˜๊ฒ ๋‹ค.

 

    if (nextWinner === 'rabbit') {
      timeline
        .to(rabbitEl.value, {
          x: () => window.innerWidth * 0.42,
          y: -10,
          rotate: -4,
          duration: 2,
          ease: 'power2.out',
        }, 0)
        .to(rabbitEl.value, {
          y: 4,
          rotate: -8,
          duration: 0.65,
          ease: 'sine.inOut',
        })
        .to(rabbitEl.value, {
          y: -8,
          rotate: 3,
          duration: 0.3,
          ease: 'power2.out',
        })
        .to(rabbitEl.value, {
          x: finishX,
          y: -12,
          rotate: 3,
          duration: 2.2,
          ease: 'power3.out',
          onComplete: () => end('rabbit'),
        })
        .to(turtleEl.value, {
          x: () => window.innerWidth * 0.62,
          y: 5,
          duration: 5.8,
        }, 0)
    }
ํ† ๋ผ๊ฐ€ ์ด๊ธธ๋•Œ ์ฝ”๋“œ์ด๋‹ค. ์ถœ๋ฐœํ•˜์ž๋งˆ์ž ํ™”๋ฉด์˜ 42% ์ง€์ ๊นŒ์ง€ ๋›ฐ์–ด๊ฐ„๋‹ค. ์ด๊ฒŒ 2์ดˆ ๊ฑธ๋ฆฌ๋„๋ก ํ–ˆ๋‹ค. ๋‚˜๋ฌด ๋ถ€๊ทผ๊นŒ์ง€ ๊ฐ€๋ฉด ๋ฉˆ์ถฐ์„œ ์•ฝ๊ฐ„์˜ ์›€์ง์ž„์„ ์ฃผ์—ˆ๋‹ค. ์‚ด์ง ๋ชธ์„ ํ”๋“œ๋Š” ๋™์ž‘์ธ๋ฐ, ๋ณ„ ๋‹ค๋ฅธ ์˜๋ฏธ๋Š” ์—†๊ณ  ๋‚˜๋ฆ„ ์žˆ์–ด๋ณด์ด๊ณ  ์‹ถ์–ด์„œ ์ถ”๊ฐ€ํ–ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ๊ฒฐ์Šน์„ ์œผ๋กœ ๋น ๋ฅด๊ฒŒ ๋‹ฌ๋ จ๊ฐ„๋‹ค. ์ด๋•Œ power3.out์„ ์ผ๋‹ค. ๋„์ฐฉํ•˜๋ฉด onComplete๊ฐ€ ๋ฐœ๋™ํ•ด์„œ end('rabbit') ํ•จ์ˆ˜๋ฅผ ๋ถ€๋ฅด๊ณ  ๊ฒฝ์ฃผ๊ฐ€ ๋๋‚œ๋‹ค.

๊ฑฐ๋ถ์ด๋Š” ํ† ๋ผ์™€ ๋™์‹œ์— ์ถœ๋ฐœํ•˜์ง€๋งŒ 5.8์ดˆ๋™์•ˆ ํ™”๋ฉด์˜ 62% ์ง€์ ๊นŒ์ง€ ๊ฐ€๋„๋ก ์„ค์ •ํ–ˆ๋‹ค. ๊ฒฐ์Šน์„ ์— ๋‹ฟ์ง€ ๋ชปํ•˜๊ณ  ๋๋‚œ๋‹ค.

 

 else {
      timeline
        .to(rabbitEl.value, {
          x: () => window.innerWidth * 0.38,
          y: -10,
          duration: 2,
          ease: 'power2.out',
        }, 0)
        .to(rabbitEl.value, {
          rotate: -8,
          y: 2,
          duration: 1.1,
          ease: 'sine.inOut',
          yoyo: true,
          repeat: 1,
        })
        .to(rabbitEl.value, {
          x: () => window.innerWidth * 0.68,
          y: -6,
          rotate: 2,
          duration: 2.8,
        })
        .to(turtleEl.value, {
          x: finishX,
          y: 2,
          duration: 5.7,
          ease: 'power1.out',
          onComplete: () => end('turtle'),
        }, 0)
    }
  }
๊ฑฐ๋ถ์ด๊ฐ€ ์ด๊ธธ๋•Œ ์ฝ”๋“œ์ด๋‹ค. ํ† ๋ผ๊ฐ€ ํ™”๋ฉด์˜ 38% ์ง€์ ๊นŒ์ง€ ๊ฐ„๋‹ค. ์•„๋งˆ ์ด ์ง€์ ์ด ๋‚˜๋ฌด ๋ถ€๊ทผ์ผ ๊ฒƒ์ด๋‹ค. ์ด ๋‹ค์Œ ์ฝ”๋“œ์— yoyo: true๊ฐ€ ์žˆ๋Š”๋ฐ, ๋‚˜๋ฌด ๋ถ€๊ทผ์—์„œ ํ† ๋ผ์˜ ์›€์ง์ž„์ด ์—ญ์žฌ์ƒ๋˜๋„๋ก ํ•œ ๊ฒƒ์ด๋‹ค. ๊ฒŒ์ž„์„ ์ง„ํ–‰ํ•  ๋•Œ ๋ณด๋ฉด ํ† ๋ผ๊ฐ€ ์ด๊ธธ๋• ํ† ๋ผ์˜ ๊ธฐ์šธ๊ธฐ๊ฐ€ ํ•œ ๋ฒˆ ์›€์ง์ธ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ๊ฑฐ๋ถ์ด๊ฐ€ ์ด๊ธธ ๋• ํ† ๋ผ์˜ ์›€์ง์ž„์ด 2๋ฒˆ ๋ฐœ์ƒํ•œ๋‹ค. ์ด๊ฒŒ yoyo๋ฅผ trueํ•ด์„œ ๊ทธ๋ ‡๋‹ค. repeat์„ 1๋กœ ์„ค์ •ํ•ด์„œ ํ•œ๋ฒˆ๋งŒ ์—ญ์žฌ์ƒ ๋˜๋„๋ก ํ–ˆ๋‹ค. ๊ทธ๋‹ค์Œ ํ† ๋ผ๋Š” ๋‹ค์‹œ ๊ฒฐ์Šน์„ ์„ ํ–ฅํ•ด ์›€์ง์ด์ง€๋งŒ 68%์—์„œ ๋ฉˆ์ถ”๊ฒŒ ๋  ๊ฒƒ์ด๋‹ค.

๊ฑฐ๋ถ์ด๋Š” 5.7ํฌ ๋™์•ˆ ํ•œ๊ฒฐ๊ฐ™์€ ๊ฑธ์Œ์œผ๋กœ ๊ฒฐ์Šน์„ ์„ ํ–ฅํ•ด ์›€์ง์ธ๋‹ค.

 

 

 

 


 

 

 

5. ์ด๊ธด ๋™๋ฌผ ํญ์ฃฝ

const origin = nextWinner === 'rabbit'
  ? { x: 0.88, y: 0.25 }
  : { x: 0.88, y: 0.72 }
์šฐ์Šนํ•œ ๋™๋ฌผ์ด ์žˆ๋Š” ์œ„์น˜์—์„œ ํญ์ฃฝ์ด ํ„ฐ์ง€๋„๋ก ์ขŒํ‘œ๋ฅผ ๊ณ„์‚ฐํ–ˆ๋‹ค. ๊ฒฐ์Šน์„ ์€ ํ™”๋ฉด์˜ 88% ์ง€์ ์— ์žˆ๋‹ค. (์™ผ์ชฝ์„ ๊ธฐ์ค€์œผ๋กœ 88%๋งŒํผ ๋–จ์–ด์ ธ ์žˆ๋‹ค๋Š” ๊ฒƒ) ํ† ๋ผ๊ฐ€ ์ด๊ธฐ๋ฉด A : B ์ค‘ A๊ฐ€ ๋œ๋‹ค. (true๋ฉด A, false๋ฉด B) ๊ทธ๋ž˜์„œ ํ† ๋ผ๊ฐ€ ์ด๊ธฐ๋ฉด y์ถ•์ด ํ™”๋ฉด์˜ 25%(์œ„๊ฐ€ ๊ธฐ์ค€) ์ง€์ ์—์„œ ํญ์ฃฝ์ด ํ„ฐ์ง€๊ณ  ํ† ๋ผ๊ฐ€ ์ง€๋ฉด ๊ฑฐ๋ถ์ด๊ฐ€ ์žˆ๋Š” ๋ถ€๊ทผ์ธ 72% ์ง€์ ์—์„œ ํ„ฐ์ง„๋‹ค. 

 

confetti({
  particleCount: 110,
  spread: 70,
  startVelocity: 36,
  origin,
  colors: ['#d692c8', '#8caa97', '#f8d0e5', '#fbfbb4', '#ffffff'],
})
ํญ์ฃฝ์„ ์ด์˜๊ฒŒ ๋งŒ๋“ค์—ˆ๋‹ค. ์ด ๋ถ€๋ถ„์€ ์›€์ง์ž„๊ณผ ์ƒ‰์ƒ์„ ์„ค์ •ํ•œ ๋ถ€๋ถ„์ด๋‹ค. particleCount๋Š” 110, ๊ฝƒ๊ฐ€๋ฃจ์˜ ๊ฐœ์ˆ˜๋ฅผ ์˜๋ฏธํ•œ๋‹ค. spread: 70์€ ๊ฝƒ๊ฐ€๋ฃจ๊ฐ€ ํผ์ง€๋Š” ๊ฐ๋„์ด๋‹ค. ๊ทธ๋ฆฌ๊ณ  startVelocity๋Š” ํญ์ฃฝ์ด ์ฒ˜์Œ ํ„ฐ์ ธ ๋‚˜๊ฐˆ ๋•Œ์˜ ์ดˆ๊ธฐ ์†๋„์ด๋‹ค. ์ˆซ์ž๊ฐ€ ํด์ˆ˜๋ก ๋” ๋ฉ€๋ฆฌ ๊ฐ•ํ•˜๊ฒŒ ๋‚˜๊ฐ„๋‹ค. origin์€ ์•„๊นŒ ์œ„์—์„œ ๊ณ„์‚ฐํ•œ ์Šน์ž์˜ ์ขŒํ‘œ๋ฅผ ๋„ฃ๋Š” ๋ถ€๋ถ„์ด๋‹ค. colors๋Š” ๊ฝƒ๊ฐ€๋ฃจ ์ƒ‰์ƒ ์กฐํ•ฉ์ด ๋œ๋‹ค.

 

 


 

https://www.npmjs.com/package/canvas-confetti

 

 

 

https://gsap.com/