이미지를 Box D 에만 texture 변수에 담어서 Mapping 하고, 나머지는 new THREE.Color() 처리해봅니다.
/* eslint-disable */
import React, { useRef, useMemo, useState, useEffect } from "react";
// import { useNavigate } from "react-router-dom";
import { Canvas, useFrame } from "@react-three/fiber";
import { Html } from "@react-three/drei";
import { gsap } from "gsap"; // 애니메이션을 위해 gsap 사용
import * as THREE from "three";
import { numberPlate } from "../../assets/images/index"; // 이미지 경로 임포트
function createMesh(geometry, material, position, name) {
const mesh = new THREE.Mesh(geometry, material.clone());
mesh.position.set(...position);
mesh.name = name;
mesh.castShadow = true;
mesh.receiveShadow = true;
return mesh;
}
function Box({ geometry, material, position, initialPosition, name, texture }) {
// const navigate = useNavigate();
// Random color generation
const [color, setColor] = useState(
() => new THREE.Color(Math.random(), Math.random(), Math.random())
);
const [showTooltip, setShowTooltip] = useState(false);
const mesh = useRef();
const materialWithTexture = useMemo(() => {
if (texture) {
console.log("Applying texture to material:", texture);
return new THREE.MeshStandardMaterial({ map: texture });
}
return new THREE.MeshStandardMaterial({ color });
}, [texture, color]);
useEffect(() => {
const animatePosition = () => {
if (mesh.current) {
gsap.fromTo(
mesh.current.position,
{
x: initialPosition[0],
y: initialPosition[1],
z: initialPosition[2]
},
{
x: position[0],
y: position[1],
z: position[2],
duration: 1.5,
ease: "power2.inOut"
}
);
} else {
requestAnimationFrame(animatePosition);
}
};
requestAnimationFrame(animatePosition);
}, [position, initialPosition]);
useEffect(() => {
if (mesh.current) {
mesh.current.material = materialWithTexture;
mesh.current.material.needsUpdate = true;
console.log("Material applied to mesh:", mesh.current.material);
}
}, [materialWithTexture]);
const handlePointerOver = (event) => {
document.body.style.cursor = "pointer";
setShowTooltip(true);
};
const handlePointerOut = (event) => {
document.body.style.cursor = "default";
setShowTooltip(false);
};
const handleClick = (event) => {
const newColor = new THREE.Color(
Math.random(),
Math.random(),
Math.random()
);
setColor(newColor);
// navigate(`/${name}`)
console.log("Clicked :", `/${name}`);
};
return (
<mesh
ref={mesh}
position={initialPosition}
castShadow
receiveShadow
onPointerOver={handlePointerOver}
onPointerOut={handlePointerOut}
onClick={handleClick}
>
<boxGeometry args={[1, 1, 1]} />
<meshStandardMaterial attach="material" {...materialWithTexture} />
{showTooltip && (
<Html position={[0.5, 0, 0]} center>
<div
style={{
background: "rgba(0, 0, 0, 0.70)",
color: "white",
padding: "5px",
borderRadius: "5px",
whiteSpace: "nowrap", // 텍스트가 한 줄로 유지되도록 설정
}}
>
{name}
</div>
</Html>
)}
</mesh>
);
}
const Blocks = React.memo(function Blocks(props) {
const modelRef = useRef();
const isDraggingRef = useRef(false);
const rotationRef = useRef({ x: 0, y: 0 });
const [scale, setScale] = useState([2.5, 2.5, 2.5]);
const [texture, setTexture] = useState(null);
// Define geometry and material within Blocks
const boxGeometry = useMemo(() => new THREE.BoxGeometry(1, 1, 1), []);
const material = useMemo(() => new THREE.MeshStandardMaterial(), []);
useEffect(() => {
const loader = new THREE.TextureLoader();
loader.load(
numberPlate,
(loadedTexture) => {
loadedTexture.flipY = false; // 텍스처의 flipY 속성 설정
setTexture(loadedTexture);
console.log("Texture loaded:", loadedTexture); // 텍스처 로드 확인
},
undefined,
(error) => {
console.error("Error loading texture:", error); // 텍스처 로드 오류 확인
}
);
}, []);
useFrame((state) => {
if (modelRef.current) {
// 드래그 중일 때만 회전만 적용
if (isDraggingRef.current) {
modelRef.current.rotation.y += rotationRef.current.y;
modelRef.current.rotation.x += rotationRef.current.x; // 회전 후에 값을 초기화하여 부드러운 회전 유지
rotationRef.current.x = 0;
rotationRef.current.y = 0;
} else {
// 드래그가 아닐 때 y축 위치 애니메이션 적용
modelRef.current.position.y =
-1.5 + Math.sin(state.clock.getElapsedTime()) * 0.3;
}
}
});
useEffect(() => {
const handleMouseDown = () => {
isDraggingRef.current = true;
};
const handleMouseUp = () => {
isDraggingRef.current = false;
};
const handleMouseMove = (event) => {
if (isDraggingRef.current) {
rotationRef.current = {
x: event.movementY * 0.005,
y: event.movementX * 0.005,
};
}
};
// touch event for mobile
const handleTouchStart = () => {
isDraggingRef.current = true;
};
const handleTouchEnd = () => {
isDraggingRef.current = false;
};
const handleTouchMove = (event) => {
if (isDraggingRef.current && event.touches.length === 1) {
const touch = event.touches[0];
rotationRef.current = {
x: touch.movementY * 0.005,
y: touch.movementX * 0.005,
};
}
};
window.addEventListener("mousedown", handleMouseDown);
window.addEventListener("mouseup", handleMouseUp);
window.addEventListener("mousemove", handleMouseMove);
window.addEventListener("touchstart", handleTouchStart);
window.addEventListener("touchend", handleTouchEnd);
window.addEventListener("touchmove", handleTouchMove);
return () => {
window.removeEventListener("mousedown", handleMouseDown);
window.removeEventListener("mouseup", handleMouseUp);
window.removeEventListener("mousemove", handleMouseMove);
window.removeEventListener("touchstart", handleTouchStart);
window.removeEventListener("touchend", handleTouchEnd);
window.removeEventListener("touchmove", handleTouchMove);
};
}, []);
useEffect(() => {
const handleResize = () => {
if (window.innerWidth < 600) {
//768
setScale([1, 1, 1]);
} else {
setScale([2.5, 2.5, 2.5]);
}
};
window.addEventListener("resize", handleResize);
// Initial check
handleResize();
return () => {
window.removeEventListener("resize", handleResize);
};
}, []);
return (
<group
{...props}
dispose={null}
ref={modelRef}
position={[0, 0, 0]}
scale={scale}
// rotation={[0.25, 0, 0]}
>
<Box
geometry={boxGeometry}
material={material}
position={[1.5, 0, 0]}
initialPosition={[3, 0, 0]} // 초기 위치를 오른쪽 멀리 설정
name="Box A"
/>
<Box
geometry={boxGeometry}
material={material}
position={[-1.5, 0, 0]}
initialPosition={[-3, 0, 0]} // 초기 위치를 왼쪽 멀리 설정
name="Box B"
/>
<Box
geometry={boxGeometry}
material={material}
position={[0, 1.5, 0]}
initialPosition={[0, 3, 0]} // 초기 위치를 위쪽 멀리 설정
name="Box C"
/>
<Box
geometry={boxGeometry}
material={material}
position={[0, -1.5, 0]}
initialPosition={[0, -3, 0]} // 초기 위치를 아래쪽 멀리 설정
name="Box D"
texture={texture} // 텍스처 추가
/>
</group>
);
});
export default Blocks;
GLSL, WGSL 의 차이 (2) | 2024.12.14 |
---|---|
OrbitControls - target, maxPolarAngle (0) | 2024.12.06 |
touch event - @react-three/fiber (2) | 2024.11.23 |
video backgorund with @react-three/fibre (1) | 2024.11.22 |
resize - Three.js (0) | 2024.11.22 |