Logo
Загрузка...

React/TypeScript Demo

Демонстрация возможностей создания интерактивных компонентов с помощью React и TypeScript

LEGO Universe - Three.js 3D Experience

Интерактивная 3D сцена с LEGO блоками, созданная с помощью Three.js

Живой предпросмотр

Ошибка рендеринга React компонента

Не удалось отобразить React компонент. Проверьте синтаксис.

Детали ошибки
Unexpected token '<'

Исходный код

import React, { useEffect, useRef, useState } from 'react';
import * as THREE from 'three';

export default function LegoExperience() {
  const containerRef = useRef(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    if (!containerRef.current) return;

    // Scene setup
    const scene = new THREE.Scene();
    scene.background = new THREE.Color(0xffffff);
    scene.fog = new THREE.Fog(0xffffff, 10, 50);

    const camera = new THREE.PerspectiveCamera(
      75,
      window.innerWidth / window.innerHeight,
      0.1,
      1000
    );
    camera.position.z = 30;

    const renderer = new THREE.WebGLRenderer({ antialias: true });
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.shadowMap.enabled = true;
    renderer.shadowMap.type = THREE.PCFSoftShadowMap;
    containerRef.current.appendChild(renderer.domElement);

    // Lighting
    const ambientLight = new THREE.AmbientLight(0xffffff, 0.4);
    scene.add(ambientLight);

    const directionalLight1 = new THREE.DirectionalLight(0xff3366, 1);
    directionalLight1.position.set(5, 5, 5);
    scene.add(directionalLight1);

    const directionalLight2 = new THREE.DirectionalLight(0x3366ff, 0.8);
    directionalLight2.position.set(-5, -5, 5);
    scene.add(directionalLight2);

    const pointLight = new THREE.PointLight(0xffaa00, 1, 50);
    pointLight.position.set(0, 0, 10);
    scene.add(pointLight);

    // LEGO brick colors
    const legoColors = [
      0xff0000, // Red
      0x0055ff, // Blue
      0xffdd00, // Yellow
      0x00aa00, // Green
      0xff6600, // Orange
      0xff00ff, // Magenta
      0x00ffff, // Cyan
      0xffffff, // White
    ];

    // Create LEGO brick geometry
    function createLegoBrick() {
      const group = new THREE.Group();
      
      // Main brick body
      const bodyGeometry = new THREE.BoxGeometry(2, 1.2, 1);
      const color = legoColors[Math.floor(Math.random() * legoColors.length)];
      const bodyMaterial = new THREE.MeshStandardMaterial({
        color: color,
        metalness: 0.3,
        roughness: 0.4,
        emissive: color,
        emissiveIntensity: 0.05,
      });
      const body = new THREE.Mesh(bodyGeometry, bodyMaterial);
      body.castShadow = true;
      group.add(body);

      // Studs on top
      const studGeometry = new THREE.CylinderGeometry(0.25, 0.25, 0.3, 16);
      const studMaterial = new THREE.MeshStandardMaterial({
        color: color,
        metalness: 0.4,
        roughness: 0.3,
        emissive: color,
        emissiveIntensity: 0.05,
      });

      // Create 2x4 stud pattern
      for (let i = 0; i < 2; i++) {
        for (let j = 0; j < 4; j++) {
          const stud = new THREE.Mesh(studGeometry, studMaterial);
          stud.position.set(-0.75 + j * 0.5, 0.75, -0.25 + i * 0.5);
          group.add(stud);
        }
      }

      return group;
    }

    // Create multiple LEGO bricks
    const bricks = [];
    const numBricks = 50;

    for (let i = 0; i < numBricks; i++) {
      const brick = createLegoBrick();
      
      // Random position
      brick.position.x = (Math.random() - 0.5) * 60;
      brick.position.y = (Math.random() - 0.5) * 40;
      brick.position.z = (Math.random() - 0.5) * 40;

      // Random rotation
      brick.rotation.x = Math.random() * Math.PI * 2;
      brick.rotation.y = Math.random() * Math.PI * 2;
      brick.rotation.z = Math.random() * Math.PI * 2;

      // Random scale
      const scale = 0.5 + Math.random() * 0.8;
      brick.scale.set(scale, scale, scale);

      // Store velocity for animation
      brick.userData = {
        velocityX: (Math.random() - 0.5) * 0.02,
        velocityY: (Math.random() - 0.5) * 0.02,
        velocityZ: (Math.random() - 0.5) * 0.02,
        rotationSpeedX: (Math.random() - 0.5) * 0.02,
        rotationSpeedY: (Math.random() - 0.5) * 0.02,
        rotationSpeedZ: (Math.random() - 0.5) * 0.02,
      };

      scene.add(brick);
      bricks.push(brick);
    }

    // Particle system for extra magic
    const particlesGeometry = new THREE.BufferGeometry();
    const particlesCount = 1000;
    const posArray = new Float32Array(particlesCount * 3);

    for (let i = 0; i < particlesCount * 3; i++) {
      posArray[i] = (Math.random() - 0.5) * 100;
    }

    particlesGeometry.setAttribute('position', new THREE.BufferAttribute(posArray, 3));
    const particlesMaterial = new THREE.PointsMaterial({
      size: 0.05,
      color: 0x888888,
      transparent: true,
      opacity: 0.3,
      blending: THREE.NormalBlending,
    });

    const particlesMesh = new THREE.Points(particlesGeometry, particlesMaterial);
    scene.add(particlesMesh);

    // Mouse interaction
    const mouse = { x: 0, y: 0 };
    const handleMouseMove = (event) => {
      mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
      mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
    };
    window.addEventListener('mousemove', handleMouseMove);

    // Animation
    let time = 0;
    function animate() {
      requestAnimationFrame(animate);
      time += 0.01;

      // Animate LEGO bricks
      bricks.forEach((brick, index) => {
        // Floating motion
        brick.position.x += brick.userData.velocityX;
        brick.position.y += brick.userData.velocityY;
        brick.position.z += brick.userData.velocityZ;

        // Boundary check
        if (Math.abs(brick.position.x) > 30) brick.userData.velocityX *= -1;
        if (Math.abs(brick.position.y) > 20) brick.userData.velocityY *= -1;
        if (Math.abs(brick.position.z) > 20) brick.userData.velocityZ *= -1;

        // Rotation
        brick.rotation.x += brick.userData.rotationSpeedX;
        brick.rotation.y += brick.userData.rotationSpeedY;
        brick.rotation.z += brick.userData.rotationSpeedZ;

        // Wave effect
        brick.position.y += Math.sin(time + index * 0.5) * 0.02;

        // Mouse interaction
        const dx = brick.position.x - mouse.x * 20;
        const dy = brick.position.y - mouse.y * 20;
        const distance = Math.sqrt(dx * dx + dy * dy);
        
        if (distance < 10) {
          brick.position.x += dx * 0.01;
          brick.position.y += dy * 0.01;
        }
      });

      // Rotate particle system
      particlesMesh.rotation.y += 0.0005;
      particlesMesh.rotation.x += 0.0003;

      // Animate point light
      pointLight.position.x = Math.sin(time * 0.5) * 15;
      pointLight.position.y = Math.cos(time * 0.7) * 15;

      // Camera gentle movement
      camera.position.x = Math.sin(time * 0.2) * 2;
      camera.position.y = Math.cos(time * 0.15) * 2;
      camera.lookAt(0, 0, 0);

      renderer.render(scene, camera);
    }

    animate();
    setLoading(false);

    // Handle window resize
    const handleResize = () => {
      camera.aspect = window.innerWidth / window.innerHeight;
      camera.updateProjectionMatrix();
      renderer.setSize(window.innerWidth, window.innerHeight);
    };
    window.addEventListener('resize', handleResize);

    // Cleanup
    return () => {
      window.removeEventListener('mousemove', handleMouseMove);
      window.removeEventListener('resize', handleResize);
      if (containerRef.current) {
        containerRef.current.removeChild(renderer.domElement);
      }
      renderer.dispose();
    };
  }, []);

  return (
    <div style={{ position: 'relative', width: '100vw', height: '100vh', overflow: 'hidden' }}>
      {loading && (
        <div
          style={{
            position: 'absolute',
            top: '50%',
            left: '50%',
            transform: 'translate(-50%, -50%)',
            color: '#333',
            fontSize: '24px',
            fontFamily: 'Arial, sans-serif',
            zIndex: 10,
          }}
        >
          Загрузка...
        </div>
      )}
      <div
        style={{
          position: 'absolute',
          top: '20px',
          left: '50%',
          transform: 'translateX(-50%)',
          color: '#333',
          fontSize: '48px',
          fontWeight: 'bold',
          fontFamily: 'Arial, sans-serif',
          textShadow: '0 0 20px rgba(0,0,0,0.1)',
          zIndex: 5,
          pointerEvents: 'none',
        }}
      >
        LEGO Universe
      </div>
      <div
        style={{
          position: 'absolute',
          bottom: '20px',
          left: '50%',
          transform: 'translateX(-50%)',
          color: 'rgba(0,0,0,0.6)',
          fontSize: '16px',
          fontFamily: 'Arial, sans-serif',
          zIndex: 5,
          pointerEvents: 'none',
          textAlign: 'center',
        }}
      >
        Двигайте мышью для взаимодействия
      </div>
      <div ref={containerRef} />
    </div>
  );
}