Skip to content

๐Ÿชต 4. ๋ณด๊ฐ„๋ฒ• ์ ์šฉ ๋ฐ ์ปค์„œ ๋ณด์ •

D.Joung edited this page Dec 5, 2024 · 1 revision

์„ ์„ ๋น ๋ฅด๊ฒŒ ๊ทธ๋ ธ์„ ๋•Œ ๋ชจ์„œ๋ฆฌ ๋ถ€๋ถ„์ด ๋Š๊น€

๋ฌธ์ œ ์ •์˜

  • ๋น ๋ฅด๊ฒŒ ๊ทธ๋ ธ์„ ๋•Œ ๋ชจ์„œ๋ฆฌ ๋ถ€๋ถ„์—์„œ ์„ ์ด ๋Š๊ธฐ๋Š” ํ˜„์ƒ์ด ๋ฐœ์ƒํ•˜์˜€์Šต๋‹ˆ๋‹ค.

  • ํ•˜์ง€๋งŒ ํ„ฐ์น˜ ์ด๋ฒคํŠธ์—์„œ๋Š” ๋ฐœ์ƒํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. (MouseEvent ํŠธ๋ฆฌ๊ฑฐ ์‹œ์—๋งŒ ๋ฐœ์ƒ)

๋ฌธ์ œ ์›์ธ

  • mousemove๋Š” ๋งˆ์šฐ์Šค๊ฐ€ ์›€์ง์ผ ๋•Œ ํŠธ๋ฆฌ๊ฑฐ๋˜์ง€๋งŒ, ๋ชจ๋“  ํ”ฝ์…€์ด ์ด๋ฒคํŠธ์— ์ „๋‹ฌ๋˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค.
  • mousemove ์ด๋ฒคํŠธ๋Š” ์‚ฌ์šฉ์ž ๋””๋ฐ”์ด์Šค์˜ Polling Rage(์ƒ˜ํ”Œ๋ง ๋นˆ๋„)์— ๋”ฐ๋ผ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.
  • ์ผ๋ฐ˜์ ์ธ ๋งˆ์šฐ์Šค์˜ ํด๋ง ๋ ˆ์ดํŠธ๋Š” 125Hz ๋˜๋Š” 1000Hz(๊ฒŒ์ด๋ฐ ๋งˆ์šฐ์Šค)์ž…๋‹ˆ๋‹ค.
  • ๋ธŒ๋ผ์šฐ์ €๋Š” ํ•˜๋“œ์›จ์–ด์—์„œ ๋ฐœ์ƒํ•œ ๋ชจ๋“œ ๋งˆ์šฐ์Šค ์ด๋™ ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•˜์ง€ ์•Š๊ณ , ์ด๋ฅผ ์ตœ์ ํ™”ํ•˜์—ฌ ์ƒ˜ํ”Œ๋งํ•œ ์ขŒํ‘œ๋งŒ ์ „๋‹ฌํ•ฉ๋‹ˆ๋‹ค.
  • ๋”ฐ๋ผ์„œ, ๋น ๋ฅด๊ฒŒ ๊ทธ๋ฆด ๊ฒฝ์šฐ mousemove๋Š” ํ˜„์žฌ ์ƒ˜ํ”Œ๋ง๊ณผ ๋‹ค์Œ ์ƒ˜ํ”Œ๋ง์˜ ์ค‘๊ฐ„ ํ”ฝ์…€์„ ๋†“์น  ์ˆ˜๋„ ์žˆ๊ฒŒ ๋˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

๋ฌธ์ œ ํ•ด๊ฒฐ1. ๊ฒฝ๊ณ„์„  ๋ถ€๋ถ„ ์ขŒํ‘œ ๋ณด๊ฐ„ [์„ฑ๊ณต]

  • ๋งˆ์šฐ์Šค๊ฐ€ ๊ฐ์ฒด๋ฅผ ๋ฒ—์–ด๋‚  ๋•Œ ํŠธ๋ฆฌ๊ฑฐ๋˜๋Š” mouseleave ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์— ์ขŒํ‘œ๊ฐ’์„ ์ถ”๊ฐ€ํ•˜์—ฌ, ๊ฒฝ๊ณ„์„  ๋ถ€๋ถ„์˜ ์ขŒํ‘œ๋ฅผ ๋ณด๊ฐ„ํ•ด์ค๋‹ˆ๋‹ค.
const { canvas, ctx } = getCanvasContext(mainCanvasRef);
const { x: drawX, y: drawY } = convertCoordinate(getDrawPoint(e, canvas));

ctx.lineTo(drawX, drawY);
ctx.stroke();
  • ๋งˆ์šฐ์Šค๊ฐ€ ํ™”๋ฉด ๋ฐ–์„ ๋ฒ—์–ด๋‚  ๋•Œ์˜ ์ขŒํ‘œ๋ฅผ ์–ป์–ด ๋ชจ์„œ๋ฆฌ๊นŒ์ง€ ์„ ์„ ๊ทธ์„ ์ˆ˜ ์žˆ๋„๋ก ํ•ด์ฃผ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

  • ํ•˜์ง€๋งŒ ํ•ด๋‹น ์กฐ์น˜๋กœ ๋งˆ๋ฌด๋ฆฌํ•˜๊ธฐ์—๋Š”, touchEvent์—์„œ๋Š” ์™œ ์›๋ž˜๋ถ€ํ„ฐ ์ž˜ ์ž‘๋™ํ•˜์˜€๋Š” ์ง€ ๊ถ๊ธˆ์ฆ์ด ์ƒ๊ฒผ์Šต๋‹ˆ๋‹ค.

    ์™œ touchEvent์—์„œ๋Š” ํ•ด๋‹น ํ˜„์ƒ์ด ์—†๋Š”๊ฐ€?

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

๋ฌธ์ œ ํ•ด๊ฒฐ2. ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ document์— ๋ถ€์ฐฉํ•˜์—ฌ ์ž…๋ ฅ ์ขŒํ‘œ๊ฐ’์„ ์ „์—ญ์—์„œ ๊ด€๋ฆฌ [์‹คํŒจ]

  • ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๋ฅผ ํŠน์ • ์š”์†Œ์— ๋ถ€์ฐฉํ•˜์—ฌ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ, ๋งˆ์šฐ์Šค ์ปค์„œ๊ฐ€ ๋น ๋ฅด๊ฒŒ ์š”์†Œ๋ฅผ ์ดํƒˆํ–ˆ์„ ๊ฒฝ์šฐ ์ด๋ฒคํŠธ๊ฐ’์„ ๋ฐ›์ง€ ๋ชปํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
  • ์•„๋ž˜์™€ ๊ฐ™์€ ๋ฐฉ๋ฒ•์œผ๋กœ ์ „์—ญ ์ˆ˜์ค€์— ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋ถ™์—ฌ ์ œ์–ดํ•ด์ฃผ์—ˆ์Šต๋‹ˆ๋‹ค.
  const canvasEventHandlers: CanvasEventHandlers = {
    mousedown: (e) => addDelegation(e, handleDrawStart),
    mousemove: (e) => addDelegation(e, handleDrawMove),
    mouseup: (e) => addDelegation(e, stopDrawing),
    mouseleave: (e) => addDelegation(e, stopDrawing),
    touchstart: (e) => addDelegation(e, handleDrawStart),
    touchmove: (e) => addDelegation(e, handleDrawMove),
    touchend: (e) => addDelegation(e, stopDrawing),
    touchcancel: (e) => addDelegation(e, stopDrawing),
  };

  useEffect(() => {
    for (const [event, handler] of Object.entries(canvasEventHandlers)) {
      document.addEventListener(event, handler);
    }

    return () => {
      for (const [event, handler] of Object.entries(canvasEventHandlers)) {
        document.removeEventListener(event, handler);
      }
    };
  }, [canvasEventHandlers]);
  • ํ•˜์ง€๋งŒ ๊ฒฝ๊ณ„์„  ๋ถ€๊ทผ์—์„œ ์„ ์ด ๋Š๊ธฐ๋Š” ์ด์Šˆ๋Š” ํ•ด๊ฒฐ๋˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.
  • ๋˜ํ•œ, ์œ„ ์ฒ˜๋Ÿผ ๊ตฌํ˜„ ์‹œ ๋ณ€๊ฒฝํ•ด์•ผํ•  ๋ถ€๋ถ„์ด ๋” ์žˆ์„ ๊ฒƒ ๊ฐ™์•„, ์„ฑ๋Šฅ ๋ฌธ์ œ๊ฐ€ ๋ฐœ๊ฒฌ๋˜์—ˆ์„ ๋•Œ ๋ฆฌํŒฉํ† ๋งํ•˜์—ฌ ๊ฐœ์„ ํ•˜๋Š” ๋ฐฉํ–ฅ์ด ๋งž์„ ๊ฒƒ ๊ฐ™๋‹ค๋Š” ๊ฒฐ๋ก ์— ์ด๋ฅด๋ €์Šต๋‹ˆ๋‹ค.

๊ฒฐ๋ก 

  • ๋”ฐ๋ผ์„œ ์šฐ์„  stopDrawing ๋ฉ”์†Œ๋“œ์— ๊ฒฝ๊ณ„์„  ์ขŒํ‘œ๋ฅผ ๋ณด๊ฐ„ํ•˜๋Š” ์ฒซ๋ฒˆ์งธ ๋ฐฉ๋ฒ•์œผ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ํ•˜์ง€๋งŒ ํ•ด๋‹น ๋ฐฉ๋ฒ•์œผ๋กœ๋Š” ๋ธŒ๋Ÿฌ์‹œ๊ฐ€ ํด๋ฆญํ•œ ์ƒํƒœ์—์„œ ์บ”๋ฒ„์Šค ๋ฐ–์œผ๋กœ ๋‚˜๊ฐ”๋‹ค๊ฐ€ ๋Œ์•„์™”์„ ๋•Œ ์„ ์ด ์ด์–ด์ง€์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค.
  • ๋งˆ์šฐ์Šค ์ด๋ฒคํŠธ๊ฐ€ ์บ”๋ฒ„์Šค์— ๋ถ™์–ด์žˆ์œผ๋ฉด, ๋งˆ์šฐ์Šค๊ฐ€ ์บ”๋ฒ„์Šค ๋ฐ–์œผ๋กœ ๋‚˜๊ฐ”์„ ๋•Œ์˜ ์ƒํƒœ๋ฅผ ๋ชจ๋‹ˆํ„ฐ๋ง ํ•  ์ˆ˜๊ฐ€ ์—†๊ธฐ ๋•Œ๋ฌธ์ด์—ˆ์Šต๋‹ˆ๋‹ค.
  • ๋”ฐ๋ผ์„œ ์œ„ ๋ฌธ์ œ๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์ด๋ฒคํŠธ๋ฅผ document์— ๋ถ™์—ฌ ์ „์—ญ์—์„œ ๊ด€๋ฆฌํ•ด์•ผ ํ•  ๊ฒƒ์ด๋ฉฐ, ํ•ด๋‹น ํ•„์š”์„ฑ์ด ์ƒ๊ฒผ์„ ๋•Œ ์‹œ๋„ํ•  ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

์„ ์„ ๊ทธ๋ฆด ๋•Œ ๊ฐ์ด ์ง€๋Š” ํ˜„์ƒ

  • ์„ ์„ ๊ทธ๋ฆด ๋•Œ ๋ถ€๋“œ๋Ÿฝ๊ฒŒ ๊ทธ๋ ค์ง€์ง€ ์•Š๋Š” ์ด์Šˆ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
  • ์ฒ˜์Œ์—๋Š” ํ”ฝ์…€์ด ๊นจ์ง€๋Š” ํ˜„์ƒ์ธ ์ค„ ์•Œ๊ณ  ์•ˆํ‹ฐ ์•จ๋ฆฌ์–ด์‹ฑ์„ ์ ์šฉํ•˜์˜€์ง€๋งŒ, ์ดํ›„ ๊ทธ๋ฆด ๋•Œ ์„ ์ด ๊ตด๊ณก์ง€๋Š” ์ด์Šˆ์— ๊ฐ€๊น๋‹ค๋Š” ์‚ฌ์‹ค์„ ๊นจ๋‹ฌ์•˜์Šต๋‹ˆ๋‹ค.
  • ์•„๋ž˜๋Š” ์ˆœ์„œ๋Œ€๋กœ ํƒ€ ์„œ๋น„์Šค์ธ ๊ฐˆํ‹ฑํฐ์˜ ๋ถ€๋“œ๋Ÿฌ์šด ์„ , ์šฐ๋ฆฌ ์„œ๋น„์Šค์˜ ๊ฐ์ง€๋Š” ์„  ์ด๋ฏธ์ง€์ž…๋‹ˆ๋‹ค.

์•Œ์•„๋‘๋ฉด ์ข‹์„ ์ง€์‹

๋งˆ์šฐ์Šค ํด๋ง ๋ ˆ์ดํŠธ(Polling Rate)๋ž€?

  • ํด๋ง ๋ ˆ์ดํŠธ๋Š” ํŠน์ • ๊ธฐ๊ธฐ์—์„œ PC๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†กํ•˜๋Š” ์ฃผ๊ธฐ์ž…๋‹ˆ๋‹ค. ์บ”๋ฒ„์Šค ์ƒํ™ฉ์—์„œ๋Š” ๋งˆ์šฐ์Šค ํ˜น์€ ํ„ฐ์น˜ ์Šคํฌ๋ฆฐ์ด PC๋กœ ์ขŒํ‘œ๋ฅผ ์ „๋‹ฌํ•˜๋Š” ๋นˆ๋„๊ฐ€ ๋  ๊ฒƒ์ž…๋‹ˆ๋‹ค.
  • ๋‹จ์œ„๋Š” Hz(ํ—ค๋ฅด์ธ )๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ดˆ ๋‹น ๋ช‡ ๋ฒˆ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ „๋‹ฌํ•˜๋Š” ์ง€๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ์ˆ˜์น˜์ž…๋‹ˆ๋‹ค.
  • ์ผ๋ฐ˜์ ์ธ ํด๋ง ๋ ˆ์ดํŠธ ๋‹จ์œ„๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.
  • ํ•˜์ง€๋งŒ! ์ผ๋ฐ˜์ ์ธ ๋ธŒ๋ผ์šฐ์ €์˜ ๋งˆ์šฐ์Šค ์ด๋ฒคํŠธ๋Š” ์œ„์™€ ๊ฐ™์€ ํ•˜๋“œ์›จ์–ด์˜ ํด๋ง ๋ ˆ์ดํŠธ์— ์™„๋ฒฝํžˆ ๋™๊ธฐํ™”๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋ธŒ๋ผ์šฐ์ € ๋งˆ์šฐ์Šค ์ด๋ฒคํŠธ ๋ฐœ์ƒ ๋นˆ๋„

  • ๋ธŒ๋ผ์šฐ์ € ๋งˆ์šฐ์Šค ์ด๋ฒคํŠธ์˜ ๋ฐœ์ƒ ๋นˆ๋„๋Š” ํ™”๋ฉด ์žฌ์ƒ ๋นˆ๋„(Refresh Rate)์™€ ๋…๋ฆฝ์ ์œผ๋กœ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.
    • ๋Œ€๋ถ€๋ถ„์˜ ๋””์Šคํ”Œ๋ ˆ์ด๋Š” 60Hz(16.67ms ๊ฐ„๊ฒฉ)๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.
    • ๋ธŒ๋ผ์šฐ์ €๋Š” ์‹œ์Šคํ…œ ์ด๋ฒคํŠธ ํ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๋งˆ์šฐ์Šค ์ด๋ฒคํŠธ๋ฅผ ์ฒ˜๋ฆฌํ•ฉ๋‹ˆ๋‹ค..
    • ํด๋ง ๋ ˆ์ดํŠธ ๊ฐ„๊ฒฉ์œผ๋กœ ๋งˆ์šฐ์Šค๊ฐ€ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ด๋ฉด, ๋ธŒ๋ผ์šฐ์ €๋Š” ์ด๋ฅผ ํ† ๋Œ€๋กœ mouseEvent๋ฅผ ๋ฐœ์…์‹œํ‚ต๋‹ˆ๋‹ค.
      • ์ฆ‰, ๋งˆ์šฐ์Šค ์ด๋ฒคํŠธ ๋ฐœ์ƒ ๋นˆ๋„๋Š” ํด๋ง ๋ ˆ์ดํŠธ์™€ ๊ด€๋ จ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

Polling Rate์™€ Refresh Rate ๊ฐ„์˜ ๊ด€๊ณ„

  • ํ•˜์ง€๋งŒ, Polling Rate๊ฐ€ ์•„๋ฌด๋ฆฌ ๋†’์•„๋„ Refrash Rate๊ฐ€ 60Hz๋ฉด ํด๋ง ๋ ˆ์ดํŠธ ๋œ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๊ฐ€ ์ƒ˜ํ”Œ๋ง๋˜์ง€๋Š” ๋ชปํ•ฉ๋‹ˆ๋‹ค.
  • ๋งˆ์šฐ์Šค๊ฐ€ 1์ดˆ์— 150๋ฒˆ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๋‚ด๋”๋ผ๋„, ๋ธŒ๋ผ์šฐ์ €๋Š” ์ด ๋ฐ์ดํ„ฐ๋ฅผ 1์ดˆ์— 60๋ฒˆ ๋งŒ ํ™•์ธํ•˜์—ฌ ๋ Œ๋”๋ง ํ•  ์ˆ˜ ์žˆ๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.
    • ๋ธŒ๋ผ์šฐ์ €๋Š” 1/60์ดˆ๋งˆ๋‹ค ๋งˆ์šฐ์Šค๊ฐ€ ๋ณด๋‚ธ ๋ฐ์ดํ„ฐ ์ค‘ ๊ฐ€์žฅ ์ตœ์‹  ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ๊ฐ€๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
    • ์ด๋ ‡๊ฒŒ ๊ฐ€์ ธ์˜ค๊ฒŒ ๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์ƒ˜ํ”Œ๋ง๋œ ๋ฐ์ดํ„ฐ๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค.
    • ๋งˆ์šฐ์Šค ์ด๋ฒคํŠธ๋Š” ํ™”๋ฉด ๊ฐฑ์‹  ์ฃผ๊ธฐ์™€๋Š” ๋…๋ฆฝ์ ์œผ๋กœ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. (๋” ๋งŽ์„ ์ˆ˜๋„ ์žˆ์Œ.)

๋ฌธ์ œ ํ•ด๊ฒฐ1. ์•„๋ž˜ ๊ฒŒ์‹œ๊ธ€ ์ฐธ๊ณ ํ•˜์—ฌ ์•ˆํ‹ฐ ์•จ๋ฆฌ์–ด์‹ฑ ์ ์šฉํ•ด๋ณด๊ธฐ [์‹คํŒจ]

canvas.width = canvas.clientWidth * window.devicePixelRatio;
canvas.height = canvas.clientHeight * window.devicePixelRatio;
ctx.imageSmoothingEnabled = true;

...
...
    
    
const convertCoordinate = ({ x, y }: Point): Point => {
  return { x: Math.floor(x * coordinateScale.current) + 0.5, y: Math.floor(y * coordinateScale.current) + 0.5 };
};

...
...

const ctx = canvas.getContext('2d', { willReadFrequently: true });
  • ํ”ํžˆ ์ถ”์ฒœํ•˜๋Š” ์•ˆํ‹ฐ ์•จ๋ฆฌ์–ด์‹ฑ ๋ฐฉ๋ฒ•์„ ์ ์šฉํ•ด๋ณด์•˜์ง€๋งŒ ํฐ ์ฐจ์ด๋Š” ์—†์—ˆ์Šต๋‹ˆ๋‹ค.
  • ๋ฌด์—‡๋ณด๋‹ค, ์œ„์—์„œ ์ •๋ฆฌํ•œ ๊ฒƒ ์ฒ˜๋Ÿผ ์„ ์ด ๊ฐ์ง€๋Š” ์ด์Šˆ๋Š” ์•ˆํ‹ฐ ์•จ๋ฆฌ์–ด์‹ฑ๊ณผ๋Š” ๊ด€๋ จ์ด ์—†์Šต๋‹ˆ๋‹ค.

๋ฌธ์ œ ํ•ด๊ฒฐ2. requestAnimationFrame์œผ๋กœ ์บ”๋ฒ„์Šค ์ตœ์ ํ™” ํ•ด๋ณด๊ธฐ [์‹คํŒจ]

  useEffect(() => {
    let lastTime: DOMHighResTimeStamp = 0;

    const drawAnimation = (timestemp: DOMHighResTimeStamp) => {
      if (timestemp - lastTime > 16) {
        if (currentDrawingRef.current) drawData(currentDrawingRef.current);
        lastTime = timestemp;
      }

      requestAnimationFrame(drawAnimation);
    };
    const aniId = requestAnimationFrame(drawAnimation);

    return () => {
      if (aniId) cancelAnimationFrame(aniId);
    };
  }, []);
  • requestAnimationFrame ์ธ์ž๋กœ ๋„˜์–ด์˜ค๋Š” timestamp ๊ฐ’์„ ์ด์šฉํ•ด 16ms ๊ฐ„๊ฒฉ์œผ๋กœ ๊ทธ๋ฆฌ๊ธฐ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•ด๋ณด์•˜์Šต๋‹ˆ๋‹ค.
  • ํ•˜์ง€๋งŒ requestAnimationFrame์˜ timestamp๋Š” ๋ธŒ๋ผ์šฐ์ € ์„ฑ๋Šฅ์— ๋”ฐ๋ผ ๊ฐ„๊ฒฉ์ด ๋‹ฌ๋ผ์งˆ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์—, performance.now() ๋ฅผ ์“ฐ๋Š” ๊ฒƒ์ด ์ •ํ™•ํ–ˆ์Šต๋‹ˆ๋‹ค.
  • ๊ฒฐ๊ณผ์ ์œผ๋กœ ์œ„ ์‹œ๋„๋กœ ํฌ๊ฒŒ ๋‹ฌ๋ผ์ง€๋Š” ์ ์€ ์—†์—ˆ๊ณ , ์„ ์ด ๊ตด๊ณก์ง€๋Š” ์ด์Šˆ๋Š” ์„ ๊ณผ ์„  ์‚ฌ์ด์— ๊ฐ์ด ์ง€๋Š” ํ˜„์ƒ์œผ๋กœ ๋ Œ๋”๋ง ์„ฑ๋Šฅ๊ณผ๋Š” ํฌ๊ฒŒ ์ƒ๊ด€์ด ์—†์—ˆ์Šต๋‹ˆ๋‹ค.

๋ฌธ์ œ ํ•ด๊ฒฐ3. ๋ณด๊ฐ„๋ฒ•์œผ๋กœ ๊ณก์„  ๊ทธ๋ฆฌ๊ธฐ [์„ฑ๊ณตโ†’๋ฏธ์ ์šฉ]

  • ์œ„ ์ด๋ฏธ์ง€์—์„œ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋“ฏ์ด, ๋งˆ์šฐ์Šค์—์„œ ์ƒ˜ํ”Œ๋ง๋œ ํฌ์ธํŠธ ๊ธฐ์ค€์œผ๋กœ ๊ตด๊ณก์ด ์ ธ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์ฆ‰, ํ•ด๋‹น ๋ฌธ์ œ๋Š” ์ ์ด ์ฐํžˆ๋Š” ๊ฐ„๊ฒฉ(Event Sampling Rate)์„ ์กฐ์ ˆํ•˜๊ฑฐ๋‚˜, ๋ณด๊ฐ„(Interpolation) ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜์—ฌ ํ•ด๊ฒฐํ•ด์•ผ ํ•œ๋‹ค๋Š” ๊ฒƒ์„ ๊นจ๋‹ฌ์•˜์Šต๋‹ˆ๋‹ค.

์Šคํ”Œ๋ผ์ธ ์ดํ•ด

  • ์Šคํ”Œ๋ผ์ธ์ด๋ž€ ๊ธฐ์ค€์ ๊ณผ ์ œ์–ด์ ์„ ํ†ตํ•ด ๊ทธ๋ ค์ง€๋Š” ๊ณก์„ ์„ ๋œปํ•ฉ๋‹ˆ๋‹ค. ์Šคํ”Œ๋ผ์ธ ๊ทธ๋ฆฌ๊ธฐ๋Š” ์ขŒํ‘œ ๋ผ์ธ์˜ ํ•จ์ˆ˜ ๋ชจ์–‘์— ๋”ฐ๋ผ 1์ฐจ, 2์ฐจ, 3์ฐจ ๋“ฑ์œผ๋กœ ๋‚˜๋‰ฉ๋‹ˆ๋‹ค.
  • ๋ถ€๋“œ๋Ÿฌ์šด ๊ณก์„ ์„ ๊ณ„์‚ฐํ•˜๋Š” ๊ฒƒ์€ ๊ฒฐ๊ตญ ์ฐํžŒ ์ ๋“ค์ด ์–ด๋–ค ๋ฐฉ์ •์‹ ๊ทธ๋ž˜ํ”„ ์œ„์— ์žˆ๋Š” ์ง€๋ฅผ ๊ณ„์‚ฐํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค. ๊ทธ๋ ‡๊ธฐ ๋•Œ๋ฌธ์— 1์ฐจ ์Šคํ”Œ๋ผ์ธ์€ ๋‘ ๊ฐœ์˜ ์ ๋งŒ ์žˆ์œผ๋ฉด ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๊ณ , 2์ฐจ ์Šคํ”Œ๋ผ์ธ์€ ์ตœ์†Œ 3๊ฐœ์˜ ์ ์ด ํ•„์š”ํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค. 3์ฐจ ์Šคํ”Œ๋ผ์ธ์€ 4๊ฐœ์˜ ์ ์ด ์žˆ์–ด์•ผ ๊ทธ๋ž˜ํ”„ ๋ฐฉ์ •์‹์„ ๊ตฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์œ„์˜ ๋ฐฉ์‹์œผ๋กœ ๊ธฐ์ค€์ ๋“ค์ด ์ด์–ด์ง€๋Š” ๋ฐฉ์ ์‹์„ ๊ตฌํ•œ ํ›„, ์Šคํ”Œ๋ผ์ธ์˜ ๋ชจ์–‘์„ ํ™•์ •ํ•˜๊ธฐ ์œ„ํ•ด ํ•„์š”ํ•œ ๊ฒƒ์ด ์ œ์–ด์ ์ž…๋‹ˆ๋‹ค.
    • ์ œ์–ด์ ์€ ๊ณก์„  ๋ฐฉ์ ์‹์„ ๋ฏธ๋ถ„ํ•˜์—ฌ 1์ฐจ ๋„ํ•จ์ˆ˜, 2์ฐจ ๋„ํ•จ์ˆ˜๋ฅผ ํ™œ์šฉํ•ด ๊ณ„์‚ฐํ•ฉ๋‹ˆ๋‹ค. 1์ฐจ ๋„ํ•จ์ˆ˜๋Š” x์ขŒํ‘œ์— ๋”ฐ๋ฅธ ๊ธฐ์šธ๊ธฐ๋ฅผ, **2์ฐจ ๋„ํ•จ์ˆ˜๋Š” ํŠน์ • ์ ์—์„œ์˜ ๊ธฐ์šธ๊ธฐ ๋ณ€ํ™”๋Ÿ‰(๊ณก๋ฅ )**์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.
    • ์ฐจ์ˆ˜๊ฐ€ ๋†’์•„์งˆ ์ˆ˜๋ก ๋„ํ•จ์ˆ˜๋“ค์˜ ๊ตฌ์กฐ๋„ ๋” ๋ณต์žกํ•ด์ง€๊ณ , ๋‹ค์–‘ํ•œ ๊ณก๋ฅ ์„ ๊ตฌํ˜„ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
  • Context2D ์˜ lineTo๊ฐ€ 1์ฐจ ์Šคํ”Œ๋ผ์ธ์„ ๊ทธ๋ฆด ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๋ฉ”์†Œ๋“œ์ž…๋‹ˆ๋‹ค.
  • quadraticCurveTo์™€ bezierCurvetTo ๋ฉ”์†Œ๋“œ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๊ฐ๊ฐ 2์ฐจ ์Šคํ”Œ๋ผ์ธ, 3์ฐจ ์Šคํ”Œ๋ผ์ธ์„ ๊ทธ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

quadraticCurveTo ๋ฉ”์†Œ๋“œ๋กœ ๋ณด๊ฐ„ํ•˜๊ธฐ

  • canvas api์—์„œ quadratic ์Šคํ”Œ๋ผ์ธ์„ ๊ทธ๋ ค์ฃผ๋Š” ๋ฉ”์†Œ๋“œ์ž…๋‹ˆ๋‹ค.
  • quadraticCurveTo(cp1x, cp1y, x, y)
  • cp๋Š” ์ œ์–ด์ , x ๋ฐ y๋Š” ๋งˆ์ง€๋ง‰์— ์ฐํž ์ ์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค. quadratic ์Šคํ”Œ๋ผ์ธ์€ ์ตœ์†Œ 3๊ฐœ์˜ ์ ์ด ์žˆ์–ด์•ผ ๊ณก์„ ์„ ๊ทธ๋ฆด ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ, ์  3๊ฐœ์ผ ๋•Œ ๋ถ€ํ„ฐ ๊ณก์„ ์„ ๊ทธ๋ฆด ์ˆ˜ ์žˆ๋„๋ก ํ•ด์•ผํ•ฉ๋‹ˆ๋‹ค.
  • ์ฒซ ๋ฒˆ์งธ ์ ๊ณผ ๋งˆ์ง€๋ง‰ ์  ์‚ฌ์ด์˜ ์ ๋“ค์„ ์ œ์–ด์  ์œผ๋กœ ์‚ผ์•„ ๊ณก์„ ์„ ๊ทธ๋ ค๋‚˜๊ฐ€๋Š” ๋ฐฉ์‹์„ ์‚ฌ์šฉํ–ˆ์Šต๋‹ˆ๋‹ค.
  const drawSmoothLine = (drawingData: DrawingData, canvasRef: RefObject<HTMLCanvasElement>) => {
    const { ctx } = getCanvasContext(canvasRef);
    if (!currentDrawingRef.current) return;
    const { points } = drawingData;

    if (currentDrawingRef.current.points.length < 2) return;

    ctx.beginPath();
    ctx.moveTo(points[0].x, points[0].y);

    for (let i = 1; i < points.length - 1; i++) {
      const midPoint = {
        x: (points[i].x + points[i + 1].x) / 2,
        y: (points[i].y + points[i + 1].y) / 2,
      };

      ctx.quadraticCurveTo(points[i].x, points[i].y, midPoint.x, midPoint.y);
    }

    // ๋งˆ์ง€๋ง‰ ์  ์—ฐ๊ฒฐ

    ctx.stroke();
  };

๊ฒฐ๋ก 

  • ํ•˜์ง€๋งŒ ์œ„ ๋ฐฉ์‹์€ ์ง€์—ฐ์ด ๋งŽ์ด ๋ฐœ์ƒํ•ด ์ถ”ํ›„ ๋„์ „ ๊ณผ์ œ๋กœ ๋‚จ๊ฒจ๋‘๊ฒŒ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

์ฐธ๊ณ  ์ž๋ฃŒ

๐Ÿ˜Ž ์›จ๋ฒ ๋ฒ ๋ฒ ๋ฒฑ

๐Ÿ‘ฎ๐Ÿป ํŒ€ ๊ทœ์น™

๐Ÿ’ป ํ”„๋กœ์ ํŠธ

๐Ÿชต ์›จ๋ฒ ๋ฒฑ ๊ธฐ์ˆ ๋กœ๊ทธ

๐Ÿช„ ๋ฐ๋ชจ ๊ณต์œ 

๐Ÿ”„ ์Šคํ”„๋ฆฐํŠธ ๊ธฐ๋ก

๐Ÿ“— ํšŒ์˜๋ก

Clone this wiki locally