media-plane.tsx 2.2 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192
  1. "use client";
  2. import { useRef, useEffect, useState } from "react";
  3. import * as THREE from "three";
  4. interface MediaPlaneProps {
  5. position?: [number, number, number] | null;
  6. rotation?: [number, number, number] | null;
  7. scale?: [number, number, number] | null;
  8. castShadow?: boolean | null;
  9. receiveShadow?: boolean | null;
  10. url: string;
  11. mediaType: "image" | "video";
  12. loop?: boolean | null;
  13. autoplay?: boolean | null;
  14. muted?: boolean | null;
  15. width?: number | null;
  16. height?: number | null;
  17. objectId?: string | null;
  18. }
  19. export function MediaPlane({
  20. position,
  21. rotation,
  22. scale,
  23. url,
  24. mediaType,
  25. loop,
  26. autoplay,
  27. muted,
  28. width,
  29. height,
  30. }: MediaPlaneProps) {
  31. const meshRef = useRef<THREE.Mesh>(null);
  32. const [texture, setTexture] = useState<THREE.Texture | null>(null);
  33. useEffect(() => {
  34. if (!url) return;
  35. if (mediaType === "video") {
  36. const video = document.createElement("video");
  37. video.src = url;
  38. video.crossOrigin = "anonymous";
  39. video.loop = loop ?? false;
  40. video.muted = muted ?? true;
  41. if (autoplay !== false) video.play();
  42. const tex = new THREE.VideoTexture(video);
  43. tex.colorSpace = THREE.SRGBColorSpace;
  44. setTexture(tex);
  45. return () => {
  46. video.pause();
  47. video.src = "";
  48. tex.dispose();
  49. };
  50. } else {
  51. const loader = new THREE.TextureLoader();
  52. loader.load(url, (tex) => {
  53. tex.colorSpace = THREE.SRGBColorSpace;
  54. setTexture(tex);
  55. });
  56. }
  57. }, [url, mediaType, loop, autoplay, muted]);
  58. const pos: [number, number, number] = position ?? [0, 0, 0];
  59. const rot: [number, number, number] = rotation ?? [0, 0, 0];
  60. const scl: [number, number, number] = scale ?? [1, 1, 1];
  61. const w = width ?? 2;
  62. const h = height ?? w * 0.75;
  63. return (
  64. <mesh
  65. ref={meshRef}
  66. position={pos}
  67. rotation={rot}
  68. scale={scl}
  69. castShadow
  70. receiveShadow
  71. >
  72. <planeGeometry args={[w, h]} />
  73. {texture ? (
  74. <meshBasicMaterial
  75. map={texture as THREE.Texture}
  76. side={THREE.DoubleSide}
  77. />
  78. ) : (
  79. <meshStandardMaterial color="#333" side={THREE.DoubleSide} />
  80. )}
  81. </mesh>
  82. );
  83. }