ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [React / Three] Events
    Reactjs/Threejs 2023. 1. 17. 06:30

    1. three.js에서 raycast 메서드(mesh, line etc)를 구현하는 개체는 이벤트를 선언하여 상호 작용할 수 있다. onUpdate는 객체가 새로운 props가 가지면 불러온다.

     

    2.  Events

    <mesh
      onClick={(e) => console.log('click')}
      onContextMenu={(e) => console.log('context menu')}
      onDoubleClick={(e) => console.log('double click')}
      onWheel={(e) => console.log('wheel spins')}
      onPointerUp={(e) => console.log('up')}
      onPointerDown={(e) => console.log('down')}
      onPointerOver={(e) => console.log('over')}
      onPointerOut={(e) => console.log('out')}
      onPointerEnter={(e) => console.log('enter')} // see note 1
      onPointerLeave={(e) => console.log('leave')} // see note 1
      onPointerMove={(e) => console.log('move')}
      onPointerMissed={() => console.log('missed')}
      onUpdate={(self) => console.log('props have been updated')}
    />

     

    3. Event Data

    ({
      ...DomEvent                   // All the original event data
      ...Intersection                 // All of Three's intersection data - see note 2
      intersections: Intersection[]    // The first intersection of each intersected object
      object: Object3D              // The object that was actually hit
      eventObject: Object3D         // The object that registered the event
      unprojectedPoint: Vector3     // Camera-unprojected point
      ray: Ray                      // The ray that was used to strike the object
      camera: Camera                // The camera that was used in the raycaster
      sourceEvent: DomEvent         // A reference to the host event
      delta: number                 // Distance between mouse down and mouse up event in pixels
    }) => ...

     

    4. Event propagation(bubbling)

     객체가 3D에서 서로 가릴 수 있기 때문에 가장 가까운 객체뿐만 아니라 교차되는 모든 객체가 포함된다. 이벤트는 먼저 카메라에 가장 가까운 객체로 전달된 다음 DOM에서와 같이 조상을 통해 버블링 된다. 그런 다음 가장 가까운 다음 객체로 전달된 다음 조상에게 전달되는 식이다. event.stopPropagation() 이 이벤트가 버블링 되는 것을 중지하고 더 멀리 있는 객체로 전달되는 것도 중지한다. 객체가 뒤에 있는 객체의 포인터 이벤트를 차단하도록 하려면 아래와 같은 이벤트 처리기가 있어야 한다.

    onPointerOver={e => {
      e.stopPropagation()
      // ...
    }}

     

    5. Pointer Capture

     이벤트는 교차된 모든 객체로 이동하기 때문에 포인터 캡쳐도 다르게 작동한다. 다음을 통해 setPointerCapture 및 releasePointerCapture 메서드에 엑세스할 수 있다.

    onPointerDown={e => {
      // Only the mesh closest to the camera will be processed
      e.stopPropagation()
      // You may optionally capture the target
      e.target.setPointerCapture(e.pointerId)
    }}
    onPointerUp={e => {
      e.stopPropagation()
      // Optionally release capture
      e.target.releasePointerCapture(e.pointerId)
    }}

     

    6. useState와 Event로 Hover시 색깔과 크기 변경, PointerDown시 Rotation 예제

    import { useRef, useState } from 'react'
    import { useFrame } from '@react-three/fiber'
    
    export default function Box(props) {
      const ref = useRef()
      const [hovered, setHover] = useState(false)
      const [rotate, setRotate] = useState(false)
      useFrame((_, delta) => {
        if (rotate) {
          ref.current.rotation.x += 1 * delta
          ref.current.rotation.y += 0.5 * delta
        }
      })
    
      return (
        <mesh
          {...props}
          ref={ref}
          scale={hovered ? [1.1, 1.1, 1.1] : [1, 1, 1]}
          onPointerDown={() => setRotate(!rotate)}
          onPointerOut={() => setHover(false)}
          onPointerOver={() => setHover(true)}
          onUpdate={(self) => console.log(self)}>
          <boxGeometry />
          <meshBasicMaterial color={hovered ? 0xff0000 : 0x00ff00} wireframe />
        </mesh>
      )
    }

     

    7. useMemo는 필요할 때만 내부 기능이 실행되도록 한다. 리소스를 많이 사용한는 기능을 불필요하게 다시 실행하지 않으려는 경우 사용한다. 아래는 Box와 Sphere 전환을 useMemo를 사용하여 uuid가 일정하게 유지하는 것을 보여준다.

    import { useRef, useState, useEffect, useMemo } from 'react'
    import { useFrame } from '@react-three/fiber'
    import * as THREE from 'three'
    
    export default function Box(props) {
      const ref = useRef()
      const [count, setCount] = useState(0)
      const geometry = useMemo(
        () => [new THREE.BoxGeometry(), new THREE.SphereGeometry(0.785398)],
        []
      )
    
      useEffect(() => {
        console.log(ref.current.geometry.uuid)
      })
    
      useFrame((_, delta) => {
        ref.current.rotation.x += delta
        ref.current.rotation.y += 0.5 * delta
      })
    
      return (
        <mesh
          {...props}
          ref={ref}
          onPointerDown={() => setCount((count + 1) % 2)}
          geometry={geometry[count]}
        >
          <meshBasicMaterial color={'lime'} wireframe />
        </mesh>
      )
    }
Designed by Tistory.