Skip to content

๐Ÿชต 4. Canvas ๊ธฐ๋ณธ ๊ธฐ๋Šฅ ๊ตฌํ˜„ ์ฃผ์š” ์ด์Šˆ

D.Joung edited this page Dec 5, 2024 · 2 revisions

Canvas ํ„ฐ์น˜ ๊ทธ๋ฆฌ๊ธฐ ๊ตฌํ˜„

  • ๊ด€๋ จ ์ด์Šˆ : #16
  • ์บ”๋ฒ„์Šค ํ„ฐ์น˜ ์‹œ ๋ ˆ์ด์•„์›ƒ ์Šคํฌ๋กค๋ง์ด ํ™œ์„ฑํ™” ๋˜๋Š” ์ด์Šˆ์™€, MouseEvent์™€ TouchEvent๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์ขŒํ‘œ ์†์„ฑ์ด ๋‹ค๋ฅธ ์ด์Šˆ๋กœ ๋“œ๋กœ์ž‰ ๊ตฌํ˜„์— ์ถ”๊ฐ€ ํ•™์Šต์ด ํ•„์š”ํ–ˆ์Šต๋‹ˆ๋‹ค.

์บ”๋ฒ„์Šค ํ„ฐ์น˜ ์‹œ ๋ ˆ์ด์•„์›ƒ ์Šคํฌ๋กค๋ง ํ™œ์„ฑํ™”

  • ํ„ฐ์น˜๋กœ ๊ทธ๋ฆฌ๊ธฐ ์œ„ํ•ด ์บ”๋ฒ„์Šค๋ฅผ ์Šฌ๋ผ์ด๋“œ ํ–ˆ์„ ๋•Œ, ํ™”๋ฉด ์ „์ฒด๊ฐ€ ์ด๋™ํ•˜๋Š” ์ด์Šˆ๊ฐ€ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

ํ•ด๊ฒฐ๋ฒ•

  1. css ์†์„ฑ์— touch-Event : none ์ถ”๊ฐ€ํ•ฉ๋‹ˆ๋‹ค.
  2. touch ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ์— e.preventDefault() ์ถ”๊ฐ€ํ•˜์—ฌ ์Šคํฌ๋กค๋ง ๋ฐฉ์ง€ํ•ฉ๋‹ˆ๋‹ค.

๊ณผ์ •

  • ์ฒ˜์Œ์—๋Š” 2๋ฒˆ ๋ฐฉ๋ฒ•์œผ๋กœ ํ•ด๊ฒฐํ•˜๋ ค ํ–ˆ์ง€๋งŒ, e.preventDefault()๋Š” ํ•ด๋‹น ์ด๋ฒคํŠธ์˜ ๋ชจ๋“  ๋™์ž‘์„ ๋ง‰์•„๋ฒ„๋ฆฌ๊ฒ ๋‹ค๋Š” ์˜๋ฏธ๋กœ ์•Œ๊ณ  ์žˆ์—ˆ๊ธฐ์— ๋ฌด์–ธ๊ฐ€ ์ฐ์ฐํ–ˆ์Šต๋‹ˆ๋‹ค.

  • ๋”ํ•ด์„œ ์ง์ ‘ ์ ์šฉํ•ด๋ณด๋‹ˆ ์•„๋ž˜์™€ ๊ฐ™์€ ๊ฒฝ๊ณ ๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค.

    Canvas.tsx:113 Unable to preventDefault inside passive event listener invocation.

  • ํŒจ์‹œ๋ธŒ ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ ํ˜ธ์ถœ ๋‚ด์—์„œ default๋ฅผ ๋ฐฉ์ง€ํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋ผ๋Š” ์˜๋ฏธ์˜ ๊ฒฝ๊ณ ์˜€์Šต๋‹ˆ๋‹ค. ๋ธŒ๋ผ์šฐ์ €๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์Šคํฌ๋กค ์„ฑ๋Šฅ์„ ์ตœ์ ํ™”ํ•˜๊ธฐ ์œ„ํ•ด touchstart์™€ touchmove ์ด๋ฒคํŠธ๋ฅผ ํŒจ์‹œ๋ธŒ(passive) ์ด๋ฒคํŠธ ๋ฆฌ์Šค๋„ˆ๋กœ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค. ํŒจ์‹œ๋ธŒ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ ๋ž€ ์„ฑ๋Šฅ ํ–ฅ์ƒ์„ ์œ„ํ•ด ํ•ธ๋“ค๋Ÿฌ ๋‚ด์—์„œ e.preventDefault() ๋ฅผ ํ˜ธ์ถœํ•˜์ง€ ์•Š์„ ๊ฒƒ์„ ๋ธŒ๋ผ์šฐ์ € ์ฐจ์›์—์„œ ๋ณด์žฅํ•˜๋Š” ํ•ธ๋“ค๋Ÿฌ์ž…๋‹ˆ๋‹ค.

  • touchmove ์˜ ๊ฒฝ์šฐ ์ฃผ๋กœ ๋ชจ๋ฐ”์ผ ํ™˜๊ฒฝ์—์„œ ์“ฐ์ด๊ณ , ๋ชจ๋ฐ”์ผ ํ™˜๊ฒฝ์—์„œ๋Š” ์Šคํฌ๋กค ๊ธฐ๋Šฅ์ด ๋งค์šฐ ์ค‘์š”ํ•˜๊ธฐ ๋•Œ๋ฌธ์—, ์Šคํฌ๋กค ์ด๋ฒคํŠธ ๋ฐœ์ƒ ์‹œ ๋งˆ๋‹ค e.preventDefault() ๊ฐ€ ํ˜ธ์ถœ๋˜์—ˆ๋Š” ์ง€ ํ™•์ธํ•˜๋Š” ์ ˆ์ฐจ๋ฅผ ์ƒ๋žตํ•˜๊ธฐ ์œ„ํ•ด ํŒจ์‹œ๋ธŒ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋กœ ์„ธํŒ…๋˜์—ˆ๋‹ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

  • ์œ„ ์„ค์ •์„ off ํ•˜๊ณ  touch์ด๋ฒคํŠธ์—์„œ ๋‹ค์‹œ e.preventDefault()๋ฅผ ํ˜ธ์ถœ ๊ฐ€๋Šฅํ•˜๊ฒŒ ํ•˜๋ ค๋ฉด, ์•„๋ž˜ ์ฒ˜๋Ÿผ ์ด๋ฒคํŠธ ํ•ธ๋“ค๋Ÿฌ๋ฅผ ๋ถ™์ด๋ฉฐ ์ธ์ž๋กœ ์„ค์ • ๊ฐ์ฒด๋ฅผ ํ•˜๋‚˜ ๋” ๋ณด๋‚ด์ฃผ๋ฉด ๋ฉ๋‹ˆ๋‹ค.

      window.addEventListener('touchmove', handleTouchMove, { passive: false });
  • ํ•˜์ง€๋งŒ ๊ตณ์ด ๋ธŒ๋ผ์šฐ์ € ์ฐจ์›์—์„œ ์ตœ์ ํ™”๋ฅผ ์œ„ํ•ด ๋ง‰์•„๋†“์€ ์„ค์ •์„ ํ•ด์ œํ•˜๊ณ  ์‹ถ์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. css ์†์„ฑ์œผ๋กœ ๊ฐ„ํŽธํ•˜๊ฒŒ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ 1๋ฒˆ ๋ฐฉ๋ฒ•์„ ์‚ฌ์šฉํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ–ˆ์Šต๋‹ˆ๋‹ค.

    touch-Event : none ์— ๋Œ€ํ•œ ์กฐ์‚ฌ๋Š” chat-gpt์—๊ฒŒ๋งŒ ๋ฌผ์–ด๋ณด๊ณ  ๋งˆ๋ฌด๋ฆฌํ•˜์˜€์Šต๋‹ˆ๋‹ค.

    • touch-Event : none ์›๋ฆฌ ์•Œ๋ ค์ค˜
      • ๊ธฐ๋ณธ ํ„ฐ์น˜ ๋™์ž‘ ์ฐจ๋‹จ: touch-action: none์„ ์„ค์ •ํ•œ ์š”์†Œ์—์„œ๋Š” ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์Šคํฌ๋กค, ํ™•๋Œ€/์ถ•์†Œ์™€ ๊ฐ™์€ ํ„ฐ์น˜ ๊ธฐ๋ฐ˜์˜ ๊ธฐ๋ณธ ๋™์ž‘์„ ์‹คํ–‰ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ด ์†์„ฑ์„ ์ ์šฉํ•˜๋ฉด ํ„ฐ์น˜๊ฐ€ ๋ฐœ์ƒํ•ด๋„ ๋ธŒ๋ผ์šฐ์ €๋Š” ์ด ๋™์ž‘์„ ๋ฌด์‹œํ•˜๊ณ , ํ„ฐ์น˜ ์ด๋ฒคํŠธ๋งŒ JavaScript์—์„œ ์ˆ˜์‹ ํ•  ์ˆ˜ ์žˆ๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.
      • ๋ธŒ๋ผ์šฐ์ € ์ตœ์ ํ™”: ๋ณดํ†ต ๋ธŒ๋ผ์šฐ์ €๋Š” ํ„ฐ์น˜ ์ด๋ฒคํŠธ๊ฐ€ ๋ฐœ์ƒํ•  ๋•Œ ๊ธฐ๋ณธ ๋™์ž‘์„ ์ฒ˜๋ฆฌํ•˜๊ธฐ ์œ„ํ•ด ์•ฝ๊ฐ„์˜ ์‹œ๊ฐ„์„ ๊ธฐ๋‹ค๋ฆฝ๋‹ˆ๋‹ค. touch-action: none์ด ์„ค์ •๋˜์–ด ์žˆ์œผ๋ฉด, ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ๊ธฐ๋ณธ ๋™์ž‘์„ ๋ฏธ๋ฆฌ ์ฐจ๋‹จํ•˜๊ณ  ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์— ๋” ๋น ๋ฅด๊ฒŒ ๋ฐ˜์‘ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
      • JavaScript๋กœ๋งŒ ํ„ฐ์น˜ ๋™์ž‘ ์ œ์–ด: ์ด ์†์„ฑ์„ ์„ค์ •ํ•œ ์š”์†Œ์—์„œ๋Š” ํ„ฐ์น˜ ๋™์ž‘์„ ์™„์ „ํžˆ JavaScript๋กœ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. touchmove, touchstart, touchend ์ด๋ฒคํŠธ๋ฅผ ํ™œ์šฉํ•ด ํ„ฐ์น˜ ์ด๋ฒคํŠธ๋ฅผ ์›ํ•˜๋Š” ๋Œ€๋กœ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

MouseEvent์™€ TouchEvent๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์ขŒํ‘œ ์†์„ฑ์ด ๋‹ค๋ฆ„

  • React๋Š” nativeEvent ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด ์บ”๋ฒ„์Šค ์ขŒํ‘œ๋ฅผ ๊ธฐ์ค€์œผ๋กœ ํ•œ x์ขŒํ‘œ์™€ y์ขŒํ‘œ๋ฅผ ๋ฐ”๋กœ ๋ณด๋‚ด์ค๋‹ˆ๋‹ค. nativeEvent๋Š” React์˜ SyntheticEvent ๊ฐ์ฒด์—์„œ ๋ธŒ๋ผ์šฐ์ € ์ด๋ฒคํŠธ ๊ฐ์ฒด ์†์„ฑ์— ์ง์ ‘ ์ ‘๊ทผํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ํ„ฐ์น˜ ์ด๋ฒคํŠธ์—๋Š” ์ด๋Ÿฐ ์†์„ฑ์ด ์—†์–ด, ํ„ฐ์น˜ ์‹œ์—๋„ ๊ทธ๋ฆฌ๊ธฐ๋ฅผ ๊ตฌํ˜„ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋Š” ์บ”๋ฒ„์Šค ์œ„์˜ ํ„ฐ์น˜ ์ง€์ ์„ ์ง์ ‘ ๊ณ„์‚ฐํ•ด์ค˜์•ผ ํ–ˆ์Šต๋‹ˆ๋‹ค.

  • MouseDown์˜ ๊ฒฝ์šฐ ํ„ฐ์น˜ ํ–ˆ์„ ๋•Œ๋„ ์ž‘๋™ํ•˜๋Š”๋ฐ, ์ด๋Š” ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ํ„ฐ์น˜ ์Šคํฌ๋ฆฐ ์žฅ์น˜์—์„œ ํ„ฐ์น˜๋ฅผ ๋งˆ์šฐ์Šค ์ž…๋ ฅ์œผ๋กœ ์—๋ฎฌ๋ ˆ์ด์…˜ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค. touchDown, touchEnd๋Š” ๋ฐœ์ƒ ์‹œ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ํ„ฐ์น˜ ์ž…๋ ฅ์„ ๋งˆ์šฐ์Šค ์ž…๋ ฅ์œผ๋กœ ์•Œ์•„์„œ ์ „ํ™˜ํ•ด์ค€๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ถ”ํ›„ ๊ณผ์ œ

  • SyntheticEvent, nativeEvent, TouchEvent, MouseEvent ๊ฐ„์˜ ๊ด€๊ณ„๋ฅผ ๋” ๊นŠ์ˆ™ํ•˜๊ฒŒ ์Šคํ„ฐ๋””ํ•  ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.
  • mouseEvent, touchEvent, window, html ํƒœ๊ทธ๊ฐ€ ์ œ๊ณตํ•˜๋Š” ์ขŒํ‘œ ์†์„ฑ๋“ค๋„ ์ •ํ™•ํžˆ ์ •๋ฆฌํ•ด ์ดํ•ดํ•  ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค. (offsetX, clientX, getBound, getBoundingClientRect(), ๊ทธ ์™ธ ๊ธฐํƒ€ ๋“ฑ๋“ฑ..)
  • ์•„๋ž˜๋Š” touchEvent์™€ clickEvent ๊ฐ„์˜ ๊ด€๊ณ„์— ๋Œ€ํ•œ ๊ธ€์ž…๋‹ˆ๋‹ค.

ํ˜„์žฌ ๊ตฌํ˜„ ์ƒํƒœ

  • MouseEvent์™€ TouchEvent ๋‘˜ ๋‹ค nativeEvent ๊ฐ์ฒด๋ฅผ ํ†ตํ•ด ์ขŒํ‘œ๋ฅผ ๋ฐ›์•„์˜ค๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

    • click : nativeEvent.offsetX, offsetY ์‚ฌ์šฉ
    • touch : nativeEvent.touches ์‚ฌ์šฉ
  • touchEvent์˜ ๊ฒฝ์šฐ ์•„๋ž˜ ๋ผ์ธ์ฒ˜๋Ÿผ ํ„ฐ์น˜ ์ขŒํ‘œ๋ฅผ ๋ฐฐ์—ด๋กœ ๋ฐ›์•„์˜ค๊ฒŒ ๋˜๋Š”๋ฐ, ์ด๋Š” ํ„ฐ์น˜ ํ™”๋ฉด์˜ ๊ฒฝ์šฐ ํ„ฐ์น˜์ ์ด 2๊ฐœ ์ด์ƒ ์ƒ๊ธธ ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

    const { clientX, clientY } = e.nativeEvent.touches[0]; //๋ทฐํฌํŠธ ๊ธฐ์ค€ ์ƒ๋Œ€ ์ขŒํ‘œ

์ฐธ๊ณ  ์ž๋ฃŒ

์บ”๋ฒ„์Šค ํŽ˜์ธํŠธ ํˆด ๊ธฐ๋Šฅ ๊ตฌํ˜„

  • ๊ด€๋ จ ์ด์Šˆ : #27

๊ตฌํ˜„ ๊ณ„ํš

  • getImageData ๋ฉ”์†Œ๋“œ๋ฅผ ํ†ตํ•ด Canvas์˜ ์ด๋ฏธ์ง€ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ฌ ์ˆ˜ ์žˆ์Œ์„ ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค.
  • ๋”ฐ๋ผ์„œ bfs ์•Œ๊ณ ๋ฆฌ์ฆ˜์œผ๋กœ ๊ตฌํ˜„ํ•˜๊ธฐ๋กœ ๊ณ„ํšํ–ˆ๋Š”๋ฐ, ๊ฒ€์ƒ‰ํ•ด๋ณด๋‹ˆ Flood Fill ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด๋ผ๊ณ  ์ด๋ฏธ ๊ฑฐ์˜ ์ •์„์œผ๋กœ ๊ตณ์–ด์ง„ ๋ฐฉ๋ฒ•์ด ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.
    • Flood Fill : ์ฃผ์–ด์ง„ ์‹œ์ž‘์ ์œผ๋กœ๋ถ€ํ„ฐ ์—ฐ๊ฒฐ๋œ ์˜์—ญ๋“ค์„ ์ฐพ์•„๋‚ด๋Š”ย ์•Œ๊ณ ๋ฆฌ์ฆ˜์ž…๋‹ˆ๋‹ค. bfs๋ฅผ ํ™œ์šฉํ•˜๋Š” ๋ฐฉ์‹์ด ๊ฐ€์žฅ ๋ณดํŽธ์ ์ธ ๋“ฏ ํ–ˆ๊ณ , ๋”ฐ๋ผ์„œ ๊ธฐ์กด ๊ณ„ํš๋Œ€๋กœ bfs๋กœ ๊ตฌํ˜„ํ•˜๊ธฐ๋กœ ๊ฒฐ์ •ํ•˜์˜€์Šต๋‹ˆ๋‹ค.

์‹œํ–‰ ์ฐฉ์˜ค

  • ์ดˆ๋ฐ˜ ๊ณ„ํš์€ ์•„๋ž˜์™€ ๊ฐ™์•˜์Šต๋‹ˆ๋‹ค.

    • getImageData๋Š” Canvas ์ด๋ฏธ์ง€ ๋ฐ์ดํ„ฐ๋ฅผ rgba ๊ฐ’์ด ๋‚˜๋ž€ํžˆ ๋‹ด๊ธด 1์ฐจ์› ๋ฐฐ์—ด๋กœ ๊ฐ€์ ธ์˜ต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ bfs ํ•จ์ˆ˜ ๋‚ด์—์„œ์˜ ์ด๋™ ์ขŒํ‘œ ๋‹จ์œ„๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

      • x์ถ• ์˜ค๋ฅธ์ชฝ์œผ๋กœ 1 pixel ์ด๋™ : + 4
      • y์ถ• ์•„๋ž˜๋กœ 1 pixel ์ด๋™ : + (canvas.width * ์ขŒํ‘œy + ์ขŒํ‘œx) * 4
      • 4๋ฅผ ๋”ํ•˜๊ณ  ๊ณฑํ•ด์ฃผ๋Š” ์ด์œ ๋Š” rgba ๊ฐ’์ด ์ด 4๊ฐœ์ด๊ธฐ ๋•Œ๋ฌธ.
    • ์‚ฌ์šฉ์ž๊ฐ€ ํด๋ฆญํ•œ ์œ„์น˜ ๋˜ํ•œ (canvas.width * ์ขŒํ‘œy + ์ขŒํ‘œx) * 4 ๊ณต์‹์œผ๋กœ ๊ตฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ํ•ด๋‹น ๋ฐฉ๋ฒ•์œผ๋กœ ์บ”๋ฒ„์Šค์˜ ์–ด๋Š ์ง€์ ์„ ์„ ํƒํ–ˆ๋Š” ์ง€๋ฅผ ๋จผ์ € ๊ตฌํ•œ ํ›„, ์œ„ ๋‹จ์œ„๋ฅผ ๋”ํ•˜๊ณ  ๋นผ๋ฉด์„œ ํ”ฝ์…€์นธ์„ ํƒ์ƒ‰ํ•ด๊ฐ€๋Š” ๋ฐฉ์‹์œผ๋กœ ๊ตฌํ˜„ํ•ด๋ณด์•˜์Šต๋‹ˆ๋‹ค.

      const movement = [4, CANVAS_SIZE * 4, -4, -CANVAS_SIZE * 4];
      
      while (searchArray.length > 0) {
        const currentIndex = searchArray.shift();
        //searchArray์˜ ๊ธธ์ด๊ฐ€ ์†Œ์ง„๋  ๋•Œ ๊นŒ์ง€ ๋ฐ˜๋ณต
        
        for (const move of movement) {
          const nextIndex = convert(currentIndex! + move);
          //convert๋Š” ์†Œ์ˆ˜๋ฅผ ์ •์ˆ˜๋กœ ์žก์•„์ฃผ๋Š” ํ•จ์ˆ˜
      
          if (
            nextIndex >= 0 &&
            nextIndex < pixelArray.length &&
            !checkArray[nextIndex] &&
            checkColorisEqual(nextIndex, beforeColor, pixelArray)
      	    //index๊ฐ€ pixelArray ๊ธธ์ด ์•ˆ์— ์žˆ์œผ๋ฉด์„œ ๋ฐฉ๋ฌธํ•œ ์  ์—†๋Š” index์ด๊ณ ,
      	    //ํด๋ฆญํ•œ ํ”ฝ์…€์˜ ์ƒ‰์ด index ์œ„์น˜์˜ ์ƒ‰๊ณผ ๊ฐ™์œผ๋ฉด ์ƒ‰์น  ํ›„ searchArray์— ๋‹ด๋Š”๋‹ค.
          ) {
            checkArray[nextIndex] = true;
            fillTargetColor(nextIndex, targetColor, pixelArray);
            searchArray.push(nextIndex);
          }
        }
      }
  • ํ•˜์ง€๋งŒ ์œ„ ์ฒ˜๋Ÿผ ๊ตฌํ˜„ํ•˜๊ณ  ๋‚˜๋‹ˆ ์›์ธ์„ ํŒŒ์•…ํ•˜๊ธฐ ํž˜๋“  ๋ฌดํ•œ ๋ฃจํ”„๊ฐ€ ๋ฐœ์ƒํ–ˆ์Šต๋‹ˆ๋‹ค. ์ง์ž‘ํ•  ์ˆ˜ ์žˆ๋Š” ์›์ธ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์•˜์Šต๋‹ˆ๋‹ค.

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

    const movement = [[1, 0],[0, -1],[-1, 0],[0, 1]];
  
  while (searchArray.length > 0) {
    const [currentX, currentY] = searchArray.shift()!;
    for (const move of movement) {
      const [nextX, nextY] = [currentX + move[0], currentY + move[1]];
      if (
        nextX >= 0 &&
        nextX < CANVAS_SIZE_WIDTH &&
        nextY >= 0 &&
        nextY < CANVAS_SIZE_HEIGHT &&
        !checkArray[nextY][nextX]
      ) {
        const nextArrayIndex = (nextY * CANVAS_SIZE_WIDTH + nextX) * 4;
        // 2์ฐจ์› ๋ฐฐ์—ด์˜ ๊ฐ€์ƒ ์ขŒํ‘œ๋ฅผ ์‹ค์ œ 1์ฐจ์› ๋ฐฐ์—ด์˜ ์ขŒํ‘œ๋กœ ๋ณ€ํ™˜
        if (checkColorisEqual(nextArrayIndex, clickColor, pixelArray)) {
          checkArray[nextY][nextX] = true;
          fillTargetColor(nextArrayIndex, targetColor, pixelArray);
          searchArray.push([nextX, nextY]);
        }
      }
    }
  }
  • ์ž˜ ์ž‘๋™ํ–ˆ์Šต๋‹ˆ๋‹ค.

๊ฐœ์„ ์‚ฌํ•ญ

  • ํŽ˜์ธํŠธ ํˆด ์‚ฌ์šฉ ์‹œ ์น ํ•ด์ง„ ์˜์—ญ ๊ฐ€์žฅ์ž๋ฆฌ์— ์ด์ „ ์ƒ‰์˜ ์ ์„  ํ”์ ์ด ๋‚จ์•˜์Šต๋‹ˆ๋‹ค. ๋‹ค๋งŒ ์ด ํ˜„์ƒ์„ ๊ฐˆํ‹ฑํฐ ๊ฐ™์€ ์„œ๋น„์Šค์—์„œ๋„ ๋ฐœ์ƒํ•˜๊ณ  ์žˆ์–ด, ์ผ๋‹จ ์ตœ์ ํ™” ๋‹จ๊ณ„์— ์‚ดํŽด๋ณด๋Š” ๊ฒƒ์œผ๋กœ ๋ฏธ๋ค˜์Šต๋‹ˆ๋‹ค.

  • ๋ธŒ๋ผ์šฐ์ € ์ฐฝ์—์„œ ์•„๋ž˜์™€ ๊ฐ™์€ ์•ˆ๋‚ด๋ฌธ์ด ๋ฐœ์ƒํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.

    Canvas2D: Multiple readback operations using getImageData are faster with the willReadFrequently attribute set to true. See: https://html.spec.whatwg.org/multipage/canvas.html#concept-canvas-will-read-frequently
    • ์ตœ์ ํ™”์— ๋Œ€ํ•œ ํŒ์„ ์•Œ๋ ค์ฃผ๊ณ  ์žˆ๋Š” ๊ฒƒ์ธ๋ฐ, getImageData ๋ฉ”์†Œ๋“œ๋ฅผ ์ž์ฃผ ์‚ฌ์šฉํ•˜๋Š” ํ™˜๊ฒฝ์ผ ๊ฒฝ์šฐ, Canvas2D ์ธ์Šคํ„ด์Šค๋ฅผ willReadFrequently ์˜ต์…˜๊ณผ ํ•จ๊ป˜ ๊ฐ€์ ธ์™€์ฃผ๋ฉด ์„ฑ๋Šฅ์ด ํ–ฅ์ƒ๋  ์ˆ˜ ์žˆ๋‹ค๋Š” ๋‚ด์šฉ์ž…๋‹ˆ๋‹ค.
    • ๋ธŒ๋ผ์šฐ์ €๋Š” canvas๊ฐ™์€ ๊ทธ๋ž˜ํ”ฝ ์š”์†Œ๋ฅผ ๊ธฐ๋ณธ์ ์œผ๋กœ gpu๋ฅผ ์‚ฌ์šฉํ•ด ๋ Œ๋”๋งํ•ฉ๋‹ˆ๋‹ค. ํ•˜์ง€๋งŒ ์œ„ willReadFrequently ์˜ต์…˜์„ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ canvas๋Š” cpu๋ฅผ ์ ๊ทน์ ์œผ๋กœ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.
    • cpu๋ฅผ ์‚ฌ์šฉํ•  ๊ฒฝ์šฐ gpu๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์˜ฎ๊ธฐ๋Š” ๊ณผ์ •์ด ์ƒ๋žต๋˜์–ด ๋” ๋นจ๋ผ์งˆ ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด์Šต๋‹ˆ๋‹ค.
    • ๊ทธ๋ž˜์„œ getImageData, toDataURL, toBlob ๋ฉ”์†Œ๋“œ๊ฐ€ ๋นˆ๋ฒˆํ•  ๊ฒฝ์šฐ, ์œ„ ์„ค์ •์„ ํ†ตํ•ด cpu ์ฒ˜๋ฆฌ๋ฅผ ๊ถŒ์žฅํ•œ๋‹ค๋Š” ๋‚ด์šฉ์ด์—ˆ์Šต๋‹ˆ๋‹ค.
    • ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์•„๋ž˜ ๋ฌธ์„œ๋ฅผ ์ฐธ๊ณ ํ•ด๋ณด๋ฉด ์ข‹์„ ๊ฒƒ ๊ฐ™์Šต๋‹ˆ๋‹ค. ํŽ˜์ธํŒ… ๊ธฐ๋Šฅ์ด ๊ทธ๋ฆฌ ์ž์ฃผ ์“ฐ์ผ๊นŒ ์‹ถ์–ด ์ฝ”๋“œ์—๋Š” ๋”ฐ๋กœ ์ ์šฉํ•˜์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ์„ฑ๋Šฅ ๋ฌธ์ œ๊ฐ€ ์ƒ๊ธฐ๋ฉด ์‚ดํŽด๋ณผ ์˜ˆ์ •์ž…๋‹ˆ๋‹ค.

์ฐธ๊ณ  ์ž๋ฃŒ

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

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

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

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

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

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

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

Clone this wiki locally