ããã¹ãã§ã¯ãReactã¢ããªã±ãŒã·ã§ã³ã§ã¢ãã¡ãŒã·ã§ã³ãæäœããæ¹æ³ã«ã€ããŠèª¬æããããã€ãã®ã¢ãããŒãïŒD3ãReact-MotionããããŒãã£ã³ã³ããŒãã³ããïŒãæ¯èŒããŸãã Reduxã¢ããªã±ãŒã·ã§ã³ã§ã¢ãã¡ãŒã·ã§ã³ããå®è¡ãããæ¹æ³ãšåæ§ã«ã ãã®è³æã¯ã12æã®HolyJS 2017ã¢ã¹ã¯ã¯äŒè°ã§ã®Alexei Taktarovã®ã¬ããŒãã®è§£éã«åºã¥ããŠããŸãã ãã®ã¬ããŒãã®ãããªãåæã«æ·»ä»ããŸãã
泚æããã©ãã£ãã¯ïŒã«ããã®äžã«ã¯ããããã®åçãšgifããããŸãïŒçŽ æã¯ã¢ãã¡ãŒã·ã§ã³ã«é¢ãããã®ã§ãïŒã
話ããå§ããããšæããŸãã çŽå å1äžçŽé ã«å€ä»£ããŒãã«ããå ŽåããŠã£ãã«ãŠã£ãŠã¹ã«äŒãããšãã§ããŸãã
ãŠã£ãã«ãŠã£ãŠã¹ã¯ãåœææãæåãªå»ºç¯å®¶ã®äžäººã§ãã 圌ã¯å»ºç¯ã«é¢ãã10å·»ãæžããŸããã 圌ã®åŽåã¯å€ãã®äººã«åœ±é¿ãäžããŸããã ããããæãèå³æ·±ãã®ã¯ãæ¬ã®1ã€ã§ãåªããã¢ãŒããã¯ãã£ãæã€ã¹ã3ã€ã®äž»èŠãªç¹æ§ãããªãã¡ã匷ããæçšæ§ãçŸãããæšæž¬ããããšã§ãã
ãã®ãã©ã€ã¢ãã¯ãç§ãã¡ã䜿çšãããã®ã®ãã¶ã€ã³ã«ãèŠãããããã§ãã ãªãã§ãããªããšïŒ ç§ãã¡éçºè ã¯ãã¢ããªã±ãŒã·ã§ã³ããµã€ããäœæããŸãã ç§ãã¡ã¯äººã ã䜿ãããšãããŸãã ãããã£ãŠãç§ãã¡ãã2000幎åã«æšè«ããããããã®ã«ãŒã«ãèŠãŠã¿ãå¿ èŠããããŸãã çµå±ã®ãšãããç§ãã¡ãäœæããã¢ããªã±ãŒã·ã§ã³ã¯ã人ã ã®ç¹å®ã®åé¡ã解決ããªããã°ãªããŸããã
å šäœã®ãã€ã³ãã¯ãã£ãŒãããã¯ã«ãããšæããŸãã ãããéèŠãªãã€ã³ãã§ãã ãããŠããçšåºŠããã£ãŒãããã¯ã¯ã¢ãã¡ãŒã·ã§ã³ãç¶æ éã®é·ç§»ã§ãã
次ã«ãReactã®äŸã䜿çšããŠãã¹ããŒããã«ã¢ããªã±ãŒã·ã§ã³ã§ã®ã¢ãã¡ãŒã·ã§ã³ã«ã€ããŠèª¬æããŸãã
å®éã®ãããžã§ã¯ãã®ãã¢ã®äŸã®ã¢ãã¡ãŒã·ã§ã³ãã¿ãŒã³
Reactã«ã€ããŠèª¬æããåã«ããã©ãŠã¶ã§ã®äžè¬çãªã¢ãã¡ãŒã·ã§ã³ã®ä»çµã¿ãšãã®å 容ã«ã€ããŠèããŠã¿ãŸãããã
çæ³çãªäžçã§ã¯ãã¢ãã¡ãŒã·ã§ã³ã¯ã¹ã ãŒãºã«æ©èœããã¯ãã§ãã ããšãã°ãsetintervalãŸãã¯settimeoutã䜿çšããŠå®è¡ã§ãããšä¿¡ããã®ã¯ç°¡åã§ãã ãããŠã2ã€ã®èª€è§£ããããŸãã
誀解â1
ç§ãã¡ã¯ééã£ãŠããŸãã ã¢ãã¡ãŒã·ã§ã³ã«setTimeoutã䜿çšã§ããªãããã§ãã çµå±ã®ãšãããsetTimeoutã¯ãæå®ãããæéå ã«é¢æ°ãå®éã«åäœããããšãä¿èšŒããŸããã ããã«ããããã¬ãŒã ãªãŒããŒã¬ã€ãªã©ã®å¹æãçããå¯èœæ§ããããŸãã ã€ãŸããã¢ãã¡ãŒã·ã§ã³ã¯16ããªç§ã§æ¥ããšèããããŸããããã¯ãããã60ãã¬ãŒã /ç§ã«çžåœããŸãããå®éã«ã¯ããã«æ©èœãããã®è² åµã¯çŽ¯ç©ãã环ç©ããŸãã
幞ããªããšã«ããã®åé¡ã«å¯ŸåŠã§ããããã«ããæ©èœããããŸã-requestAnimationFrameã ãã©ãŠã¶ã¹ã±ãžã¥ãŒã©ã«ãšã£ãŠéœåã®è¯ãæéã«ã³ãŒã«ããã¯ãå®è¡ãããããã«ããŸãã ãã ããäžåçãªééã§ãæ©èœããããšã«æ³šæããŠãã ããã requestAnimationFrameã䜿çšããå Žåã¯ãã¿ãŒã²ããã«ããŠãããã©ãŠã¶ãŒã確èªããããšã匷ããå§ãããŸãã
ãããžã§ã¯ãã§requestAnimationFrameã䜿çšããå Žåã¯ããã®ãã¿ãŒã³ã®ãããªãã®ãé©çšããŸãã ã€ãŸããã¢ãã¡ãŒã·ã§ã³ãçºçããç¬éã«èµ·åããé¢æ°ã宣èšããŸãã 次ã«ãrequestAnimationFrameãåŒã³åºããŸããããã¯ãé©åãªã¿ã€ãã³ã°ã§é¢æ°ãå®è¡ããããšãæå³ããŸãã
ããã«èå³æ·±ãç¹ã ååãšããŠãé¢æ°ã®æåŸã§æ¬¡ã®ãã¬ãŒã ã®ã¹ã±ãžã¥ãŒãªã³ã°ãåŒã³åºãããšãã§ããŸããããã®ãããªãã¿ãŒã³ã¯æåã®æ¬¡ã®ãã¬ãŒã ã®requestAnimationFrameãåŒã³åºããŠããŸãã é¢æ°ã®å®è¡äžã«äœãèµ·ããã誰ãç¥ããªãã®ã§ã ããšãã°ã圌女ã¯äŸå€ãã¹ããŒããã§ãããã ãããã£ãŠãããã«æ¬¡ã®ãã¬ãŒã ãèšç»ããããšããå§ãããŸãã
// Or use a polyfill: // import requestAnimationFrame from 'raf' const { requestAnimationFrame } = window const animate = () => { requestAnimationFrame(animate) // Perform an animation step x += velocity } // Fire it up requestAnimationFrame(animate)
requestAnimationFrameã¯ããã©ãŠã¶ãŒã®ã¢ãã¡ãŒã·ã§ã³ã«äžå¯æ¬ ãªããŒã«ã§ãã
次ã«ãããã€ãã®ããããã£ãæéã®çµéãšãšãã«ã¢ãã¡ãŒã·ã§ã³åããŸããããšãã°ããªããžã§ã¯ãã®åº§æšãååŸãããããæ¬è³ªçã«é床ã§ããå®æ°ã ãå¢å ãããŸãã ãããŠãããã§ãšã©ãŒçªå·2ã«çŽé¢ããŠããŸãã
誀解â2
é床ã¯äžå®ã§ãïŒ
ãã¢ãèŠã
ãã®é¢æ°ã¯ç°ãªãééã§åŒã³åºããããããã·ã¹ãã å ã§ä»ã®èšç®ãåæã«çºçããå Žåã«çºçããå¯èœæ§ããããæºè¶³ã§ããªãç°ãªãè»éãååŸããŸãã ãããã£ãŠãrequestAnimationFrameã®åäœãæéå·®ã«åºã¥ããŠèª¿æŽããããšãéèŠã§ãã ãŸããæåã®ãã©ã¡ãŒã¿ãŒãšããŠrequestAnimationFrameã¯ã¿ã€ã ã¹ã¿ã³ããæäŸããŸããããã¯ããã©ãŠã¶ãŒãéãããŠããã®ç¹å®ã®ã¿ã€ã ã¹ã¿ã³ãã§ãã äžéšã®ãã©ãŠã¶ã§ã¯ããã®ã¿ã°ã¯é«ç²ŸåºŠã¿ã°ã«ãªããŸãã
requestAnimationFrame(timestamp => { // DOMHighResTimeStamp // timestamp ~> 30485.84100000153 })
rAFã¯ã5ãã€ã¯ãç§ã®ç²ŸåºŠã§ã¿ã€ã ã¹ã¿ã³ããã³ãŒã«ããã¯ã«æž¡ããŸãã
äžè¬ã«ãå°æ°ç¹ã®åã«ããªç§ãå«ãŸãããã®åŸã«ãã€ã¯ãç§ãå«ãŸãããããã«ïŒèŠªåã䜿çšãããå Žåã¯ã»ãšãã©ãããŸããïŒã«ãªããšäºæ³ã§ããŸãã
ãã®ã©ãã«ã䜿çšããŠããããšåã®é¢æ°åŒã³åºãã®å·®ãèšç®ã§ããŸãã
const animate = timestamp => { const delta = timestamp - prevTimestamp // Note, it's a function now! x += velocity(delta) requestAnimationFrame(animate) }
åŒã³åºãéã®æéå·®ãèæ ®ãããã«ã¿ã«æ¯äŸããŠå€ãã¢ãã¡ãŒã·ã§ã³åããããšãéèŠã§ãïŒ
ãããã£ãŠãçŸåšã®ã©ãã«ãšåŒã³åºãã®åã®ã©ãã«ãšã®å·®ã§ããããçš®ã®ãã«ã¿ãå°å ¥ããå€æ°ãã¢ãã¡ãŒãããŸãããããã¯é¢æ°ã«ãªããŸãã ããã§ãé¢æ°å ã§å€æãããã«ã¿ãæž¡ããŸãã ãããŠãé床ã¯æ¯äŸãããšæããŸãã
äœãèµ·ãããèŠãŠã¿ãŸãããã
ãã¢ãèŠã
å·ŠåŽ-å®ç§ãªã¢ãã¡ãŒã·ã§ã³ãå³åŽ-é©å¿ã¢ã«ãŽãªãºã ã䜿çšã äžå®ã®ééã§åŒã³åºããè¡ãããªãããšã«æ°ä»ããããããŸããã ãŸããå³åŽã®ã¢ãã¡ãŒã·ã§ã³ã¯å·ŠåŽã®ã¢ãã¡ãŒã·ã§ã³ã»ã©æ»ããã§ã¯ãªãå ŽåããããŸãããåã圢ç¶ã§ãã ã€ãŸããåãããã«èŠããããšãä¿èšŒããŸãã ããã¯ãã¬ãŒã ã¹ããããšåŒã°ããŸãã äžå®ã®æéå ã«ã¢ãã¡ãŒã·ã§ã³ãå®è¡ããããšã¯æ£åžžã§ã¯ãããŸããããã¢ãã¡ãŒã·ã§ã³ã®ãã¬ãŒã ãã¹ãããããããšã¯å®å šã«æ£åžžã§ãã ãããã£ãŠããã«ã¿ã¢ãããŒãã䜿çšãããšãé«æ§èœã®ã¢ãã¡ãŒã·ã§ã³ãäœæã§ããŸãã
å°ããªäŸ
ãããªã§ã¯ã13: 09ã«éå§ããŸãã
requestAnimationFrameã¯ããã©ãŠã¶ãŒã®ã«ã¹ã¿ã ã¢ãã¡ãŒã·ã§ã³ã«æé©ãªããŒã«ã§ãã ãã®äžã§ãã¢ãã¡ãŒã·ã§ã³ã®ããžãã¯ãå®è¡ã§ããããã§ãã
ãã¢ãèŠã
ãã®äŸã§ã¯ãäœããã®æ³åã«åŸã£ãŠåãç²åãæšå®ããŸããã ããã¯ãé£è¡äžã®é³¥ã®åãã®ã·ãã¥ã¬ãŒã·ã§ã³ã§ãã ãã©ãŠã¶ã§ãã®ãããªäŸãã©ã®ããã«è¡ããŸããïŒ æãããã¢ãã¡ãŒã·ã§ã³ãã¬ã³ããªã³ã°ãããšãã«åŒã³åºããããã£ãã¯æ©èœãéå§ããã§ãããã 圌女ã¯2ã€ã®ããšãããŸãïŒç©çåŠãæ°ãããããŠåæç»ããŸãã
const redraw = _ => { points.forEach(point => { // make sure `will-change: transform` is set point.element.style.transform = ` translate3d(${point.x}px, ${point.y}px, 0.0px) rotate(${point.angle}rad)` }) } const tick = ts => { _lastRaf = requestAnimationFrame(tick) physicsStep(delta) redraw(delta) }
åæç»ã«ã€ããŠã¯éåžžã«èå³æ·±ããã®ã§ããäŸã§ç€ºããããã«ãdivã䜿çšããå Žåããã©ãŠã¶ã§ã§ããã ãæ©ãã¢ãã¡ãŒã·ã§ã³åããã«ã¯ãtransformã䜿çšããå¿ èŠããããŸãã ãã ããããŒãžã³ãããã£ã³ã°ããŸãã¯çµ¶å¯Ÿé 眮ã䜿çšããå Žåã¯æåããŸããããããŸããã£ãå Žåã¯éåžžã«é ããªããŸãã
ãããã®èŠçŽ ã«ãwill-changeïŒtransformãããããã£ãããããšãéåžžã«éèŠã§ãã ããã«ãããæ£æ¹åœ¢ãããã«å¥ã®ãã©ãŠã¶ãŒã¬ã€ã€ãŒã«é 眮ããã1ã€ã®å ±éã®ã¬ã€ã€ãŒã«ã³ã³ãã€ã«ãããããã«ãªããŸãã ãããã£ãŠãæ倧ã®ããã©ãŒãã³ã¹ãéæã§ããŸãã 次ã«ããã¹ãŠã®ãã€ã³ãã調ã¹ãŠãç»é¢äžã®ãã€ã³ãã®å転ãšäœçœ®ãèšå®ããŸãã
次ã«ãã¹ããŒããã«ã¢ããªã±ãŒã·ã§ã³ã«ã€ããŠèª¬æããŸãã
ç§ã¯ãå€ãã®äººããããå®çŸããããšãªããã¹ããŒããã«ãªã¢ããªã±ãŒã·ã§ã³ã§åäœãããšç¢ºä¿¡ããŠããŸãã ãããŠãã»ãšãã©ã®å ŽåãImmutable UIãšåŒã°ããã¢ãããŒãã䜿çšããŸãã
äžå€UIãšã¯äœã§ããïŒ ããã¯ãäœããã®ç¶æ ãããããããããŒãžäžã®èŠçŽ ã«æ確ã«å€æããå Žåã§ãã ããã¯éåžžãåãªãã¬ã³ããªã³ã°ã§ãã ã€ãŸããrenderé¢æ°ãåŒã³åºããåŸãæã£ãŠããããŒã¿ãèŠçŽ ã«å€æãããããŒãžã®ç¶æ ãååŸãããŸãã ãã¹ãŠãçŽ æŽãããïŒ ãããããã®åŸãããŒãžäžã§ããã€ãã®ã¢ã¯ã·ã§ã³ãå®è¡ãå§ããããŠã¹ã§æäœããããããŒããŒãã®ããŒãæŒããŠãã€ãã³ããäœæããŸãã
ã¢ããªã±ãŒã·ã§ã³ã®ãããã®ã€ãã³ãã«ãããããŒãžã®èŠçŽ ã ãã§ãªããç¶æ ãå€åããŸãã ã€ãŸããæ¬è³ªçã«ãã¢ããªã±ãŒã·ã§ã³ã¯ç¶æ ã®ãã§ãŒã³ãšãããŒãžäžã®èŠçŽ ã®å¯Ÿå¿ããç¶æ ã§ãã ãããåé¡ã¯ããªã¢ã¯ãã£ããŸãã¯ã¢ã³ã°ã«ã§äœæ¥ããŠããå Žåãéçºè ãšããŠã¯ãããããããé ãããŠãããšããããšã§ãããªããªããç¶æ ãæŽæ°ãããšãåæç»ãããæ°ããç¶æ ãç»é¢ã«è¡šç€ºãããããã§ãã ãããã£ãŠããã®å Žåãã¢ãã¡ãŒã·ã§ã³ãã©ãããããšããçåãçããŸãã
ãã®Immutable UIã®ã¢ãããŒãã«ã¯å€ãã®å©ç¹ãããããšã«æ³šæããŠãã ãããããã¯ãç°¡åã«ãã¹ãããŠããŸã£ããããããªããšãããããšãã§ããããã§ãã
ã¹ãã€ã©ãŒã®äžã®äŸã§ã¯ãto doã·ãŒãã®èŠãç®ãäœæããŸããïŒ 18:55ãããã¹ãã€ã©ãŒãæ¿å ¥ããŸãïŒã
ç§ã¯åœŒã®ãã€ã³ãã®ç¶æ ãå€æŽããããããã¹ãŠãåæã«æšæž¬ããèªåã®è¡åã«é¢ããŠæéãé¡ãããšãã§ããŸããã ããã¯ãã¹ãŠéåžžã«ã¯ãŒã«ã§ãã
äžå€ã¢ããªã±ãŒã·ã§ã³ã§ã¢ãã¡ãŒã·ã§ã³ãäœæããæãç°¡åãªæ¹æ³
ããã§ãäžå€ã¢ããªã±ãŒã·ã§ã³ã§ã¢ãã¡ãŒã·ã§ã³ãäœæããæãç°¡åãªæ¹æ³ãèŠãŠã¿ãŸãããã ãããã¯cssé·ç§»ã§ãã
// CSS property // transition: transform is ease; // Conditional state change <div className ={isVisible ? 'is-visible' : 'is-hidden'} /> // Direct style manipulation <div style={{ transform: `translate(${scale})` }} />
Reactã®CSSã¢ãã¡ãŒã·ã§ã³ã¯ãã®ãŸãŸäœ¿çšã§ããŸãã é·ç§»ããããã£+ç¶æ å€å=ã¢ãã¡ãŒã·ã§ã³ã
圌ãã¯ããªããæã£ãŠããã»ãŒãã¹ãŠã®ã¿ã¹ã¯ã«é©ããŠãããšããç¹ã§è¯ãã§ãã ãããã¯ãç¶æ ããŒã¹ã®ã¢ããªã±ãŒã·ã§ã³ãšãŸã£ããåãããã«æ©èœããŸãã ããããã£ãå®çŸ©ãããããã©ã®ããã«ã¢ãã¡ãŒã·ã§ã³åãããããããç¶æ ããå¥ã®ç¶æ ã«ã©ã®ããã«ç§»è¡ããããèšããŸãã ããã«ãReactããã®ä»ã®ã©ã€ãã©ãªã§ã¢ãã¡ãŒã·ã§ã³ãæäœããããã®ãã¿ãŒã³ããããŸãã
ãã¿ãŒã³ã®1ã€ã¯ãããã¯ã©ã¹ãå¥ã®ã¯ã©ã¹ã«çœ®ãæããããšã§ãã ããŠãèŠçŽ ã®ã¹ã¿ã€ã«ãæåã§å€æŽã§ããŸãã ããã§ãã¹ãŠãæ確ã«ãªããŸããã
<div> <div style="transform: translate(42.00px, 165.00px)" /> ... </div
ãã¢ãèŠã
Reactã¢ããªã±ãŒã·ã§ã³ã§CSSé·ç§»ãã©ã®ããã«æ©èœãããã瀺ãããã®äŸãäœæããŸããã ç¹å®ã®æ³åã«åŸã£ãŠããã€ã³ãã®ã»ãããããããããã座æšã«å€æããŠæç»ããŸãã ããã¯ãç°ãªãããããã£ãæã€èŠçŽ ã®é åã§ãã ãã¹ãŠã®ããŒã¿ãå®å šã«å€æŽãããšãç»åãå³åº§ã«å€æŽããããã©ãŠã¶èªäœã移è¡ãå®äºããŸãã
å Žåã«ãã£ãŠã¯ãCSSé·ç§»ã¯éåžžã«èªç¶ã«åäœããŸããã ããšãã°ãããã¢ãã¡ãŒã·ã§ã³ãå®è¡ãããšåæã«å¥ã®ã¢ãã¡ãŒã·ã§ã³ãå®è¡ããå Žåããã©ãŠã¶ã¯åæ¢ããŠæ°ããç¶æ ã«ç§»è¡ããæ¹æ³ãç¥ã£ãŠãããããäœãå£ããŸããã ããããå®éã®ããã°ã©ã ã¯ãã®ããã«ã¯åäœããŸããã
幞ããªããšã«ãReactã®å ŽåãReact-Motionã©ã€ãã©ãªããããŸãã ãããŠããã®äžã§2çªç®ã®ãã¢ïŒ 21:25 ïŒãäœæããŸããã åãäŸãåãäžããŸãããç¹ã®é åãããããã®ç¶æ ãå€æŽããŸãããMotionãšããã©ãããŒããããŸãã ãã¹ãŠãåãããã«èµ·ãããã©ã€ãã©ãªèªäœãæåã§ç§»è¡ãè¡ããŸãã
<div> <Motion style={{x: spring(42.00, y: spring(165.00))}}> <div style="transform: translate(42.00px, 165.00px)" /> </Motion> ... </div>
ãã¢ãèŠã
React-Motionã¯ãç©çãšã³ãžã³ã«äŒŒããã®ã䜿çšããŸãã ã€ãŸããã¢ãã¡ãŒã·ã§ã³ãäºãã«éãåããããšãåããããåŒã«ãªããå šäœçã«èŠãç®ãè¯ãããšã«æ°ä»ãã§ãããã
Reactã§ã¯ããã®ããã«èŠããŸãã
<Motion> {interpolated => <div style={{ opacity: interpolated.x }} /> } </Motion>
React-Motionã¯ãåªããæ©èœãšããŠã®æ©èœãã¿ãŒã³ã䜿çšããŸãã ã³ã³ããŒãã³ãããããããã£ãšãã®åãããå Žåããããã®åã¯èŠçŽ ã§ããå¿ èŠã¯ãããŸããã äœããã®ç¶æ ãåããèŠçŽ ãè¿ãé¢æ°ãªã©ãä»»æã®ã¿ã€ãã®ããŒã¿ã䜿çšã§ããŸãã ãã®ãããªèšé²ã¯åå¿è ã«ã¯å°ãæãã§ããããšãŠãã¯ãŒã«ã«æ©èœããŸãã React-Motionã¯DOMã«å¿ã³èŸŒã¿ãããã€ãã®ããããã£ãå€æŽãããšèãããããããŸããã ããã¯å®éã«ã¯ããã§ã¯ãããŸããã
ããã¯ãæåã«èª¬æããrequestAnimationFrameãšåããã®ã§ãããã¢ãã¡ãŒã·ã§ã³ã®åã¹ãããã§ç¶æ ãæŽæ°ããã ãã§ãã ã€ãŸããåãã¬ãŒã ã¯æ°ããç¶æ ãæ°ããã¬ã³ããŒã§ãã é©ãã¹ãããšã«ãããã¯æ©èœããŸãã
ãã¢ãèŠã
1ã€ã®ãã³ã-ã©ãã§ãReact-Motionã䜿çšããªãã§ãã ããã
æ®å¿µãªããããã¹ãŠã®å Žåã«é©ããŠããããã§ã¯ãããŸããã æ¥ã®ã¢ãã¡ãŒã·ã§ã³ã¯æéã«å¶éããããŸãããã€ãŸãã10ç§éåäœãéå§ããã¢ãã¡ãŒã·ã§ã³ãäœæããå¿ èŠãããå ŽåãReact-Motionã¯ããã§ã®ã¢ã·ã¹ã¿ã³ãã§ã¯ãããŸããã
ãŸãã1ã€ã®èŠçŽ ãæåã«ã¢ãã¡ãŒã·ã§ã³åããã次ã«2çªç®ã®èŠçŽ ãã¢ãã¡ãŒã·ã§ã³åãããè€éãªã¢ãã¡ãŒã·ã§ã³ãæ±ãããšãå°é£ã§ãã ååãšããŠãããã¯ã©ã€ãã©ãªã«ãã£ãŠå®è¡ã§ããŸãããããŸã䟿å©ã§ã¯ãããŸããã
ãããŠæåŸã«ãããã©ãŒãã³ã¹ã ãšã«ãããæåã¢ãã¡ãŒã·ã§ã³ãšæ¯èŒã§ãããã®ã¯ãããŸããã èŠçŽ ã«ç»ãããã®å€æãå€æŽãããšãã ãããã£ãŠãå Žåã«ãã£ãŠã¯ãReact-Motionã¯ãé£ãããåãã«ãªããŸãããããŸãæ©èœããŸãã
ããŒãã£ã¢ãã¡ãŒã·ã§ã³
ããŒãã£ã¢ãã¡ãŒã·ã§ã³ã¯ããã¹ãŠãåžžã«ç¶æ ã«åºã¥ããŠæ§ç¯ã§ããããã§ã¯ãªãã¢ãã¡ãŒã·ã§ã³ã§ãã
class Dialog extends Component{ componentDidMount(){ const node = findDOMNode(this) // Or $.animate, anime.js, GSAP, D3 ... Velocity(node, {scale: 1.5}, {duration: 1000}) } render(){ ... } }
ãå ¥åã¢ãã¡ãŒã·ã§ã³ããã¿ãŒã³ã¯ãcomponentDidMountããã¯ãä»ããŠæ©èœããèŠçŽ ã«çŽæ¥ã¢ã¯ã»ã¹ããŸãã
ãã€ã¢ãã°ããã¯ã¹ã®äŸãèŠãŠã¿ãŸãããã ãã€ã¢ãã°ããã¯ã¹ã衚瀺ãŸãã¯é衚瀺ã«ããå¿ èŠãããå Žåã¯ãããã«ééããŠããå¿ èŠããããŸãã ããã¯ãã»ãšãã©ã®å ŽåãcomponentDidMountãã¿ãŒã³ã䜿çšããŠè¡ãããŸãããã€ãŸããReactã«ã¯ãã³ã³ããŒãã³ããDOMã«è¿œå ãããåŸã«åŒã³åºãããããã¯ããããŸãã ãã ããåé¡ããããŸããã¢ãã¡ãŒã·ã§ã³ãçµäºããåã«ãã€ã¢ãã°ããã¯ã¹ãDOMãé¢ããå ŽåããããŸãã ãããã£ãŠããããç£èŠããå¿ èŠããããŸãã
ããŒãã£ãªå ¥åã¢ãã¡ãŒã·ã§ã³ã§äœæ¥ããå Žåãã€ãŸãå ¥åã§äœããã¢ãã¡ãŒãããå Žåãã¢ãã¡ãŒã·ã§ã³ãã³ãã«ãä¿æããã³ã³ããŒãã³ããäºåã«DOMããé¢ããå Žåã¯ãã£ã³ã»ã«ããããšããå§ãããŸãã ããã¯ãVelocityãŸãã¯jQueryã®ã¢ãã¡ãŒã·ã§ã³ã©ã€ãã©ãªã䜿çšããå Žåã«å¯èœã§ãã
class Dialog extends Component{ componentDidMount(){ const node = findDOMNode(this) // animate returns a cancellable // promise-like object this._anim = animate(node, { ... }) } componentWillUnmount(){ this._anim && this._anim.cancel() } }
ã€ãŸããããã§ã¯ãã¢ãã¡ãŒã·ã§ã³ãçµäºããåã«ã³ã³ããŒãã³ããæœåºã§ããŸãã
ãã¢ãèŠã
ãã®äŸã§ã¯ããã€ã¢ãã°ããã¯ã¹ã¯ããã«æ¶ããååãšããŠãåºåãã¢ãã¡ãŒã·ã§ã³åããæ¹æ³ã¯ãããŸããã éåžžããŠã£ã³ããŠã衚瀺ãããã©ããã決å®ããç¹å®ã®ãã©ã°ãéå§ãããªãã·ã§ã³ã§ãã®ã³ã³ããŒãã³ããã¬ã³ããªã³ã°ããŸãã ãŸããã³ã³ããŒãã³ããDOMãé¢ãããšããã«ã¢ãã¡ãŒã·ã§ã³åã§ããªããããçµäºã¢ãã¡ãŒã·ã§ã³ãäœæããããšã¯ã§ããŸããã ãããŠãããªãã¯ããã«ã€ããŠäœããããå¿ èŠããããŸãã
<div> {this.state.showDialog && <Dialog />} </div>
ããã§ã¯ãåæ§ã«åäœããçµäºã¢ãã¡ãŒã·ã§ã³ãå®è¡ã§ããã©ãããŒãäœæããŸãããã ç§ãã¡ã®å Žåãç§ã¯ãããã¢ãã¡ãŒã·ã§ã³ãšåŒã³ãŸããã
<Animated> {this.state.showDialog && <Dialog />} </Animated>
åèŠçŽ ãå éšã«è¡šç€ºãããããå ¥åã¢ãã¡ãŒã·ã§ã³ãéå§ããããããæ¶ãããããã«åºåã¢ãã¡ãŒã·ã§ã³ãäœæãããšããžã±ãŒã¹ã確èªããŸãã
ã³ã³ããŒãã³ããã©ã®ããã«æ©èœããããæ³åãããšããã®ãããªã«ãŒããæã«å ¥ããŸãã ã³ã³ããŒãã³ããç¹å®ã®ç¬éã«ååšããå¯èœæ§ã®ãã4ã€ã®ç¶æ ããããŸããã¢ãã¡ãŒã·ã§ã³åããããšããç»é¢äžãçµäºãããšãããã§ã«çµäºãããšãã§ãã æåã®2ã€ã®ç¶æ ã§ã¯ãã¹ãŠãæ確ïŒå ¥åãå ¥åïŒã§ãããã³ã³ããŒãã³ããçµäºãããšãã«ç¶æ ïŒçµäºïŒãã©ãããããåé¡ã§ãã ç§ãã¡ã«äžããããåäŸã¯ããããŸããã äœããæããªããã°ãªããŸããã ãããã£ãŠãããã§ã¯ghostChildrenãšåŒã°ããããªãã¯ã䜿çšã§ããŸããã€ãŸããã¢ãã¡ãŒã·ã§ã³ãæ©èœãããŸã§èŠçŽ ãšã³ã³ããŒãã³ãã«æ®ããŸãã
const element = <Dialog size="medium" /> // => { type: Dialog, props: { size: 'medium' }, ... } const element = React.createElement(Dialog, { size: 'medium'})
JSXã®èåŸã«ã¯äœããããŸããïŒ
çµäºã¢ãã¡ãŒã·ã§ã³ãäœæããå¿ èŠãããå Žåã¯ãåãååŸããŠä¿åããç¶æ ã«è¿œå ããŠçµäºã¢ãã¡ãŒã·ã§ã³ãå®è¡ããŸãã äžè¬çã«ãã³ãŒãã¯ããŸãå¿«é©ã§ã¯ãããŸããã
æ°ããåäŸãåŸããšã圌ããå€ãã£ãããšãããããŸãã ãã®é¢æ°ã§ã¯ãã©ã®ç¶æ ã«è¡ãããããã©ã®ãããªè¿œå ãªãã·ã§ã³ãåãå ¥ããããèŠãŸãã æãéèŠãªããšã¯ãã³ã³ããŒãã³ããžã®ãªã³ã¯ã䜿çšããŠããã®äžã§åºåã¢ãã¡ãŒã·ã§ã³é¢æ°ãåŒã³åºããŠãæ£ããé·ç§»ãè¡ãããšãã§ããããšã§ãã
componentWillReceiveProps(nextProps){ // Exit transition if(this.props.children && !nextProps.children){ return this.transitionState(st.EXITING, {children: this.props.children}) } } transitionState(transitionTo, opt = {}){ // .. FSM logic .. // Wait for `this._content.animateExit()` }
åºåã¢ãã¡ãŒã·ã§ã³ããµããŒãããã¢ãã¡ãŒã·ã§ã³åããããã«ããŒã³ã³ããŒãã³ãã
äœãèµ·ãã£ãã®ãèŠãŠã¿ãŸãããïŒ 31:33ã®ãããªã§ïŒã
ãã¢ãèŠã
èå³æ·±ãããšã«ãç¶æ ã®å€æŽãéããããšãã¢ãã¡ãŒã·ã§ã³ã¯æ£ããåäœããŸãã ãã©ã³ãžã·ã§ã³ã¯å®å šã«ã¢ãã¡ãŒã·ã§ã³åããããæéå ã«ç»é¢ãé¢ããŸãã
ãã ããReactã§èšè¿°ããå Žåã¯ãreact-transition-groupã©ã€ãã©ãªã䜿çšã§ãããããçŸåšè¡ãããšããã¹ãŠè¡ãå¿ èŠã¯ãããŸããã 圌女ã¯ä»¥åReactã®ã¢ããªã³ã§ããã ç§ã¯ãæ°ããããŒãžã§ã³ã«ç§»è¡ãšåŒã°ãã䟿å©ãªãã«ããŒãããããšãæ°ã«å ¥ã£ãŠããŸãã äžè¬ã«ãããã¯å ã»ã©ãšã»ãŒåãããšããšãã¥ã¬ãŒãããäœã¬ãã«ã®ã³ã³ããŒãã³ãã§ãã
import Transition from 'react-transition-group/Transition' // `state` is 'entered', 'entering', 'exited' etc. <Transition in={isVisible} timeout={duration}> {state => <ModalDialog animationState={state} />} </Transition>
React-transition-group@2.0ã¯ãå ¥å/åºåã¢ãã¡ãŒã·ã§ã³çšã®å®£èšçãªã³ã³ããŒãã³ãã§ãã
ããããããŒãã£ã³ã³ããŒãã³ãã䜿çšããŠãç¶æ ãå€åããè€éãªã³ã³ããŒãã³ããäœæã§ããŸãã
ãã¢ãèŠã
å¥ã®äŸïŒ 32:57ã®ãããªïŒã§ã¯ãå€ãå€åãããã¹ãã°ã©ã ãäœæããŸããã å®éãããã¯ããããã¯ãŒã¯ããã³websocketããã®ç¶æ ãèªåçã«æ¥ã倧ããªã³ã³ããŒãã³ãã§ãã ã¢ãã¡ãŒã·ã§ã³ãããèªäœãå®è¡ããå¿ èŠããããããç§ã®ã³ã³ããŒãã³ãã¯éåžžã®ã³ã³ããŒãã³ãã®ããã«èŠããŸãããå éšã§ã¯ããŒãã£ã¢ãã¡ãŒã·ã§ã³ã§ç¶æ ãã¢ãã¡ãŒã·ã§ã³åããŸãã ãã®å ŽåãD3ã䜿çšããŸããã
èŠçŽ ã«ã¢ã¯ã»ã¹ã§ãããWeb APIã䜿çšããå¿ èŠãããå ŽåããããããããŒãã£ã³ã³ããŒãã³ããCanvasã§ããå Žåãªã©ããã®å Žåããã¿ãŒã³-責任ã®ååãé©çšã§ããŸãã
render(){ return <canvas /> } // Render only once! shouldComponentUpdate() { return false } componentWillReceiveProps(nextProps){ if(this.props.color != nextProps.color){ // Animate on canvas... } }
ããã¯ã䜿çšãããšãã¬ã³ããªã³ã°ã®è²¬ä»»ãå®å šã«ææ¡ã§ããŸãã ããšãã°ãCanvasãWebGLãWebAudioãªã©ã䜿çšããŸãã
ãããã£ãŠãäžåºŠã¬ã³ããªã³ã°ããŸãã ããã«ã責任ãã€ã³ã¿ãŒã»ããããç¹å¥ãªé¢æ°ã§falseãè¿ãããããã®ã³ã³ããŒãã³ããã¬ã³ããªã³ã°ããªããšèšããŸãã ããã«componentWillReceivePropsããã¯ã§ãæ¥ãããããã£ãå€æŽããããã©ããã確èªããå¿ èŠãªã¢ãã¡ãŒã·ã§ã³ãå®è¡ããŸãã ç°¡åã«èãããŸãããå®éã«ã¯ã¢ãã¡ãŒã·ã§ã³åã¯å¿ ããã䟿å©ã§ã¯ãªãããšãããããŸãã çç±ãèŠãŠã¿ãŸãããã
WebGLã§ãã®ãããªã³ã³ããŒãã³ããäœæããŸããã ããã¯äºåé¢äœã§ãã äžãã2ã€ã®ããããã£ãä»ããŠåœŒã«æž¡ããŸã=瞊ãšæšªã«ãããæ¹æ³ã ãããŠãcomponentWillReceivePropsé¢æ°å ã§ããã²ãããæ¯èŒããå¿ èŠãªå€æãè¡ããŸãã
ãã¢ãèŠã
ãã®å転ã¯å®å šã«èªç¶ãªãã®ã§ã¯ãªããèãèŠãããžã£ãŒã¯ã䌎ããŸãã ããããã¹ã ãŒãºãªã¢ãã¡ãŒã·ã§ã³ãäœæã§ããããã«ããã³ã³ãããŒã©ãŒãšåŒã°ããããªãã¯ããããŸãã
éãã¯äœã§ããïŒ äžããæ¥ããã®ãèŠãåã«ãå éšç¶æ ãæŽæ°ããã®ã§ãå転ã¯ç¬æã§ããã ãŸããã³ã³ãããŒã©ãŒã®åäœã¯å°ãç°ãªããŸãã ã³ã³ãããŒã©ãŒã¯ãå¶åŸ¡çè«ããã®æŠå¿µã§ãã ç§ãã¡ã®å Žåãããã¯P-controllerãPIDã³ã³ãããŒã©ãŒã®ç¹æ®ãªã±ãŒã¹ã§ãã ããã¯ãããããã®æãå¶åŸ¡ããé åã§ãã
ç§ãã¡ã®å Žåãããã¯åçŽãªã³ã³ãããŒã©ãŒã§ããããã®ã¢ã¯ã·ã§ã³ã¯æ¬¡ã®å¹æã«åºã¥ããŠããŸãã
// Limit delta to avoid divergence const delta = Math.min(100.0, ts - prevTs) const P = 0.001 + delta this.x = P + (this.target - x)
Pã³ã³ãããŒã©ãŒã¯ãã¹ã ãŒãºã§ã¿ã€ã ã¬ã¹ãªã¢ãã¡ãŒã·ã§ã³ã«äŸ¿å©ã§ãã
æååããããå€ïŒthis.xïŒãããããããã¿ãŒã²ããã«å€æããå¿ èŠããããŸãã é©åãªå Žæããã©ãã ãé¢ããŠããããèŠãŠãä¿æ°ãæããŠãã®ãã€ã³ãã«ç§»åããŸãã äžè¬çã«ãåŒã¯ããã¯ã®æ³åãšã¹ããªã³ã°ã®å Žåãšåãã§ãã ã¢ãã¡ãŒã·ã§ã³ã§requestAnimationFrameãšã³ã³ãããŒã©ãŒã䜿çšããå Žåããã«ã¿ãè¿œå ããã®ãæé©ã§ããããšã«æ³šæããŠãã ããã requestAnimationFrameåŒã³åºãã®éã«åãåã£ããã®ã ãããŠããã®å Žåãå¥ã®ãã©ãŠã¶ã¿ãã«åãæ¿ããŠããæ»ããšãéåžžã«å€§ããªãã«ã¿ã«ãªãããããããå¶éããŸããã ããã«ãããå€ãéåžžã«å€§ãããªããã¹ããªã³ã°ãç ŽæããŸãã ãããã£ãŠããããå¶éããããå®æ°ãæããŠäœ¿çšããŸãã
ã責任ã®ååããã¿ãŒã³ã䜿çšããŠãç©çåŠãæ±ãããšãã§ããŸãã
ãã¢ãèŠã
æ±ããã³ã³ããŒãã³ããäœæããå Žåã次ã®ããšã確èªããå¿ èŠããããŸãã
- ã³ã³ããŒãã³ãã«ã¯ããããªã€ã³ã¿ãŒãã§ãŒã¹ããããŸããã
- å¯äœçšã¯é ãããŠãã
ååãšããŠããããã®ã«ãŒã«ã¯å察æ¹åã«æ©èœããŸãã ããšãã°ãã¹ããŒããã«ã¢ããªã±ãŒã·ã§ã³ã§äœæ¥ããã¢ãã¡ãŒã·ã§ã³ãå®è¡ããå¿ èŠããããŸãã 次ã«ãäœããã®ããªã¬ãŒïŒç¶æ ã®å€æŽãªã©ïŒãèšå®ããã³ã³ããŒãã³ãå ã§ãã®å€æŽãç£èŠããŠãã¢ãã¡ãŒã·ã§ã³ãéå§ããå¿ èŠããããŸãã
import { Actuator, actuate } from 'redux-actuator' // Inside the component <Actuator on={{ animateBadge: this.animateBadge }} /> // Where the business logic is store.dispatch(actuate('animateBadge')) store.dispatch(actuate('highlighUser', { id: 1 }))
ãã®ãã¿ãŒã³ã¯ãã°ããŒãã«ç¶æ ãéä¿¡ããå¯äžã®æ¹æ³ã§ããReduxã¢ããªã±ãŒã·ã§ã³ã§äŸ¿å©ã«äœ¿çšãããŸãã
ç§ãã¡ã¯ãã°ãã°Reduxãšé£æºããã¢ãã¡ãŒã·ã§ã³ãäœæããããã«ãçŸåšã®ã¢ãžã¥ãŒã«ã«äŸåããªããã¹ãŠã®ãã®ãã¢ããªã±ãŒã·ã§ã³ã§ã¢ãã¡ãŒã·ã§ã³åãããããã«ããå¿ èŠãããå ŽåããããŸãã
ç§ã¯å°ããªãªãŒãã³ãœãŒã¹ãŠãŒãã£ãªãã£redux-actuatorãå ¬éããŸããã ã³ã³ããŒãã³ãå ã§ã€ãã³ããããªã¬ãŒã§ãããããªã¢ã¯ãã¥ãšãŒã¿ããããŸãã
ãã¢ãèŠã
ã©ã®ããã«èŠãããã 40ïŒ27ãããããªãèŠãããšãã§ããŸãã äºå®ãç§ãã¡ã¯èªåã®ç¶æ ã«ããç¹å®ã®ããŒãåãããããå¥ã®ããŒã«å€æŽããŸãã ã€ãŸããç¶æ ãæ¬åœã«å€åããããšã確èªããå¿ èŠããããŸãã ã¢ã¯ãã¥ãšãŒã¿ã®å Žåã次ã®ããã«é²ã¿ãŸããç¹å®ã®id event'aãååŸããè¡çªãé¿ããããã«çŸåšã®æå»ãšã«ãŠã³ã¿ãŒããäœæããŸãã ãããã£ãŠãã¢ãã¡ãŒã·ã§ã³ãåŒã³åºãããšãã§ããŸãã
ãã¢ãèŠã
è€éãªã¢ãã¡ãŒã·ã§ã³ãäœæããæ¹æ³ã«ã€ããŠè³ªåããããããããŸããïŒäŸã¯41:27ã®ãããªã§èŠãããšãã§ããŸãïŒã ç§ã¯ãããèšããŸããã³ãŒãã®ã¢ãã¡ãŒã·ã§ã³ã¯ã»ãšãã©ãã€ããããã«èŠããŸãã â . , , , . , , , item'. , , , FLIP. FLIP, . FLIP â , , , . , , DOM, . , , DOM. , , React , , . â .
. . . , . : , DOM. , , . Canvas, WebGL, . , .
, .
åç §è³æ
:
React, 16.3, componentWillReceiveProps deprecated, .. . , state props, . React componentWillReceiveProps getDerivedStateFromProps. , , props state , .
.
, , npm , . , react-canvas.
high-performant React Native. API . <Animated />, . , . : RN, setState react-native â , , RN .. «». ã€ãŸã JS , View ( iOS) . low-level . <Animated />, , , . , , .. , â JS, , ( ). : https://github.com/motiz88/animated.macro
API, web . , . , «--». - React - .
, . JS , , HolyJS 2018 Piter , :
- Mining crypto in browser: GPU, WebAssembly, JavaScript and all the good things to try ( , Evolution Gaming)
- JavaScript debugging using Chrome DevTools ( , Google)
- VS ( , )
- React Native Deep Inside ( , Lowl.io)