AI студия Владимира Ломтева
УСЛУГИПРОЕКТЫСТАТЬИБАЗА ЗНАНИЙМаркетплейсПолезные сервисы

Оставьте заявку,
чтобы обсудить проект

Напишите ваш вопрос, не забудьте указать телефон. Мы перезвоним и все расскажем.

Контакты

Москва

Работаем по всей России
и миру (онлайн)

+7 (999) 760-24-41

Ежедневно с 9:00 до 21:00

lamooof@gmail.com

По вопросам сотрудничества

TelegramWhatsApp

Есть предложение?

Напишите нам в мессенджеры

© 2025 AI студия Владимира Ломтева

Политика конфиденциальностиСогласие на обработку ПДн|ИНН 623412173261

    R3f Interaction

    React Three Fiber interaction pointer events, controls, gestures, selection. Use when handling user input, implementing click detection, adding camera controls, or creating interactive 3D experiences.

    Скиллы для операций#GitHub#enzed/r3f-skills#skills.sh
    Скачивания
    0
    В избранном
    0
    Комментарии
    0
    Просмотры
    2

    Установить скилл

    Добавьте инструмент одной командой или скачайте проверенный архив версии.

    npx skills add enzed/r3f-skills --skill r3f-interaction
    Скачать ZIP
    Версия
    1.0.0+8ce187a05a53
    Автор
    Владимир Ломтев
    Репозиторий
    enzed/r3f-skills
    GitHub: enzed/r3f-skills

    Как установить

    1. 1Скопируйте команду из блока установки.
    2. 2Запустите её в терминале из каталога проекта.

    Документация

    React Three Fiber Interaction

    Quick Start

    import { Canvas } from '@react-three/fiber'
    import { OrbitControls } from '@react-three/drei'
    
    function InteractiveMesh() {
      return (
        <mesh
          onClick={(e) => console.log('Clicked!', e.point)}
          onPointerOver={(e) => console.log('Hover')}
          onPointerOut={(e) => console.log('Unhover')}
        >
          <boxGeometry />
          <meshStandardMaterial color="hotpink" />
        </mesh>
      )
    }
    
    export default function App() {
      return (
        <Canvas>
          <ambientLight />
          <InteractiveMesh />
          <OrbitControls />
        </Canvas>
      )
    }
    

    Pointer Events

    R3F provides built-in pointer events on mesh elements.

    Available Events

    <mesh
      // Click events
      onClick={(e) => {}}           // Click (pointerdown + pointerup on same object)
      onDoubleClick={(e) => {}}     // Double click
      onContextMenu={(e) => {}}     // Right click
    
      // Pointer events
      onPointerDown={(e) => {}}     // Pointer pressed
      onPointerUp={(e) => {}}       // Pointer released
      onPointerMove={(e) => {}}     // Pointer moved while over object
      onPointerOver={(e) => {}}     // Pointer enters object
      onPointerOut={(e) => {}}      // Pointer leaves object
      onPointerEnter={(e) => {}}    // Pointer enters object (no bubbling)
      onPointerLeave={(e) => {}}    // Pointer leaves object (no bubbling)
      onPointerMissed={(e) => {}}   // Click that missed all objects
    
      // Wheel
      onWheel={(e) => {}}           // Mouse wheel
    
      // Touch
      onPointerCancel={(e) => {}}   // Touch cancelled
    >
      <boxGeometry />
      <meshStandardMaterial />
    </mesh>
    

    Event Object

    function InteractiveMesh() {
      const handleClick = (event) => {
        // Stop propagation to parent objects
        event.stopPropagation()
    
        // Event properties
        console.log({
          object: event.object,           // The mesh that was clicked
          point: event.point,             // World coordinates of intersection
          distance: event.distance,       // Distance from camera
          face: event.face,               // Intersected face
          faceIndex: event.faceIndex,     // Face index
          uv: event.uv,                   // UV coordinates at intersection
          normal: event.normal,           // Face normal
          camera: event.camera,           // Current camera
          ray: event.ray,                 // Ray used for intersection
          intersections: event.intersections, // All intersections
          nativeEvent: event.nativeEvent, // Original DOM event
          delta: event.delta,             // Click distance (useful for drag detection)
        })
      }
    
      return (
        <mesh onClick={handleClick}>
          <boxGeometry />
          <meshStandardMaterial />
        </mesh>
      )
    }
    

    Hover Effects

    import { useState } from 'react'
    
    function HoverableMesh() {
      const [hovered, setHovered] = useState(false)
    
      return (
        <mesh
          onPointerOver={(e) => {
            e.stopPropagation()
            setHovered(true)
            document.body.style.cursor = 'pointer'
          }}
          onPointerOut={(e) => {
            setHovered(false)
            document.body.style.cursor = 'default'
          }}
          scale={hovered ? 1.2 : 1}
        >
          <boxGeometry />
          <meshStandardMaterial color={hovered ? 'hotpink' : 'orange'} />
        </mesh>
      )
    }
    

    Selective Raycasting

    // Disable raycasting for specific objects
    <mesh raycast={() => null}>
      <boxGeometry />
      <meshStandardMaterial />
    </mesh>
    
    // Or use layers
    <mesh
      layers={1}  // Only raycast against layer 1
      onClick={() => console.log('clicked')}
    >
      <boxGeometry />
      <meshStandardMaterial />
    </mesh>
    

    Camera Controls

    OrbitControls

    import { OrbitControls } from '@react-three/drei'
    
    function Scene() {
      return (
        <>
          <mesh>
            <boxGeometry />
            <meshStandardMaterial />
          </mesh>
    
          <OrbitControls
            makeDefault                    // Use as default controls
            enableDamping                  // Smooth movement
            dampingFactor={0.05}
            enableZoom={true}
            enablePan={true}
            enableRotate={true}
            autoRotate={false}
            autoRotateSpeed={2}
            minDistance={2}
            maxDistance={50}
            minPolarAngle={0}              // Top limit
            maxPolarAngle={Math.PI / 2}    // Horizon limit
            minAzimuthAngle={-Math.PI / 4} // Left limit
            maxAzimuthAngle={Math.PI / 4}  // Right limit
            target={[0, 1, 0]}             // Look-at point
          />
        </>
      )
    }
    

    OrbitControls with Ref

    import { OrbitControls } from '@react-three/drei'
    import { useRef, useEffect } from 'react'
    
    function Scene() {
      const controlsRef = useRef()
    
      useEffect(() => {
        // Access controls methods
        if (controlsRef.current) {
          controlsRef.current.reset()
          controlsRef.current.target.set(0, 1, 0)
          controlsRef.current.update()
        }
      }, [])
    
      return <OrbitControls ref={controlsRef} />
    }
    

    MapControls

    Top-down map-style controls.

    import { MapControls } from '@react-three/drei'
    
    <MapControls
      enableDamping
      dampingFactor={0.05}
      screenSpacePanning={false}  // Pan in world space
      maxPolarAngle={Math.PI / 2}
    />
    

    FlyControls

    Free-flying camera controls.

    import { FlyControls } from '@react-three/drei'
    
    <FlyControls
      movementSpeed={10}
      rollSpeed={Math.PI / 24}
      dragToLook
    />
    

    FirstPersonControls

    FPS-style controls.

    import { FirstPersonControls } from '@react-three/drei'
    
    <FirstPersonControls
      movementSpeed={10}
      lookSpeed={0.1}
      lookVertical
    />
    

    PointerLockControls

    Lock pointer for FPS games.

    import { PointerLockControls } from '@react-three/drei'
    import { useRef } from 'react'
    
    function Scene() {
      const controlsRef = useRef()
    
      return (
        <>
          <PointerLockControls ref={controlsRef} />
    
          {/* Click to lock pointer */}
          <mesh onClick={() => controlsRef.current?.lock()}>
            <planeGeometry args={[10, 10]} />
            <meshBasicMaterial color="green" />
          </mesh>
        </>
      )
    }
    

    CameraControls

    Advanced camera controls with smooth transitions.

    import { CameraControls } from '@react-three/drei'
    import { useRef } from 'react'
    
    function Scene() {
      const controlsRef = useRef()
    
      const focusOnObject = async () => {
        // Smooth transition to target
        await controlsRef.current?.setLookAt(
          5, 3, 5,    // Camera position
          0, 0, 0,    // Look-at target
          true        // Enable transition
        )
      }
    
      return (
        <>
          <CameraControls ref={controlsRef} />
    
          <mesh onClick={focusOnObject}>
            <boxGeometry />
            <meshStandardMaterial color="red" />
          </mesh>
        </>
      )
    }
    

    TrackballControls

    Unconstrained rotation controls.

    import { TrackballControls } from '@react-three/drei'
    
    <TrackballControls
      rotateSpeed={2.0}
      zoomSpeed={1.2}
      panSpeed={0.8}
      staticMoving={true}
    />
    

    ArcballControls

    Arc-based rotation controls.

    import { ArcballControls } from '@react-three/drei'
    
    <ArcballControls
      enableAnimations
      dampingFactor={25}
    />
    

    Transform Controls

    Gizmo for moving/rotating/scaling objects.

    import { TransformControls, OrbitControls } from '@react-three/drei'
    import { useRef, useState } from 'react'
    
    function Scene() {
      const meshRef = useRef()
      const [mode, setMode] = useState('translate')
      const orbitRef = useRef()
    
      return (
        <>
          <OrbitControls ref={orbitRef} makeDefault />
    
          <TransformControls
            object={meshRef}
            mode={mode}  // 'translate' | 'rotate' | 'scale'
            space="local"  // 'local' | 'world'
            onMouseDown={() => {
              // Disable orbit while transforming
              if (orbitRef.current) orbitRef.current.enabled = false
            }}
            onMouseUp={() => {
              if (orbitRef.current) orbitRef.current.enabled = true
            }}
          />
    
          <mesh ref={meshRef}>
            <boxGeometry />
            <meshStandardMaterial color="orange" />
          </mesh>
    
          {/* Mode switching buttons in HTML */}
          <div className="controls">
            <button onClick={() => setMode('translate')}>Move</button>
            <button onClick={() => setMode('rotate')}>Rotate</button>
            <button onClick={() => setMode('scale')}>Scale</button>
          </div>
        </>
      )
    }
    

    PivotControls

    Alternative transform gizmo with pivot point.

    import { PivotControls } from '@react-three/drei'
    
    function Scene() {
      return (
        <PivotControls
          anchor={[0, 0, 0]}         // Anchor point
          depthTest={false}          // Always visible
          lineWidth={2}              // Axis line width
          axisColors={['red', 'green', 'blue']}
          scale={1}                  // Gizmo scale
          fixed={false}              // Fixed screen size
        >
          <mesh>
            <boxGeometry />
            <meshStandardMaterial color="orange" />
          </mesh>
        </PivotControls>
      )
    }
    

    Drag Controls

    useDrag from @use-gesture/react

    npm install @use-gesture/react
    
    import { useDrag } from '@use-gesture/react'
    import { useSpring, animated } from '@react-spring/three'
    import { useThree } from '@react-three/fiber'
    
    function DraggableMesh() {
      const { size, viewport } = useThree()
      const aspect = size.width / viewport.width
    
      const [spring, api] = useSpring(() => ({
        position: [0, 0, 0],
        config: { mass: 1, tension: 280, friction: 60 }
      }))
    
      const bind = useDrag(({ movement: [mx, my], down }) => {
        api.start({
          position: down ? [mx / aspect, -my / aspect, 0] : [0, 0, 0]
        })
      })
    
      return (
        <animated.mesh {...bind()} position={spring.position}>
          <boxGeometry />
          <meshStandardMaterial color="hotpink" />
        </animated.mesh>
      )
    }
    

    DragControls (Drei)

    import { DragControls, OrbitControls } from '@react-three/drei'
    import { useRef } from 'react'
    
    function Scene() {
      const meshRef = useRef()
      const orbitRef = useRef()
    
      return (
        <>
          <OrbitControls ref={orbitRef} makeDefault />
    
          <DragControls
            onDragStart={() => {
              if (orbitRef.current) orbitRef.current.enabled = false
            }}
            onDragEnd={() => {
              if (orbitRef.current) orbitRef.current.enabled = true
            }}
          >
            <mesh ref={meshRef}>
              <boxGeometry />
              <meshStandardMaterial color="orange" />
            </mesh>
          </DragControls>
        </>
      )
    }
    

    Keyboard Controls

    KeyboardControls (Drei)

    import { KeyboardControls, useKeyboardControls } from '@react-three/drei'
    import { useFrame } from '@react-three/fiber'
    import { useRef } from 'react'
    
    // Define key mappings
    const keyMap = [
      { name: 'forward', keys: ['ArrowUp', 'KeyW'] },
      { name: 'backward', keys: ['ArrowDown', 'KeyS'] },
      { name: 'left', keys: ['ArrowLeft', 'KeyA'] },
      { name: 'right', keys: ['ArrowRight', 'KeyD'] },
      { name: 'jump', keys: ['Space'] },
      { name: 'sprint', keys: ['ShiftLeft'] },
    ]
    
    function Player() {
      const meshRef = useRef()
      const [, getKeys] = useKeyboardControls()
    
      useFrame((state, delta) => {
        const { forward, backward, left, right, jump, sprint } = getKeys()
    
        const speed = sprint ? 10 : 5
    
        if (forward) meshRef.current.position.z -= speed * delta
        if (backward) meshRef.current.position.z += speed * delta
        if (left) meshRef.current.position.x -= speed * delta
        if (right) meshRef.current.position.x += speed * delta
        if (jump) meshRef.current.position.y += speed * delta
      })
    
      return (
        <mesh ref={meshRef}>
          <boxGeometry />
          <meshStandardMaterial color="blue" />
        </mesh>
      )
    }
    
    export default function App() {
      return (
        <KeyboardControls map={keyMap}>
          <Canvas>
            <ambientLight />
            <Player />
          </Canvas>
        </KeyboardControls>
      )
    }
    

    Subscribe to Key Changes

    import { useKeyboardControls } from '@react-three/drei'
    import { useEffect } from 'react'
    
    function KeyListener() {
      const jumpPressed = useKeyboardControls((state) => state.jump)
    
      useEffect(() => {
        if (jumpPressed) {
          console.log('Jump!')
        }
      }, [jumpPressed])
    
      return null
    }
    

    Selection System

    Click to Select

    import { useState } from 'react'
    
    function SelectableScene() {
      const [selected, setSelected] = useState(null)
    
      return (
        <>
          {[[-2, 0, 0], [0, 0, 0], [2, 0, 0]].map((position, i) => (
            <mesh
              key={i}
              position={position}
              onClick={(e) => {
                e.stopPropagation()
                setSelected(i)
              }}
            >
              <boxGeometry />
              <meshStandardMaterial
                color={selected === i ? 'hotpink' : 'orange'}
                emissive={selected === i ? 'hotpink' : 'black'}
                emissiveIntensity={0.3}
              />
            </mesh>
          ))}
    
          {/* Click on empty space to deselect */}
          <mesh
            position={[0, -1, 0]}
            rotation={[-Math.PI / 2, 0, 0]}
            onClick={() => setSelected(null)}
          >
            <planeGeometry args={[20, 20]} />
            <meshStandardMaterial color="gray" />
          </mesh>
        </>
      )
    }
    

    Multi-Select with Outline

    import { useState } from 'react'
    import { EffectComposer, Outline, Selection, Select } from '@react-three/postprocessing'
    
    function MultiSelectScene() {
      const [selected, setSelected] = useState(new Set())
    
      const toggleSelect = (id, event) => {
        event.stopPropagation()
        setSelected((prev) => {
          const next = new Set(prev)
          if (event.shiftKey) {
            // Multi-select with shift
            if (next.has(id)) {
              next.delete(id)
            } else {
              next.add(id)
            }
          } else {
            // Single select
            next.clear()
            next.add(id)
          }
          return next
        })
      }
    
      return (
        <Selection>
          <EffectComposer autoClear={false}>
            <Outline
              blur
              visibleEdgeColor={0xffffff}
              edgeStrength={10}
            />
          </EffectComposer>
    
          {[0, 1, 2, 3, 4].map((id) => (
            <Select key={id} enabled={selected.has(id)}>
              <mesh
                position={[(id - 2) * 2, 0, 0]}
                onClick={(e) => toggleSelect(id, e)}
              >
                <boxGeometry />
                <meshStandardMaterial color="orange" />
              </mesh>
            </Select>
          ))}
        </Selection>
      )
    }
    

    Screen-Space to World-Space

    Get World Position from Click

    import { useThree } from '@react-three/fiber'
    import * as THREE from 'three'
    
    function ClickToPlace() {
      const { camera, raycaster, pointer } = useThree()
      const planeRef = useRef()
    
      const handleClick = (event) => {
        // Create intersection plane
        const plane = new THREE.Plane(new THREE.Vector3(0, 1, 0), 0)
        const intersection = new THREE.Vector3()
    
        // Cast ray from pointer
        raycaster.setFromCamera(pointer, camera)
        raycaster.ray.intersectPlane(plane, intersection)
    
        console.log('World position:', intersection)
      }
    
      return (
        <mesh
          ref={planeRef}
          rotation={[-Math.PI / 2, 0, 0]}
          onClick={handleClick}
        >
          <planeGeometry args={[100, 100]} />
          <meshBasicMaterial visible={false} />
        </mesh>
      )
    }
    

    World Position to Screen Position

    import { useThree, useFrame } from '@react-three/fiber'
    import { Html } from '@react-three/drei'
    import * as THREE from 'three'
    
    function WorldToScreen({ target }) {
      const { camera, size } = useThree()
    
      const getScreenPosition = (worldPos) => {
        const vector = worldPos.clone()
        vector.project(camera)
    
        return {
          x: (vector.x * 0.5 + 0.5) * size.width,
          y: (1 - (vector.y * 0.5 + 0.5)) * size.height
        }
      }
    
      // Or use Html component which handles this automatically
      return (
        <Html position={target}>
          <div className="label">Label</div>
        </Html>
      )
    }
    

    Gesture Recognition

    usePinch and useWheel

    import { usePinch, useWheel } from '@use-gesture/react'
    import { useSpring, animated } from '@react-spring/three'
    
    function ZoomableMesh() {
      const [spring, api] = useSpring(() => ({
        scale: 1,
        config: { mass: 1, tension: 200, friction: 30 }
      }))
    
      usePinch(
        ({ offset: [s] }) => {
          api.start({ scale: s })
        },
        { target: window }
      )
    
      useWheel(
        ({ delta: [, dy] }) => {
          api.start({ scale: spring.scale.get() - dy * 0.001 })
        },
        { target: window }
      )
    
      return (
        <animated.mesh scale={spring.scale}>
          <boxGeometry />
          <meshStandardMaterial color="cyan" />
        </animated.mesh>
      )
    }
    

    Scroll Controls

    import { Canvas } from '@react-three/fiber'
    import { ScrollControls, Scroll, useScroll } from '@react-three/drei'
    import { useFrame } from '@react-three/fiber'
    import { useRef } from 'react'
    
    function AnimatedOnScroll() {
      const meshRef = useRef()
      const scroll = useScroll()
    
      useFrame(() => {
        const offset = scroll.offset // 0 to 1
        meshRef.current.rotation.y = offset * Math.PI * 2
        meshRef.current.position.y = offset * 5
      })
    
      return (
        <mesh ref={meshRef}>
          <boxGeometry />
          <meshStandardMaterial color="orange" />
        </mesh>
      )
    }
    
    export default function App() {
      return (
        <Canvas>
          <ScrollControls pages={3} damping={0.25}>
            <Scroll>
              <AnimatedOnScroll />
            </Scroll>
    
            {/* HTML content that scrolls */}
            <Scroll html>
              <h1 style={{ position: 'absolute', top: '10vh' }}>Page 1</h1>
              <h1 style={{ position: 'absolute', top: '110vh' }}>Page 2</h1>
              <h1 style={{ position: 'absolute', top: '210vh' }}>Page 3</h1>
            </Scroll>
          </ScrollControls>
        </Canvas>
      )
    }
    

    Presentation Controls

    For product showcases with limited rotation.

    import { PresentationControls } from '@react-three/drei'
    
    function ProductShowcase() {
      return (
        <PresentationControls
          global                 // Apply to whole scene
          snap                   // Snap back when released
          speed={1}              // Rotation speed
          zoom={1}               // Zoom speed
          rotation={[0, 0, 0]}   // Initial rotation
          polar={[-Math.PI / 4, Math.PI / 4]}    // Vertical limits
          azimuth={[-Math.PI / 4, Math.PI / 4]}  // Horizontal limits
          config={{ mass: 1, tension: 170, friction: 26 }}
        >
          <mesh>
            <boxGeometry />
            <meshStandardMaterial color="gold" />
          </mesh>
        </PresentationControls>
      )
    }
    

    Performance Tips

    1. Stop propagation: Prevent unnecessary raycasts
    2. Use layers: Filter raycast targets
    3. Simpler collision meshes: Use invisible simple geometry
    4. Throttle events: Limit onPointerMove frequency
    5. Disable controls when not needed: enabled={false}
    // Use simpler geometry for raycasting
    function OptimizedInteraction() {
      return (
        <group>
          {/* Complex visible mesh */}
          <mesh raycast={() => null}>
            <torusKnotGeometry args={[1, 0.4, 100, 16]} />
            <meshStandardMaterial color="purple" />
          </mesh>
    
          {/* Simple invisible collision mesh */}
          <mesh onClick={() => console.log('clicked')}>
            <sphereGeometry args={[1.5]} />
            <meshBasicMaterial visible={false} />
          </mesh>
        </group>
      )
    }
    
    // Throttle pointer move events
    import { useMemo, useCallback } from 'react'
    import throttle from 'lodash/throttle'
    
    function ThrottledHover() {
      const handleMove = useMemo(
        () => throttle((e) => {
          console.log('Move', e.point)
        }, 100),
        []
      )
    
      return (
        <mesh onPointerMove={handleMove}>
          <boxGeometry />
          <meshStandardMaterial />
        </mesh>
      )
    }
    

    See Also

    • r3f-fundamentals - Canvas and scene setup
    • r3f-animation - Animating interactions
    • r3f-postprocessing - Visual feedback effects (outline, selection)

    Требования и возможности

    Источник пакета
    https://github.com/enzed/r3f-skills/tree/8ce187a05a5316cf19fb6cc020e30018a418e565/skills/r3f-interaction

    Файлы версии

    ПутьРазмерSHA256
    SKILL.md19990dc0bc0ef2a5621bb...

    Частые вопросы

    Как установить R3f Interaction?
    Используйте команду npx skills add enzed/r3f-skills --skill r3f-interaction или скачайте ZIP-архив.
    Можно ли скачать R3f Interaction бесплатно?
    Да, опубликованную версию можно скачать из маркетплейса бесплатно.

    Похожие инструменты

    Смотреть все
    Story Long Write长篇网文写作。从大纲到正文,辅助长篇网络小说的创作,包括世界观、人物、情节线管理。触发方式:/story-long-write、/写长篇、「帮我开书」「写大纲」「日更」「续写」「继续写」「修改第X章」「回炉」「重写第X章」。Parallel Deep ResearchONLY use when user explicitly says 'deep research', 'exhaustive', 'comprehensive report', or 'thorough investigation'. Slower and more expensive than parallel-web-search. For normal research/lookup requests, use parallel-web-search instead. Supports multi-turn: pass --previous-interaction-id from a prior research or enrichment to continue with context.LangfuseInteract with Langfuse and access its documentation: tracing, monitoring, creating datasets, running experiments, and evaluating AI applications. Use when needing to (1) query or modify Langfuse data, (2) look up Langfuse documentation, concepts, integration guides, a feature or SDK usage, or (3) do any AI engineering task (AI observability, prompt engineering/management, evaluation and evaluator management, experimentation, dataset management, evaluation-driven CI/CD, feedback collection). Invoke it for tasks in this scope even when Langfuse is not configured or explicitly mentioned.
    Комментарии

    Войдите, чтобы оставить комментарий.

    Комментариев пока нет.

    Установить скилл

    Добавьте инструмент одной командой или скачайте проверенный архив версии.

    npx skills add enzed/r3f-skills --skill r3f-interaction
    Скачать ZIP
    Версия
    1.0.0+8ce187a05a53
    Автор
    Владимир Ломтев
    Репозиторий
    enzed/r3f-skills
    GitHub: enzed/r3f-skills
    Excel Automation>
    R3f Interaction — Скилл для ИИ-агентов | AI Рассвет