React.js と動くもの、鳴るもの

React.js と動くもの、鳴るもの

UIT meetup vol.7 で発表した内容です!
https://uit.connpass.com/event/138084/

↓の記事の内容が大筋になっています!
Webでどこまで遊べるか試してみた
https://qiita.com/Leonardo-mbc/items/3c365836acfd71a55185

React ライフサイクルの demo
https://youtu.be/VXeuXkEJ76U

requestAnimationFrame の demo
https://youtu.be/40NbLRaTT6M

39b48efe3422d1c8a48f28aad53e209a?s=128

LeonardoKen Orihara

July 24, 2019
Tweet

Transcript

  1. 3FBDUKTͱ ಈ͘΋ͷɺ ໐Δ΋ͷ

  2.  XIPBNJ ϓϩϑΟʔϧ ંݪ ϨΦφϧυݡ -FPOBSEP,FO 0SJIBSB -*/&גࣜձࣾ 6*5ࣨ 6TFS*OUFSGBDF

    5FDIOPMPHZUFBN!-*/&DPSQ
  3. None
  4. ͜ΕΛ۷ΓԼ͛ͨ࿩͠·͢

  5.  "HFOEB ͳʹΛ࿩͔͢ ϑϩϯτΤϯυͰΦʔιυοΫεͳ 3FBDUKT 3FEVYͷ؀ڥͰ 8FC(- UISFFKT ͱ͔ 8FC"VEJP"1*

    Λ࢖ͬͨ 5JQTͳΜ͔Λ঺հ ಈ͘΋ͷ ໐Δ΋ͷ
  6. ಈ͘΋ͷ

  7. 5JNJOHPGSFOEFSJOH ϨϯμϦϯάͷλΠϛϯά

  8. None
  9.  3FBDU 8FC(-SFOEFSFS 3FBDUͱ 8FC(-SFOEFSFS composer.render(); Λ܁Γฦ͠ݺͿ ௨ৗͷ UISFFKT ͷϨϯμϦϯά

    3FBDU 3FEVYͰϧʔϓॲཧͲ͏͠Α͏ʁ
  10.  3FBDU 8FC(-SFOEFSFS 3FBDUͱ 8FC(-SFOEFSFS લఏɿ ΦϒδΣΫτ͕͙Δ͙Δͯ͠Δ͚ͩͷ8FCΞϓϦ͸جຊͳ͍

  11.  #VJMEPO3FBDUMJGFDZDMF 3FBDUͷϥΠϑαΠΫϧʹࡌͤͯΈΔ export class AnimationGround extends React.Component<Props, State> {

    constructor(props: Props, state: State) { super(props); this.state = { position: { x: 0, y: 0, z: 0 } } } componentDidUpdate() { composer.render(); const newPosition = ... this.setState({ position: newPosition }); } } ٖࣅίʔυ
  12. ΊͪΌͪ͘Όॏ͍ GQTग़ͤͳ͍ IUUQTZPVUVCF79FV9L&+6

  13.  #VJMEPO3FBDUDPNQPOFOU 3FBDUͷίϯϙʔωϯτͰಈ͔ͯ͠ΈΔ export class AnimationGround extends React.Component<Props, State> {

    constructor(props: Props, state: State) { super(props); this.position = { x: 0, y: 0, z: 0 }; } componentDidMount() { requestAnimationFrame(this.renderAnim); } renderAnim = () => { this.position = ... composer.render(); } } ٖࣅίʔυ
  14. state ໰୊ͳ͘ಈ͘ requestAnimationFrame IUUQTZPVUVCF79FV9L&+6 IUUQTZPVUVCF/C-3B55.

  15. • ࡾ֯ؔ਺ͷԋࢉ͕ϝΠϯ • ಉ͡ଟ߲ࣜͰɺຖϑϨʔϜಉ͡ܭࢉ for (let i = 0; i

    < freqsLength; i++) { const thetaMuli = theta * i; const thetaPlusPI = thetaMuli + Math.PI / 2; const thetaSubPI = thetaMuli - Math.PI / 2; spectrumVertices[2].x = value * Math.cos(thetaMuli) + halfWidth * Math.cos(thetaPlusPI); spectrumVertices[2].y = value * Math.sin(thetaMuli) + halfWidth * Math.sin(thetaPlusPI); spectrumVertices[3].x = value * Math.cos(thetaMuli) + halfWidth * Math.cos(thetaSubPI); spectrumVertices[3].y = value * Math.sin(thetaMuli) + halfWidth * Math.sin(thetaSubPI); spectrumGeometry.verticesNeedUpdate = true; } ٖࣅίʔυ  )PXUPDBMDVMBUFUIFTQFDUSVN ͦ΋ͦ΋ࠓճͷॲཧͰͳʹΛ΍͍ͬͯΔ͔
  16. • ࡾ֯ؔ਺ͷԋࢉ͕ϝΠϯ • ಉ͡ଟ߲ࣜͰɺຖϑϨʔϜಉ͡ܭࢉ for (let i = 0; i

    < freqsLength; i++) { const thetaMuli = theta * i; const thetaPlusPI = thetaMuli + Math.PI / 2; const thetaSubPI = thetaMuli - Math.PI / 2; spectrumVertices[2].x = value * Math.cos(thetaMuli) + halfWidth * Math.cos(thetaPlusPI); spectrumVertices[2].y = value * Math.sin(thetaMuli) + halfWidth * Math.sin(thetaPlusPI); spectrumVertices[3].x = value * Math.cos(thetaMuli) + halfWidth * Math.cos(thetaSubPI); spectrumVertices[3].y = value * Math.sin(thetaMuli) + halfWidth * Math.sin(thetaSubPI); spectrumGeometry.verticesNeedUpdate = true; } ٖࣅίʔυ  )PXUPDBMDVMBUFUIFTQFDUSVN ͦ΋ͦ΋ࠓճͷॲཧͰͳʹΛ΍͍ͬͯΔ͔ [2].x = 振幅 * cos(角度) + 幅/2 * cos(角度+Δ); [2].y = 振幅 * sin(角度) + 幅/2 * sin(角度+Δ); [3].x = 振幅 * cos(角度) + 幅/2 * cos(角度-Δ); [3].y = 振幅 * sin(角度) + 幅/2 * sin(角度-Δ); [0] === [1] こんな四角形 ͜ΕΛͲ͏ߴ଎Խ͢Δ͔
  17. 8FC"TTFNCMZΛ࢖͏ #include <cmath> #include <iostream> using namespace std; int calcSpectrum(int

    freqsLength) { static const double PI = 3.141592653589793; for (int i = 0; i < freqsLength; i++) { double thetaMuli = theta * i; double thetaPlusPI = thetaMuli + PI / 2.0; double thetaSubPI = thetaMuli - PI / 2.0; double v2x = value * cos(thetaMuli) + halfWidth * cos(thetaPlusPI); double v2y = value * sin(thetaMuli) + halfWidth * sin(thetaPlusPI); double v3x = value * cos(thetaMuli) + halfWidth * cos(thetaSubPI); double v3y = value * sin(thetaMuli) + halfWidth * sin(thetaSubPI); } }  'VSUIFSGBTUFS ͞ΒͳΔߴ଎Խ ٖࣅίʔυ
  18. • ͦ΋ͦ΋ඳըॲཧΛ $16Ͱܭࢉ͢ΔϝϦοτ͕ͳ͍ • γΣʔμʔʢ(-4-ʣͰܭࢉ͢Δ  'BTUFTUXBZ ͞ΒͳΔɺ͞ΒͳΔߴ଎Խ コード考え中 ܽ఺

    ࠓ·ͰͷྲྀΕͰ͸ͳ͘ɺ৽ͨʹίʔυΛॻ͔ͳ͍ͱ͍͚ͳ͍
  19. $PNCJOBUJPOXJUI)5.- ϚʔΫΞοϓͱͷ૊Έ߹Θͤ

  20.  (SPVOEHMBTTFGGFDU ͢ΓΨϥεޮՌ ϦϦʔε൛ ͢ΓΨϥεޮՌͳ͠ 8FC(-Ͱ΅͔͠Λ͚ͭͣɺ$44ͷ GJMUFSΛ࢖༻ :local(.container) { filter:

    blur(3px); }
  21.  (SPVOEHMBTTFGGFDU ͢ΓΨϥεޮՌ ϦϦʔε൛ ͢ΓΨϥεޮՌͳ͠ ϝϦοτ • (-ଆ͕Ͱ΍Γ͍ͨࣄͱԋग़͕෼཭Ͱ͖ͯγϯϓϧʹͳΔ • $44΋ϞϊʹΑͬͯ͸(16ॲཧ͕Ͱ͖Δ

    • ϢʔβʔೖྗʹΑͬͯԋग़Λม͍͑ͨͱ͖ʹ$44ͷΫϥεΛ ม͑Δैདྷͷ΍ΓํͰ௨༻͢Δʢ8FC(-ଆʹύϥϝʔλΛૹΒͳͯ͘ྑ͍ʣ
  22.  5SBOTJUJPO τϥϯδγϣϯ 8FCΞϓϦશମͷը໘స׵͕͞ΕͯΔΑ͏ͳԋग़

  23.  5SBOTJUJPO τϥϯδγϣϯ

  24.  5SBOTJUJPO τϥϯδγϣϯ ͕͜͜τϥϯδγϣϯ͍ͯ͠Δ ࠷લ໘ͷཁૉͳͷͰɺ ΫϦοΫ൑ఆ΋ୣ͍͍͑ͯײ͡

  25.  5SBOTJUJPO τϥϯδγϣϯ uniform float mixRatio; uniform sampler2D tDiffuse1; uniform

    sampler2D tDiffuse2; varying vec2 vUv; void main() { vec4 tex1 = texture2D(tDiffuse1, vUv); vec4 tex2 = texture2D(tDiffuse2, vUv); gl_FragColor = mix(tex2, tex1, mixRatio); } ٖࣅίʔυ +4ଆ͔Β mixRatio Λ౉͢͜ͱͰɺ̎ͭͷγʔϯΛ੾ସ͑Δ
  26. ໐Δ΋ͷ

  27. 8IFSFUPTUPSF"VEJPOPEFT "VEJPOPEFΛ֨ೲ͢Δ৔ॴ

  28.  8IFSFUPTUPSF"VEJPOPEFT "VEJPOPEFΛ֨ೲ͢Δ৔ॴ export const initialState: State = { system:

    { sound: { sources: { titleMusic: null, unlockedSound: null, musicSamples: {}, }, context: null, systemGainNode: null, cueAGainNode: null, cueBGainNode: null, analyzerNode: null, filterNode: null, analyzerParams: null, } }, ... } ٖࣅίʔυ ݁ہશ෦ 4UPSFʹ࣋ͭ
  29. export function reducer(state: SystemState = initialState.system, action: ActionTypes): SystemState {

    switch (action.type) { case CREATE_SOUNDS_LINE: const context = new AudioContext(); const cueAGainNode = context.createGain(); const cueBGainNode = context.createGain(); const analyzerNode = context.createAnalyser(); cueAGainNode.connect(analyzerNode); cueBGainNode.connect(analyzerNode); analyzerNode.connect(context.destination); return { ...state, sound: { ...state.sound, context, cueAGainNode, cueBGainNode, analyzerNode } }; } } ٖࣅίʔυ 3FEVDFSͰ૊Έཱͯ  8IFSFUPTUPSF"VEJPOPEFT "VEJPOPEFΛ֨ೲ͢Δ৔ॴ
  30. // Action export const sampleMusicPlay = ({ musicId }: SampleMusicPlayPayload):

    SampleMusicPlay => ({ type: SAMPLE_MUSIC_PLAY, payload: { musicId }, }); // redux-saga takeEvery(SAMPLE_MUSIC_PLAY, function*(action: SampleMusicPlay) { const { musicId } = action.payload; const store = yield select(); const sound = store.system.sound as Sound; const { samples } = sound.sources; samples[musicId].start(0, 0); }) ٖࣅίʔυ "DUJPOˠ 4BHBܦ༝Ͱ࠶ੜ  8IFSFUPTUPSF"VEJPOPEFT "VEJPOPEFΛ֨ೲ͢Δ৔ॴ
  31. "QMBZFSDPNQPOFOU ࠶ੜ༻ͷίϯϙʔωϯτ

  32. #VGGFS/PEF ͸̍౓࠶ੜͨ͠Βഁغ͢Δඞཁ͕͋Δ  "QMBZFSDPNQPOFOU ࠶ੜ༻ͷίϯϙʔωϯτ 4UPSFͰ࠶ੜ͢Δ͚Ͳ ίϯϙʔωϯτͷৼΔ෣͍ͱಉظ͍ͨ͠

  33. export class SamplePlayer extends React.Component<SamplePlayerProps, SamplePlayerState> { componentDidMount() { this.props.sampleMusicPlay();

    } componentWillUnmount() { this.props.sampleMusicFadeOut(); } render() { return <div />; } } ٖࣅίʔυ  "QMBZFSDPNQPOFOU ࠶ੜ༻ͷίϯϙʔωϯτ
  34. takeEvery(SAMPLE_MUSIC_FADE_OUT, function*(action: SampleMusicFadeOut) { const fadeTime = action.payload.duration || 250;

    new TWEEN.Tween(gainNode.gain) .to({ value: 0.0 }, fadeTime) .onComplete(() => { try { bufferNode.buffer.stop(); } catch (e) {} }).start(); yield put(remakeSampleSounds({ bufferNode, gainNode }); }) ٖࣅίʔυ  "QMBZFSDPNQPOFOU ࠶ੜ༻ͷίϯϙʔωϯτ
  35. takeEvery(SAMPLE_MUSIC_FADE_OUT, function*(action: SampleMusicFadeOut) { const fadeTime = action.payload.duration || 250;

    new TWEEN.Tween(gainNode.gain) .to({ value: 0.0 }, fadeTime) .onComplete(() => { try { bufferNode.buffer.stop(); } catch (e) {} }).start(); yield put(remakeSampleSounds({ bufferNode, gainNode }); }) ٖࣅίʔυ  "QMBZFSDPNQPOFOU ࠶ੜ༻ͷίϯϙʔωϯτ const buffer = bufferNode.buffer; const bufferNode = context.createBufferSource(); bufferNode.buffer = buffer; bufferNode.loop = true; bufferNode.connect(gainNode); ٖࣅίʔυ 3FNBLF෦෼
  36. $PODMVTJPO ࠓ೔࿩ͨ͜͠ͱ

  37.  $PODMVTJPO ࠓ೔࿩ͨ͜͠ͱ 3FBDUKT 3FEVYͷ؀ڥͰ 8FC(- UISFFKT ͱ͔ 8FC"VEJP"1* Λ࢖ͬͨ

    5JQT ಈ͘΋ͷ ໐Δ΋ͷ
  38.  $PODMVTJPO ࠓ೔࿩ͨ͜͠ͱ • ϨϯμϦϯάͷλΠϛϯάʹ͍ͭͯ • ϚʔΫΞοϓͱ૊Έ߹ΘͤͯදݱͰ͖Δྫ • "VEJPOPEFͷ֨ೲ৔ॴʹ͍ͭͯ •

    8FC"VEJPͱίϯϙʔωϯτ؅ཧ