New file |
| | |
| | | const areaData = [ |
| | | { |
| | | x: -500, |
| | | y: -400, |
| | | width: 700, |
| | | height: 800, |
| | | name: 'selected-rect1684723196047', |
| | | type: 'area', |
| | | strokeColor: 'rgb(255, 0, 14)', |
| | | areaNumber: 'A1', |
| | | textHeight: 400, |
| | | }, |
| | | ]; |
| | | |
| | | export default areaData; |
New file |
| | |
| | | import React, { useRef, useState } from 'react'; |
| | | import { useFrame, useThree } from '@react-three/fiber'; |
| | | import * as THREE from 'three'; |
| | | import { CameraControls } from '@react-three/drei'; |
| | | import Text2 from './text'; |
| | | // import Annotation, { IAnnotationDataItem, IAnnotationRef } from './annotation'; |
| | | |
| | | const Y = 1; |
| | | const Area = ({ |
| | | x, |
| | | y, |
| | | width, |
| | | height, |
| | | areaNumber, |
| | | textHeight, |
| | | strokeColor, |
| | | }) => { |
| | | const [hovered, setHover] = useState(false); |
| | | const [clicked, setClicked] = useState(false); |
| | | const meshRef = useRef(null); |
| | | const { controls } = useThree((state) => ({ |
| | | controls: state.controls, |
| | | })); |
| | | |
| | | // 转换为x轴和z轴组成的坐标系中的位置 |
| | | const position = new THREE.Vector3(x + width / 2, Y, y + height / 2); |
| | | const size = [width, 1, height]; |
| | | textHeight ||= Y + 50; |
| | | |
| | | // 相机移动到区域的位置 |
| | | const handleClick = () => { |
| | | if (clicked) return; |
| | | setClicked(true); |
| | | // controls.fitToBox(meshRef.current!, true); |
| | | const mesh = meshRef.current; |
| | | const { x, y, z } = mesh.position; |
| | | |
| | | const box = new THREE.Box3().setFromCenterAndSize( |
| | | new THREE.Vector3(x, (y + textHeight) / 2, z), |
| | | new THREE.Vector3(width, textHeight, height) |
| | | ); |
| | | controls.fitToBox(box, true); |
| | | |
| | | // annotationRef.current?.show(); |
| | | }; |
| | | |
| | | // 相机移动回原来的位置 |
| | | const handleDoubleClick = () => { |
| | | setClicked(false); |
| | | }; |
| | | |
| | | // 监听鼠标移入和移出事件,改变hover状态 |
| | | const handlePointerOver = () => setHover(true); |
| | | const handlePointerOut = () => setHover(false); |
| | | |
| | | // 每帧更新边框的颜色和粗细 |
| | | useFrame(() => { |
| | | const box = meshRef.current; |
| | | if (box) { |
| | | const color = hovered || clicked ? strokeColor : 'white'; |
| | | const thickness = clicked ? 0.5 : 0.2; |
| | | const material = box.material; |
| | | material.color.set(color); |
| | | material.linewidth = thickness; |
| | | } |
| | | }); |
| | | |
| | | // const annotationRef = useRef(null); |
| | | const annotationData = [ |
| | | { |
| | | label: '长', |
| | | value: width + '米', |
| | | }, |
| | | { |
| | | label: '宽', |
| | | value: height + '米', |
| | | }, |
| | | ]; |
| | | |
| | | return ( |
| | | <group |
| | | onClick={handleClick} |
| | | onPointerOver={handlePointerOver} |
| | | onPointerOut={handlePointerOut} |
| | | onDoubleClick={handleDoubleClick} |
| | | onPointerMissed={() => setClicked(false)} |
| | | > |
| | | <mesh ref={meshRef} position={position}> |
| | | <boxGeometry attach="geometry" args={size} /> |
| | | <lineSegments> |
| | | <edgesGeometry attach="geometry" args={[new THREE.BoxGeometry(...size)]} /> |
| | | <lineBasicMaterial attach="material" color={strokeColor} linewidth={0.2} /> |
| | | </lineSegments> |
| | | <meshBasicMaterial attach="material" color={strokeColor} transparent opacity={0.2} /> |
| | | </mesh> |
| | | |
| | | {areaNumber && ( |
| | | <Text2 |
| | | position={new THREE.Vector3(position.x, textHeight, position.z)} |
| | | text={areaNumber} |
| | | scale={new THREE.Vector3(100, 100, 100)} |
| | | fontSize={100} |
| | | /> |
| | | )} |
| | | {/* <Annotation |
| | | ref={annotationRef} |
| | | title={areaNumber} |
| | | position={position} |
| | | data={annotationData} |
| | | ></Annotation> */} |
| | | </group> |
| | | ); |
| | | }; |
| | | |
| | | export default Area; |
New file |
| | |
| | | import * as THREE from 'three'; |
| | | import { useEffect, useState } from 'react'; |
| | | |
| | | |
| | | function generateSprite(text, color, fontSize = 50) { |
| | | const canvas = document.createElement('canvas'); |
| | | canvas.width = 300; |
| | | canvas.height = 300; |
| | | const context = canvas.getContext('2d'); |
| | | context.beginPath(); |
| | | context.font = `${fontSize}px Microsoft YaHei`; |
| | | context.fillStyle = color; |
| | | context.textAlign = 'center'; |
| | | context.textBaseline = 'middle'; |
| | | context.fillText(text, canvas.width / 2, canvas.height / 2); |
| | | context.fill(); |
| | | context.stroke(); |
| | | return canvas; |
| | | } |
| | | |
| | | const Text2 = ({ |
| | | text, |
| | | position, |
| | | scale = new THREE.Vector3(100, 100, 100), |
| | | color = '#00D1D1', |
| | | fontSize = 50, |
| | | }) => { |
| | | const [material, setMaterial] = useState(undefined); |
| | | useEffect(() => { |
| | | const material = new THREE.SpriteMaterial({ |
| | | map: new THREE.CanvasTexture(generateSprite(text, color, fontSize)), |
| | | blending: THREE.AdditiveBlending, |
| | | }); |
| | | setMaterial(material); |
| | | }, [text, color, fontSize]); |
| | | |
| | | return <sprite material={material} position={position} scale={scale} />; |
| | | } |
| | | |
| | | export default Text2; |
| | |
| | | import { useFrame } from '@react-three/fiber'; |
| | | import * as THREE from 'three'; |
| | | import Tunnel from '../components/tunnel'; |
| | | import Area from '../components/area'; |
| | | import Shelf from '../components/shelf'; |
| | | import Box from '../components/box'; |
| | | import Agv from '../components/agv'; |
| | | |
| | | import tunnelData from '@/assets/data/tunnel'; |
| | | import areaData from '@/assets/data/area'; |
| | | import shelfData from '@/assets/data/shelf'; |
| | | import agvRealDataList from '@/assets/data/agv'; |
| | | import { INTERVAL_TIME } from '@/config/setting' |
| | |
| | | return tunnelData.map((data, index) => <Tunnel key={index} {...data} />) |
| | | }, []); |
| | | |
| | | const areaEl = useMemo(() => { |
| | | return areaData.map((area, index) => <Area key={index} {...area} />) |
| | | }, []); |
| | | |
| | | const shelfEl = useMemo(() => { |
| | | return shelfData.map((data, index) => <Shelf key={index} {...data} />) |
| | | }, []); |
| | |
| | | <> |
| | | <group> |
| | | {tunnelEl} |
| | | {areaEl} |
| | | {shelfEl} |
| | | {/* {shelfEl1} */} |
| | | {agvEl} |