pmndrs / react-three-fiber

🇨🇭 A React renderer for Three.js
https://docs.pmnd.rs/react-three-fiber
MIT License
27.62k stars 1.6k forks source link

touch action similar to model viewer #3263

Closed dhruvadhia1 closed 3 months ago

dhruvadhia1 commented 6 months ago

Hi There,

I am wondering if there is a way to achieve touch-action: pan-y effect for mobile interaction, so if I first drag horizontally then i can interact with 3d model using orbit controls, otherwise i can scroll the page. It feels intuitive and can allow for both.

the examples on model viewer site on mobile works perfect.

https://modelviewer.dev/

i also tried applying the same css property on canvas, but no luck so far

HoseinKhanBeigi commented 6 months ago

i think To achieve the desired touch interaction using react-three-fiber, you need to combine the library with the drei library for orbit controls and handle touch events to distinguish between horizontal and vertical swipes. Below is a detailed guide on how to implement this in a react-three-fiber application. Step 1: Set Up Your Project Ensure you have react-three-fiber and drei installed. If not, you can install them using: Step 2: Create Your React Component App Component:

import React, { useRef, useState } from 'react';
import { Canvas } from '@react-three/fiber';
import { OrbitControls } from '@react-three/drei';
import { Box } from './Box'; // Assume Box is a simple 3D box component

const App = () => {
  const controlsRef = useRef();
  const [isHorizontalPan, setIsHorizontalPan] = useState(false);

  const handleTouchStart = (event) => {
    const touch = event.touches[0];
    controlsRef.current.startX = touch.clientX;
    controlsRef.current.startY = touch.clientY;
    controlsRef.current.moved = false;
  };

  const handleTouchMove = (event) => {
    const touch = event.touches[0];
    const deltaX = touch.clientX - controlsRef.current.startX;
    const deltaY = touch.clientY - controlsRef.current.startY;

    if (Math.abs(deltaX) > Math.abs(deltaY)) {
      setIsHorizontalPan(true);
      controlsRef.current.moved = true;
      // Prevent vertical scrolling when interacting with the model
      event.preventDefault();
    } else if (!controlsRef.current.moved) {
      setIsHorizontalPan(false);
    }
  };

  const handleTouchEnd = () => {
    controlsRef.current.moved = false;
  };

  return (
    <Canvas
      onTouchStart={handleTouchStart}
      onTouchMove={handleTouchMove}
      onTouchEnd={handleTouchEnd}
    >
      <ambientLight />
      <pointLight position={[10, 10, 10]} />
      <Box position={[-1.2, 0, 0]} />
      <Box position={[1.2, 0, 0]} />
      <OrbitControls ref={controlsRef} enablePan={isHorizontalPan} />
    </Canvas>
  );
};

export default App;

Box Component:

import React from 'react';
import { useFrame } from '@react-three/fiber';

const Box = (props) => {
  const mesh = React.useRef();

  useFrame(() => {
    mesh.current.rotation.x += 0.01;
    mesh.current.rotation.y += 0.01;
  });

  return (
    <mesh {...props} ref={mesh}>
      <boxGeometry args={[1, 1, 1]} />
      <meshStandardMaterial color={'orange'} />
    </mesh>
  );
};

export { Box };