Understanding JavaScript's requestAnimationFrame() method for smooth animations - Lee-hyuna/33-js-concepts-kr GitHub Wiki

๋ถ€๋“œ๋Ÿฌ์šด ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์œ„ํ•œ JavaScript์˜ requestAnimationFrame() ๋ฉ”์†Œ๋“œ์˜ ์ดํ•ด

์›๋ฌธ : http://www.javascriptkit.com/javatutors/requestanimationframe.shtml

์ž‘์„ฑ์ผ(์ˆ˜์ •์ผ) 2017-11-01

์šฉ์–ด ์‚ฌ์ „ ์ •์˜ (์˜->ํ•œ)


  • animation : ์• ๋‹ˆ๋งค์ด์…˜
  • interaction : ์ธํ„ฐ๋ ‰์…˜(์ƒํ˜ธ ์ž‘์šฉ)
  • logic : ๋กœ์ง
  • recursively : ์žฌ๊ท€์ ์œผ๋กœ
  • element : ์—˜๋ฆฌ๋จผํŠธ
  • delay : ์ง€์—ฐ ์‹œ๊ฐ„
  • function : ํ•จ์ˆ˜
  • frame : ํ”„๋ ˆ์ž„
  • resource : ์ž์›
  • reflow : ๋ฆฌํ”Œ๋กœ์šฐ
  • cardiac arrest : ์‹ฌ์ •์ง€๋ฅผ ๋œปํ•˜์—ฌ ๋ฉˆ์ถฐ์žˆ์Œ์œผ๋กœ ๋ฒˆ์—ญ
  • layout thrashing : ๋ ˆ์ด์•„์›ƒ ์Šค๋ ˆ์‹ฑ
  • janky page : ์ €์„ฑ๋Šฅ์˜ ํŽ˜์ด์ง€
  • method : ๋ฉ”์†Œ๋“œ
  • repaint : ๋ฆฌํŽ˜์ธํŠธ
  • vendor prefix : ๋ฒค๋” ํ”„๋ฆฌํ”ฝ์Šค
  • sync : ๋™๊ธฐํ™”
  • fallback : ํด๋ฐฑ
  • nemesis counterpart : ์˜์—ญ;์ฝœ๋ฐฑ ํ•จ์ˆ˜ ์‹คํ–‰์„ ์‹œ์ž‘ํ•  ๋•Œ์˜ ์‹œ์  ์ฐธ๊ณ  : MDN
  • callback : ์ฝœ๋ฐฑ
  • parameter : ๋งค๊ฐœ๋ณ€์ˆ˜
  • variable : ๋ณ€์ˆ˜
  • return : ๋ฐ˜ํ™˜

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

์™œ requestAnimationFrame()์„ ํ•„์š”๋กœ ํ• ๊นŒ์š”?

์šฐ์„  requestAnimationFrame()์— ๋Œ€ํ•œ ์•„์ด๋””์–ด์™€ ์™œ ๊ทธ ๋ฉ”์†Œ๋“œ๊ฐ€ ํ•„์š”ํ•œ์ง€ ์ด์•ผ๊ธฐํ•ด ๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. ์ „ํ†ต์ ์ธ ๋ฐฉ๋ฒ•์œผ๋กœ Javascript๋กœ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ๋งŒ๋“ค๋ ค๋ฉด ์žฌ๊ท€์ ์œผ๋กœ setTimeout()์„ ํ˜ธ์ถœํ•˜๊ฑฐ๋‚˜, ๋ฐ˜๋ณต์ ์œผ๋กœ setInterval()์„ ์‹คํ–‰ํ•˜๋„๋ก ํ•˜์—ฌ ์—˜๋ฆฌ๋จผํŠธ๋ฅผ ํ”„๋ ˆ์ž„ ๋‹จ์œ„๋กœ ๋ณ€๊ฒฝ(์˜ˆ: 50๋ฐ€๋ฆฌ์ดˆ๋งˆ๋‹ค ํ•œ ๋ฒˆ์”ฉ)ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

var adiv = document.getElementById('mydiv')
var leftpos = 0
setInterval(function(){
    leftpos += 5
    adiv.style.left = leftpos + 'px' // move div by 5 pixels each time
}, 50) // run code every 50 milliseconds

์œ„์˜ ์ฝ”๋“œ๊ฐ€ ๋กœ์ง์ ์œผ๋กœ๋Š” ๋งž์•„ ๋ณด์ด์ง€๋งŒ ์‹ค์ œ๋กœ ์‹คํ–‰ํ•ด๋ณด๋ฉด ์™„๋ฒฝํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ™”๋ฉด์—์„œ ๋ฌด์–ธ๊ฐ€๋ฅผ ๋ณ€๊ฒฝํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๊ธฐ ์œ„ํ•ด setTmeout/setInterval์„ ์‚ฌ์šฉํ•  ๋•Œ ๋‘๊ฐ€์ง€์˜ ๋ฌธ์ œ์ ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์ด ํ•จ์ˆ˜๋“ค์ด ๋‚ด๋ถ€์ ์œผ๋กœ ์ง€์—ฐ ์‹œ๊ฐ„(์˜ˆ, 50 milliseconds)์˜ ๊ฐ’์„ ์ฃผ์—ˆ์ง€๋งŒ ์‚ฌ์šฉ์ž ์‹œ์Šคํ…œ ์ž์›๋“ค์˜ ๋ณ€ํ™”๋กœ ์ธํ•ด ์ข…์ข… ์‹คํ–‰๋˜์ง€ ์•Š์•„ ์• ๋‹ˆ๋ฉ”์ด์…˜ ํ”„๋ ˆ์ž„ ๊ฐ„์— ์ผ๊ด€์„ฑ์—†๋Š” ์ง€์—ฐ ์‹œ๊ฐ„์˜ ๊ฐ„๊ฒฉ์ด ์ƒ๊น๋‹ˆ๋‹ค.

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

ํ•ด๊ฒฐ์ฑ… - requestAnimationFrame()

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

๋”ฐ๋ผ์„œ ์• ๋‹ˆ๋ฉ”์ด์…˜์˜ ๊ฒฝ์šฐ setTimeout/ setInterval ๋Œ€์‹  requestAnimationFrame()์„ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ ์ •ํ™•ํžˆ ์–ด๋–ป๊ฒŒ ํ•ด์•ผํ• ๊นŒ์š”? ๋จผ์ € ๋ธŒ๋ผ์šฐ์ € ์ง€์› ๋ฒ”์œ„์„ ์‚ดํŽด๋ณด์„ธ์š”. requestAnimationFrame()์€ ์˜ค๋Š˜๋‚  IE10 +, FF11 +, Chrome ๋ฐ Safari ๋“ฑ์˜ ์ตœ์‹  ๋ธŒ๋ผ์šฐ์ €๋ฅผ ๊ด‘๋ฒ”์œ„ํ•˜๊ฒŒ ์ง€์›ํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ธŒ๋ผ์šฐ์ €์˜ ์ผ๋ถ€ ์ด์ „ ๋ฒ„์ „์—์„œ๋Š” ์ด๋ฅผ ์‹คํ–‰ํ•˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด ๋ฉ”์„œ๋“œ ์ด๋ฆ„ ์•ž์— ๋ฒค๋” ํ”„๋ฆฌํ”ฝ์Šค๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฉ”์†Œ๋“œ๋ฅผ ์ง€์›ํ•˜์ง€ ์•Š๋Š” ์–ด๋– ํ•œ ํ™˜๊ฒฝ์—์„œ์˜ ๋ธŒ๋ผ์šฐ์ €๋„ ํŠน์ • ์ง€์—ฐ ์‹œ๊ฐ„ ์ด ํ›„์— ์ฝ”๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๊ธฐ ์œ„ํ•ด setTimeout()์— ํด๋ฐฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋‹ค์Œ ์ฝ”๋“œ๋Š” ๋ฒ”์šฉ์ ์œผ๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” requestAnimationFrame() ํ•จ์ˆ˜์™€ cancelAnimationFrame() ํ•จ์ˆ˜์ž…๋‹ˆ๋‹ค ์ด ํ•จ์ˆ˜๋Š” ํด๋ฐฑ์ด ๋‚ด์žฅ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

window.requestAnimationFrame = window.requestAnimationFrame
    || window.mozRequestAnimationFrame
    || window.webkitRequestAnimationFrame
    || window.msRequestAnimationFrame
    || function(f){return setTimeout(f, 1000/60)} // simulate calling code 60 
 
window.cancelAnimationFrame = window.cancelAnimationFrame
    || window.mozCancelAnimationFrame
    || function(requestID){clearTimeout(requestID)} //fall back

๋Œ€์ฒด setTimeout() ์ฝ”๋“œ์˜ ๊ฒฝ์šฐ ์ง€์—ฐ์ด 1000/60 ๋˜๋Š” 16.7 ๋ฐ€๋ฆฌ์ดˆ๋กœ ์„ค์ •๋ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์‚ฌ์šฉ์ž์˜ ํ™”๋ฉด ์ƒˆ๋กœ ๊ณ ์นจ ์†๋„์ธ ์ดˆ๋‹น 60ํ”„๋ ˆ์ž„์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ํ˜ธ์ถœํ•  ๋•Œ๋งˆ๋‹ค ์‹ค์ œ requestAnimationFrame()์ด ์ผ๋ฐ˜์ ์œผ๋กœ ์–ผ๋งˆ๋‚˜ ์ž์ฃผ ํ˜ธ์ถœ๋˜๋Š”์ง€ ์‹œ๋ฎฌ๋ ˆ์ดํŠธํ•ฉ๋‹ˆ๋‹ค.

requestAnimationFrame()์˜ ์ดํ•ด ๋ฐ ์‚ฌ์šฉ

requestAnimationFrame์˜ ๊ตฌ๋ฌธ์€ ๋งค์šฐ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค.

requestAnimationFrame(callback)

์šฐ๋ฆฌ๊ฐ€ ์‹คํ–‰ํ•˜๊ณ ์ž ํ•˜๋Š” ์ฝ”๋“œ๋ฅผ ํฌํ•จํ•˜๋Š” ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ์ž…๋ ฅํ•˜๊ณ  requestAnimationFrame()์€ ํ™”๋ฉด์ด ๋‹ค์Œ ํ™”๋ฉด์„ ๋ฆฌํŽ˜์ธํŠธ๋ฅผ ๋ฐ›์•„๋“ค์ผ ์ค€๋น„๊ฐ€ ๋˜๋ฉด ์ฝœ๋ฐฑ ํ•จ์ˆ˜๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.

์ฃผ๋ชฉํ•  ๋งŒํ•œ ๋ช‡ ๊ฐ€์ง€ ์‚ฌํ•ญ์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์ฝœ๋ฐฑ ํ•จ์ˆ˜์—๋Š” requestAnimationFrame()์ด ํ˜ธ์ถœ๋œ ์ •ํ™•ํ•œ ์‹œ๊ฐ„์„ ๋‚˜ํƒ€๋‚ด๋Š” ํƒ€์ž„ ์Šคํƒฌํ”„๊ฐ€ ์ž๋™์œผ๋กœ ์ „๋‹ฌ๋ฉ๋‹ˆ๋‹ค.

  • requestAnimationFrame()์€ ์ฝœ๋ฐฑ ํ•จ์ˆ˜ ์‹คํ–‰์„ ์‹œ์ž‘ํ•  ๋•Œ์˜ ์‹œ์ (nemesis counterpart ์˜์—ญ)์ด cancelAnimationFrame()์— ์ „๋‹ฌ๋˜์–ด requestAnimationFrame() ํ˜ธ์ถœ์„ ์ทจ์†Œํ•  ์ˆ˜ ์žˆ๋Š” 0์ด ์•„๋‹Œ ์ •์ˆ˜๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค. (requestAnimationFrame() returns a non 0 integer that can be passed into its nemesis counterpart cancelAnimationFrame() to cancel a requestAnimationFrame() call)

requestAnimationFrame ()์„ ํ•œ ๋ฒˆ ํ˜ธ์ถœํ•˜์—ฌ DIV๋ฅผ ํ™”๋ฉด์˜ ์›๋ž˜ ์œ„์น˜์—์„œ ๋‹จ์ง€ 5ํ”ฝ์…€ ์ด๋™์‹œํ‚ค๋Š” ์˜ˆ์ž…๋‹ˆ๋‹ค.

var adiv = document.getElementById('mydiv')
var leftpos = 0
requestAnimationFrame(function(timestamp){
    leftpos += 5
    adiv.style.left = leftpos + 'px'
})

์œ„์˜ ์ฝ”๋“œ๋Š” setTimeout()์„ ์‚ฌ์šฉํ•˜์—ฌ ๋™์ผํ•œ ์ฝ”๋“œ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๊ฒƒ๊ณผ ๋งค์šฐ ์œ ์‚ฌํ•ฉ๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ์ •์˜ํ•œ ์ง€์—ฐ ์‹œ๊ฐ„ ์ดํ›„์— ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹Œ ๋‹ค์Œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ ํ™”๋ฉด์„ ๋ฆฌํŽ˜์ธํŠธํ•  ๋•Œ ์ฝ”๋“œ๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค. (์ผ๋ฐ˜์ ์œผ๋กœ 60fps, ํ™”๋ฉด ์ƒˆ๋กœ ๊ณ ์นจ ๋นˆ๋„์— ๋”ฐ๋ผ ์•ฝ 16.7 ๋ฐ€๋ฆฌ ์ดˆ) ์ •ํ™•ํ•œ ์ˆ˜์น˜๋Š” ๋ณ€๋™์ด ๋  ์ˆ˜ ์žˆ์œผ๋ฉฐ ์ค‘์š”ํ•˜์ง€๋Š” ์•Š์Šต๋‹ˆ๋‹ค. ์—ฌ๊ธฐ์„œ ์ค‘์š”ํ•˜๊ฒŒ ๋ด์•ผํ•  ๋ถ€๋ถ„์€ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์•„๋‹Œ ์ด์ „ ํ™”๋ฉด์„ ๋ณ€๊ฒฝํ•  ์ค€๋น„๊ฐ€ ๋˜์—ˆ์„ ๋•Œ๋งŒ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ง€๋Šฅ์ ์œผ๋กœ ์ฝ”๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

requestAnimationFrame()์„ ํ•œ ๋ฒˆ ํ˜ธ์ถœํ•˜๋ฉด ๋Œ€๋ถ€๋ถ„ ๋ฌด์˜๋ฏธํ•ด์ง‘๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ "์žฌ๊ท€์ ์ด๊ฒŒ" ํ˜ธ์ถœ๋  ๋•Œ๋งˆ๋‹ค ํ”„๋ ˆ์ž„๋ณ„๋กœ ์›ํ•˜๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜ ํ”„๋ ˆ์ž„์„ ์ƒ์„ฑํ•  ๋•Œ ๋น„๋กœ์†Œ ์˜๋ฏธ๊ฐ€ ์žˆ์–ด์ง‘๋‹ˆ๋‹ค. ๊ฐ ํ”„๋ ˆ์ž„์€ ๋ธŒ๋ผ์šฐ์ €๊ฐ€ ์ค€๋น„๋  ๋•Œ๋งŒ ํ˜ธ์ถœ๋ฉ๋‹ˆ๋‹ค. ์ด๊ฒƒ์€ ์• ๋‹ˆ๋ฉ”์ด์…˜ ๊ด€๋ จ ์ฝ”๋“œ๋ฅผ ํšจ์œจ์ ์œผ๋กœ ์ฒ˜๋ฆฌํ•  ๋•Œ requestAnimationFrame ()์ด setTimeout ๋˜๋Š” setInterval๋ณด๋‹ค ์šฐ์ˆ˜ํ•œ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. requestAnimationFrame()์„ ์‚ฌ์šฉํ•˜์—ฌ ํ™”๋ฉด์—์„œ DIV๋ฅผ ํ•œ ๋ฒˆ์— 5ํ”ฝ์…€์”ฉ ์ด๋™ํ•˜๋Š” ์ดˆ๊ธฐ ์˜ˆ์ œ๋ฅผ ๋‹ค์‹œ ์ž‘์„ฑํ•ฉ๋‹ˆ๋‹ค.

var adiv = document.getElementById('mydiv')
var leftpos = 0
function movediv(timestamp){
    leftpos += 5
    adiv.style.left = leftpos + 'px'
    requestAnimationFrame(movediv) // call requestAnimationFrame again to animate next frame
}
requestAnimationFrame(movediv) // call requestAnimationFrame and pass into it animation function

์œ„์˜ ์ฝ”๋“œ๋Š” requestAnimationFrame()์„ ์‚ฌ์šฉํ•˜์—ฌ ํ•จ์ˆ˜ ๋‚ด์—์„œ ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์ •์˜ํ•œ ๋‹ค์Œ requestAnimationFrame()์„ ํ†ตํ•ด ์Šค์Šค๋กœ ์žฌ๊ท€์ ์œผ๋กœ ํ˜ธ์ถœํ•˜์—ฌ ์• ๋‹ˆ๋ฉ”์ด์…˜์˜ ๊ฐ ํ”„๋ ˆ์ž„์„ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•œ ๊ธฐ๋ณธ blueprint๋ฅผ ๋ณด์—ฌ์ค€๋‹ค. ์• ๋‹ˆ๋ฉ”์ด์…˜์„ ์‹œ์ž‘ํ•˜๋ ค๋ฉด, ํ•ด๋‹น ํ•จ์ˆ˜๋ฅผ ๋งค๊ฐœ ๋ณ€์ˆ˜๋กœ ์‚ฌ์šฉํ•˜์—ฌ ํ•จ์ˆ˜ ๋ฐ–์—์„œ requestAnimationFrame()์„ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

requestAnimationFrame()์˜ ์‹œ๊ฐ„ ๊ฒฝ๊ณผ์— ๋”ฐ๋ฅธ ์• ๋‹ˆ๋ฉ”์ด์…˜

๋”ฐ๋ผ์„œ requestAnimationFrame()์„ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ˜๋ณต์ ์œผ๋กœ ์• ๋‹ˆ๋ฉ”์ด์…˜ ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•˜๋Š” ๊ฒƒ๋งŒํผ ๊ฐ„๋‹จํ•˜์ง€๋งŒ ๋Œ€๋ถ€๋ถ„์˜ ์• ๋‹ˆ๋ฉ”์ด์…˜์€ ํŠน์ • ๊ธฐ๋Šฅ์„ ์ˆ˜ํ–‰ํ•œ ํ›„ ์–ด๋Š ์‹œ์ ์—์„œ ๋ฉˆ์ถฐ์•ผ ํ•  ๋•Œ๊ฐ€ ๊นŒ๋‹ค๋กญ์Šต๋‹ˆ๋‹ค. ์œ„์˜ DIV๋ฅผ ์›€์ง์ด๋Š” ์˜ˆ๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ์‹ค์ œ๋กœ ๊ตฌํ˜„ํ•  ๋ฒ•ํ•œ ์‹œ๋‚˜๋ฆฌ์˜ค๋ฅผ 'DIV๋ฅผ 2์ดˆ ์ •๋„์˜ ์‹œ๊ฐ„ ๋™์•ˆ 400ํ”ฝ์…€ ์˜ค๋ฅธ์ชฝ์œผ๋กœ ์ด๋™'์œผ๋กœ ์ •์˜ํ•ด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค. requestAnimationFrame()์„ ์‚ฌ์šฉํ•˜์—ฌ ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•˜๋ ค๋ฉด ์ฝœ๋ฐฑ ํ•จ์ˆ˜์— ์ „๋‹ฌ๋œ timestamp ๋งค๊ฐœ ๋ณ€์ˆ˜๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์œ„์˜ movediv ์ฝ”๋“œ๋ฅผ ๋‹ค์‹œ ์ˆ˜์ •ํ•˜์—ฌ DIV๋ฅผ ์ผ์ • ์‹œ๊ฐ„ ๋™์•ˆ ์ผ์ •ํ•œ ๊ฑฐ๋ฆฌ๋งŒํผ ์ด๋™ ์‹œํ‚ค๋ฉด ์ด๊ฒƒ์ด ์–ด๋–ป๊ฒŒ ์ž‘๋™ํ•˜๋Š”์ง€ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

var adiv = document.getElementById('mydiv')
var starttime
 
function moveit(timestamp, el, dist, duration){
    //if browser doesn't support requestAnimationFrame, generate our own timestamp using Date:
    var timestamp = timestamp || new Date().getTime()
    var runtime = timestamp - starttime
    var progress = runtime / duration
    progress = Math.min(progress, 1)
    el.style.left = (dist * progress).toFixed(2) + 'px'
    if (runtime < duration){ // if duration not met yet
        requestAnimationFrame(function(timestamp){ // call requestAnimationFrame again with parameters
            moveit(timestamp, el, dist, duration)
        })
    }
}
 
requestAnimationFrame(function(timestamp){
    starttime = timestamp || new Date().getTime() //if browser doesn't support requestAnimationFrame, generate our own timestamp using Date
    moveit(timestamp, adiv, 400, 2000) // 400px over 1 second
})

์ด๊ฒƒ์ด ์–ด๋–ป๊ฒŒ ์‹คํ–‰๋˜๋Š”์ง€ ์‚ดํŽด๋ณด๊ฒ ์Šต๋‹ˆ๋‹ค.

  • ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ์‹คํ–‰๋˜๊ธฐ ์ง์ „์— requestAnimationFrame์˜ timestamp ๋งค๊ฐœ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ starttime ๋ณ€์ˆ˜๋ฅผ ํ˜„์žฌ ์‹œ๊ฐ์œผ๋กœ ์„ค์ •ํ•˜๊ฑฐ๋‚˜ requestAnimationFrame์ด ์ง€์›๋˜์ง€ ์•Š๋Š” ๊ฒฝ์šฐ ์ •ํ™•์„ฑ์ด ๋–จ์–ด์ง€๋Š” new Date().getTime()์„ ๋Œ€์‹  ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ์‹คํ–‰๋˜๊ธฐ ์ง์ „์— requestAnimationFrame์˜ ์ฝœ๋ฐฑ ํ•จ์ˆ˜์˜ ์ฒซ ๋ฒˆ์งธ ๋งค๊ฐœ๋ณ€์ˆ˜์ธ ์ž๋™์œผ๋กœ ์ „๋‹ฌ๋œ ๊ฐ’์ด ํ˜„์žฌ ์‹œ๊ฐ์„ ๋ฐ€๋ฆฌ ์ดˆ ๋‹จ์œ„๋กœ ์ •ํ™•ํ•˜๊ฒŒ ํ‘œ์‹œํ•ฉ๋‹ˆ๋‹ค. (์ •ํ™•ํžˆ 5๋งˆ์ดํฌ๋กœ ์ดˆ). ์ด๋ฅผ ํ†ตํ•ด ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ์–ธ์ œ ์‹œ์ž‘๋˜์—ˆ๋Š”์ง€ ์•Œ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

  • ์• ๋‹ˆ๋ฉ”์ด์…˜ ํ•จ์ˆ˜ moveit() ๋‚ด์—์„œ ๋ณ€์ˆ˜ timestamp๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ˜„์žฌ "ํ”„๋ ˆ์ž„"์˜ ํ˜„์žฌ ์‹œ๊ฐ์„ ์บก์ฒ˜ํ•ฉ๋‹ˆ๋‹ค. ์šฐ๋ฆฌ๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜ ์‹œ์ž‘ ์‹œ์ ๊ณผ์˜ ์ฐจ์ด์ ์„ ์‚ฌ์šฉํ•˜์—ฌ ํ˜„์žฌ ์• ๋‹ˆ๋ฉ”์ด์…˜์— ์žˆ๋Š” "์ง€์ "์„ ํŒŒ์•…ํ•˜๊ณ  ์ด ๊ฑฐ๋ฆฌ (์ฆ‰ : 400ํ”ฝ์…€)์—์„œ DIV์˜ ์œ„์น˜๋ฅผ ๋ณ€๊ฒฝํ•ฉ๋‹ˆ๋‹ค.

requestAnimationFrame()์˜ ์†๋„ ์ €ํ•˜ ๋˜๋Š” ์ทจ์†Œ

ํ‘œ์ค€ requestAnimationFrame์€ ์ด์ƒ์ ์ธ ์กฐ๊ฑด์—์„œ ์•ฝ 60fps (๋˜๋Š” 16.7ms๋งˆ๋‹ค ํ•œ ๋ฒˆ)์—์„œ ์ผ๋ฐ˜ ๋ชจ๋‹ˆํ„ฐ์˜ ์žฌ์ƒ ๋นˆ๋„์™€ ๋™๊ธฐํ™”๋˜์–ด ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค. ์• ๋‹ˆ๋ฉ”์ด์…˜์ด ์ดˆ๋‹น ๋‹ค๋ฅธ ํ”„๋ ˆ์ž„ (์ตœ๋Œ€ 60fps)์„ ํ•„์š”๋กœ ํ•˜๊ฑฐ๋‚˜ ๊ฐ„๋‹จํžˆ ๋†’์€ ์žฌ์ƒ ๋นˆ๋„๋ฅผ ์š”๊ตฌํ•˜์ง€ ์•Š์œผ๋ฉด setTimeout() ๋‚ด requestAnimationFrame์„ ํ˜ธ์ถœํ•˜์—ฌ ๋Š๋ ค์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์•„๋ž˜์™€ ๊ฐ™์€ ๋ฐฉ๋ฒ•์œผ๋กœ ์ ์šฉํ•˜๋ฉด requestAnimationFrame์˜ ์ด์ ์„ ๋ˆ„๋ฆฌ๋Š” ๋™์•ˆ ์›ํ•˜๋Š” ํ”„๋ ˆ์ž„ ์†๋„๋ฅผ ์–ป์„ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค

var adiv = document.getElementById('mydiv')
var leftpos = 0
var fps = 20
function movediv(timestamp){
    setTimeout(function(){ //throttle requestAnimationFrame to 20fps
        leftpos += 5
        adiv.style.left = leftpos + 'px'
        requestAnimationFrame(movediv)
    }, 1000/fps)
}
 
requestAnimationFrame(movediv)

DIV๋ฅผ ์ˆ˜ํ‰์œผ๋กœ ์›€์ง์ด๋Š” ์ด ๋ฒ„์ „์—์„œ๋Š” ๋งค๋ฒˆ setTimeout () ๋‚ด์— requestAnimationFrame์„ ํ˜ธ์ถœํ•˜์—ฌ ์ดˆ๋‹น ํ”„๋ ˆ์ž„์„ ์•ฝ 20๊ฐœ๋กœ ์กฐ์ ˆํ•ฉ๋‹ˆ๋‹ค.

requestAnimationFrame() ์ทจ์†Œ ์‹คํ–‰

setTimeout/ setInterval๊ณผ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ requestAnimationFrame ํ˜ธ์ถœ์„ ์ทจ์†Œ ํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ ๋™์ผํ•œ ๋ฐฉ์‹์œผ๋กœ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. requestAnimationFrame์ด ํ˜ธ์ถœ๋˜๋ฉด ๋ณ€์ˆ˜ ๋‚ด์—์„œ ์บก์ณํ•œ ๊ฐ’์ธ ์ฝœ๋ฐฑ ํ•จ์ˆ˜ ์‹คํ–‰์„ ์‹œ์ž‘ํ•  ๋•Œ์˜ ์‹œ์ ์˜ ๊ฐ’์„ cancelAnimationFrame()์— ์ „๋‹ฌํ•˜์—ฌ ์ฝœ๋ฐฑ ์š”์ฒญ์„ ์ทจ์†Œ ํ•˜๋Š” 0์ด ์•„๋‹Œ ์ •์ˆ˜๊ฐ€ ๋ฐ˜ํ™˜๋ฉ๋‹ˆ๋‹ค.(requestAnimationFrame when called returns a non 0 integer that can be captured inside a variable and passed into its nemesis counterpart cancelAnimationFrame() to stop it from being invoked again) ์•„๋ž˜์˜ ์ฝ”๋“œ๋Š” cancelAnimationFrame์„ ์‚ฌ์šฉํ•˜์—ฌ ์ฝœ๋ฐฑ์„ ์ค‘์ง€ํ•˜๊ธฐ ์œ„ํ•ด requestAnimationFrame์˜ timestamp ๋งค๊ฐœ ๋ณ€์ˆ˜ ๊ฐ’์„ 2์ดˆ ๋™์•ˆ ๊ธฐ๋กํ•˜๋Š” ์ฝ”๋“œ์ž…๋‹ˆ๋‹ค.

var reqanimationreference
 
function logtimestamp(timestamp){
    console.log(timestamp)
    reqanimationreference = requestAnimationFrame(logtimestamp)
}
 
requestAnimationFrame(logtimestamp)
 
setTimeout(function(){ // cancel requestAnimationFrame after 2 seconds
    cancelAnimationFrame(reqanimationreference)
}, 2000)

๋ฐ๋ชจ์— ๋Œ€ํ•œ ์„ค๋ช… ๋‹ค์Œ์€ ๋งˆ์šฐ์Šค๊ฐ€ ๋ถ€๋ชจ ์ปจํ…Œ์ด๋„ˆ(onmouseenter)์— ๋“ค์–ด๊ฐˆ ๋•Œ requestAnimationFrame์„ ์‚ฌ์šฉํ•˜์—ฌ DIV์˜ ๋„ˆ๋น„๋ฅผ ๊ณ„์† ๋ณ€๊ฒฝํ•˜๊ณ  onmouseleave๋ฅผ ์ทจ์†Œ๋ฅผ ํ•˜๋Š” ์ •๊ตํ•œ ์˜ˆ์ œ์ž…๋‹ˆ๋‹ค.

๊ฒฐ๋ก 

requestAnimationFrame()์€ ์‹ค์ œ๋กœ ๋ชฉ์ ๊ณผ ๊ณตํ†ต ํŒจํ„ด์„ ์ดํ•ดํ•˜๋ฉด ๊ฐœ๋…๊ณผ ๊ตฌํ˜„์ด ๋งค์šฐ ๊ฐ„๋‹จํ•ฉ๋‹ˆ๋‹ค. jQuery 3.0๊ณผ ๊ฐ™์€ ๋งŽ์€ ํ”„๋ ˆ์ž„ ์›Œํฌ๋Š” requestAnimationFrame()์„ ๋ชจ๋“  ์• ๋‹ˆ๋ฉ”์ด์…˜ ๊ด€๋ จ ํ•จ์ˆ˜์— ๋‚ด๋ถ€์ ์œผ๋กœ ํ™œ์šฉํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์ด ๋ฉ”์„œ๋“œ๊ฐ€ ๊ธฐ๋ณธ์ ์œผ๋กœ ์ž‘๋™ํ•˜๋Š” ๋ฐฉ์‹์„ ์ดํ•ดํ•˜๋ฉด ๋ชจ๋“  JavaScript ํ™˜๊ฒฝ์—์„œ์ด ๋ฉ”์„œ๋“œ๋ฅผ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

โš ๏ธ **GitHub.com Fallback** โš ๏ธ