diff --git a/README.md b/README.md index 95e2957..3470e45 100644 --- a/README.md +++ b/README.md @@ -1 +1,6 @@ -# vite-react-template \ No newline at end of file +# virtual-assistant + +## 项目简介 + + +运行threejs \ No newline at end of file diff --git a/index.html b/index.html index e4b78ea..dd9ddf2 100644 --- a/index.html +++ b/index.html @@ -1,13 +1,24 @@ - - - - - Vite + React + TS - - -
- - - + + + + + + virtual-assistant + + + + +
+ + + + \ No newline at end of file diff --git a/package.json b/package.json index b11320f..991acd5 100644 --- a/package.json +++ b/package.json @@ -17,6 +17,7 @@ "license": "MIT", "dependencies": { "@kevisual/router": "0.0.9", + "@kevisual/store": "^0.0.4", "clsx": "^2.1.1", "dayjs": "^1.11.13", "lodash-es": "^4.17.21", @@ -25,6 +26,7 @@ "react": "^19.0.0", "react-dom": "^19.0.0", "react-toastify": "^11.0.5", + "three": "^0.175.0", "zustand": "^5.0.3" }, "devDependencies": { @@ -34,6 +36,7 @@ "@types/node": "^22.13.13", "@types/react": "^19.0.12", "@types/react-dom": "^19.0.4", + "@types/three": "^0.175.0", "@vitejs/plugin-react": "^4.3.4", "tailwindcss": "^4.0.16", "typescript": "^5.8.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 47f8705..c46140a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@kevisual/router': specifier: 0.0.9 version: 0.0.9 + '@kevisual/store': + specifier: ^0.0.4 + version: 0.0.4 clsx: specifier: ^2.1.1 version: 2.1.1 @@ -35,6 +38,9 @@ importers: react-toastify: specifier: ^11.0.5 version: 11.0.5(react-dom@19.0.0(react@19.0.0))(react@19.0.0) + three: + specifier: ^0.175.0 + version: 0.175.0 zustand: specifier: ^5.0.3 version: 5.0.3(@types/react@19.0.12)(immer@10.1.1)(react@19.0.0)(use-sync-external-store@1.2.2(react@19.0.0)) @@ -57,6 +63,9 @@ importers: '@types/react-dom': specifier: ^19.0.4 version: 19.0.4(@types/react@19.0.12) + '@types/three': + specifier: ^0.175.0 + version: 0.175.0 '@vitejs/plugin-react': specifier: ^4.3.4 version: 4.3.4(vite@6.2.3(@types/node@22.13.13)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.5.1)) @@ -346,6 +355,9 @@ packages: '@kevisual/router@0.0.9': resolution: {integrity: sha512-qPyC2GVJ7iOIdJCCKNDsWMAKOQeSJW9HBpL5ZWKHTbi+t4jJBGTzIlXmjKeMHRd0lr/Qq1imQvlkSh4hlrbodA==} + '@kevisual/store@0.0.4': + resolution: {integrity: sha512-iOgUg7VfyV8au27wSt0DdFqptcykb0mOAayCWChjgfKRKaLh4B021VBn5bdfyrfN1ektJo0ibsapd/QAN6GBtg==} + '@kevisual/types@0.0.6': resolution: {integrity: sha512-7yxe1QmuC5g7lI/1Hm+zXly8if0z+ZqGM1SVOVv2VNRwRAVYBJDc365zWCCfRwE+5YaB2daWTe5zBOU4EkltkQ==} @@ -522,6 +534,9 @@ packages: peerDependencies: vite: ^5.2.0 || ^6 + '@tweenjs/tween.js@23.1.3': + resolution: {integrity: sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==} + '@types/babel__core@7.20.5': resolution: {integrity: sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==} @@ -557,12 +572,24 @@ packages: '@types/react@19.0.12': resolution: {integrity: sha512-V6Ar115dBDrjbtXSrS+/Oruobc+qVbbUxDFC1RSbRqLt5SYvxxyIDrSC85RWml54g+jfNeEMZhEj7wW07ONQhA==} + '@types/stats.js@0.17.3': + resolution: {integrity: sha512-pXNfAD3KHOdif9EQXZ9deK82HVNaXP5ZIF5RP2QG6OQFNTaY2YIetfrE9t528vEreGQvEPRDDc8muaoYeK0SxQ==} + + '@types/three@0.175.0': + resolution: {integrity: sha512-ldMSBgtZOZ3g9kJ3kOZSEtZIEITmJOzu8eKVpkhf036GuNkM4mt0NXecrjCn5tMm1OblOF7dZehlaDypBfNokw==} + + '@types/webxr@0.5.21': + resolution: {integrity: sha512-geZIAtLzjGmgY2JUi6VxXdCrTb99A7yP49lxLr2Nm/uIK0PkkxcEi4OGhoGDO4pxCf3JwGz2GiJL2Ej4K2bKaA==} + '@vitejs/plugin-react@4.3.4': resolution: {integrity: sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: vite: ^4.2.0 || ^5.0.0 || ^6.0.0 + '@webgpu/types@0.1.60': + resolution: {integrity: sha512-8B/tdfRFKdrnejqmvq95ogp8tf52oZ51p3f4QD5m5Paey/qlX4Rhhy5Y8tgFMi7Ms70HzcMMw3EQjH/jdhTwlA==} + abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} @@ -660,6 +687,12 @@ packages: resolution: {integrity: sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==} engines: {node: '>=6'} + eventemitter3@5.0.1: + resolution: {integrity: sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA==} + + fflate@0.8.2: + resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==} + form-data-encoder@1.7.2: resolution: {integrity: sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A==} @@ -816,6 +849,9 @@ packages: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} + meshoptimizer@0.18.1: + resolution: {integrity: sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw==} + mime-db@1.52.0: resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==} engines: {node: '>= 0.6'} @@ -929,6 +965,9 @@ packages: resolution: {integrity: sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==} engines: {node: '>=6'} + three@0.175.0: + resolution: {integrity: sha512-nNE3pnTHxXN/Phw768u0Grr7W4+rumGg/H6PgeseNJojkJtmeHJfZWi41Gp2mpXl1pg1pf1zjwR4McM1jTqkpg==} + to-fast-properties@2.0.0: resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==} engines: {node: '>=4'} @@ -1286,6 +1325,11 @@ snapshots: - bufferutil - utf-8-validate + '@kevisual/store@0.0.4': + dependencies: + eventemitter3: 5.0.1 + path-to-regexp: 8.2.0 + '@kevisual/types@0.0.6': {} '@rollup/rollup-android-arm-eabi@4.34.8': @@ -1406,6 +1450,8 @@ snapshots: tailwindcss: 4.0.16 vite: 6.2.3(@types/node@22.13.13)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.5.1) + '@tweenjs/tween.js@23.1.3': {} + '@types/babel__core@7.20.5': dependencies: '@babel/parser': 7.25.6 @@ -1454,6 +1500,19 @@ snapshots: dependencies: csstype: 3.1.3 + '@types/stats.js@0.17.3': {} + + '@types/three@0.175.0': + dependencies: + '@tweenjs/tween.js': 23.1.3 + '@types/stats.js': 0.17.3 + '@types/webxr': 0.5.21 + '@webgpu/types': 0.1.60 + fflate: 0.8.2 + meshoptimizer: 0.18.1 + + '@types/webxr@0.5.21': {} + '@vitejs/plugin-react@4.3.4(vite@6.2.3(@types/node@22.13.13)(jiti@2.4.2)(lightningcss@1.29.2)(yaml@2.5.1))': dependencies: '@babel/core': 7.26.0 @@ -1465,6 +1524,8 @@ snapshots: transitivePeerDependencies: - supports-color + '@webgpu/types@0.1.60': {} + abort-controller@3.0.0: dependencies: event-target-shim: 5.0.1 @@ -1569,6 +1630,10 @@ snapshots: event-target-shim@5.0.1: {} + eventemitter3@5.0.1: {} + + fflate@0.8.2: {} + form-data-encoder@1.7.2: {} form-data@4.0.2: @@ -1696,6 +1761,8 @@ snapshots: math-intrinsics@1.1.0: {} + meshoptimizer@0.18.1: {} + mime-db@1.52.0: {} mime-types@2.1.35: @@ -1799,6 +1866,8 @@ snapshots: tapable@2.2.1: {} + three@0.175.0: {} + to-fast-properties@2.0.0: {} tr46@0.0.3: {} diff --git a/src/main.tsx b/src/main.tsx index 7cf35ea..d1871ba 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,6 +1,26 @@ -import { createRoot } from 'react-dom/client'; -import { App } from './pages/App.tsx'; +// import { createRoot } from 'react-dom/client'; +// import { App } from './pages/App.tsx'; -import './index.css'; +// import './index.css'; -createRoot(document.getElementById('root')!).render(); +// createRoot(document.getElementById('root')!).render(); +import { render, addCube } from './vr'; +import { addDivDocument, addDivDocument2 } from './vr'; +const root = document.getElementById('root')!; + +render({ + root, + width: window.innerWidth, + height: window.innerHeight, +}); + +addCube(); +addDivDocument(); +addDivDocument2(); +window.addEventListener('resize', () => { + render({ + root, + width: window.innerWidth, + height: window.innerHeight, + }); +}); diff --git a/src/vr.ts b/src/vr.ts new file mode 100644 index 0000000..e34646a --- /dev/null +++ b/src/vr.ts @@ -0,0 +1,124 @@ +import * as THREE from 'three'; +import { useContextKey } from '@kevisual/store/context'; +import { CSS3DRenderer, CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer'; +import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls'; +export const renderer = useContextKey('renderer', () => { + const renderer = new THREE.WebGLRenderer(); + renderer.setSize(window.innerWidth, window.innerHeight); + return renderer; +}); + +export const mainScene = useContextKey('mainScene', () => { + const scene = new THREE.Scene(); + return scene; +}); + +export const cssRenderer = useContextKey('cssRenderer', () => { + const renderer = new CSS3DRenderer(); + renderer.setSize(window.innerWidth, window.innerHeight); + return renderer; +}); +export const cssScene = useContextKey('cssScene', () => { + const scene = new THREE.Scene(); + return scene; +}); + +export const mainCamera = useContextKey('mainCamera', () => { + const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 10000); + return camera; +}); +// export const mainControls = useContextKey('mainControls', () => { +// const controls = new OrbitControls(mainCamera, renderer.domElement); +// return controls; +// }); + +export const render = ({ root, width, height }: { root: HTMLElement; width: number; height: number }) => { + const camera = mainCamera; + + root.appendChild(renderer.domElement); + root.appendChild(cssRenderer.domElement); + + renderer.render(mainScene, camera); + + camera.position.z = 453; + + // 设置CSS渲染器的样式 + cssRenderer.domElement.style.position = 'absolute'; + cssRenderer.domElement.style.top = '0'; + cssRenderer.domElement.style.pointerEvents = 'auto'; + const controls = new OrbitControls(camera, cssRenderer.domElement); + + function animate() { + renderer.render(mainScene, camera); + // cssRenderer.render(cssScene, camera); + controls.update(); + } + renderer.setAnimationLoop(animate); + return { renderer, cssRenderer }; +}; +// add a cube to the scene + +export const addCube = () => { + const geometry = new THREE.BoxGeometry(1000, 1000, 1000); + const material = new THREE.MeshBasicMaterial({ color: 0x00ff00 }); + const cube = new THREE.Mesh(geometry, material); + setInterval(() => { + cube.rotation.x += 0.01; + cube.rotation.y += 0.01; + }, 16); + mainScene.add(cube); +}; + +export const addDivDocument = () => { + const div = document.createElement('div'); + // div.style.width = '1920px'; + // div.style.height = '1080px'; + div.style.width = window.innerWidth + 'px'; + div.style.height = window.innerHeight + 'px'; + div.style.backgroundColor = 'red'; + div.style.overflow = 'scroll'; + div.innerHTML = 'Hello
World'; + for (let i = 0; i < 2000; i++) { + div.innerHTML += 'Hello
World'; + } + // document.body.appendChild(div); + // 挂载到mainScene + const divObject = new CSS3DObject(div); + divObject.position.set(0, 0, 0); + divObject.scale.set(1, 1, 1); + divObject.rotation.set(0, 0, 0); + divObject.visible = true; + let count = 1; + div.addEventListener('click', () => { + console.log('click'); + count++; + }); + + divObject.userData = { + name: 'divObject', + }; + cssScene.add(divObject); + + // setInterval(() => { + // div.style.transform = `translate(${Math.random() * 100}px, ${Math.random() * 100}px)`; + // }, 16); +}; + +export const addDivDocument2 = () => { + const div = document.createElement('div'); + div.style.width = window.innerWidth + 'px'; + div.style.height = window.innerHeight + 'px'; + div.style.backgroundColor = 'blue'; + div.style.overflow = 'scroll'; + div.innerHTML = 'Hello
World'; + for (let i = 0; i < 2000; i++) { + div.innerHTML += 'Hello
World'; + } + document.body.appendChild(div); + const divObject = new CSS3DObject(div); + divObject.position.set(0, -window.innerHeight, 0); + divObject.scale.set(1, 1, 1); + divObject.rotation.set(0, 0, 0); + divObject.visible = true; + cssScene.add(divObject); +}; diff --git a/tsconfig.json b/tsconfig.json index 1ffef60..84c8a23 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -3,5 +3,5 @@ "references": [ { "path": "./tsconfig.app.json" }, { "path": "./tsconfig.node.json" } - ] + ], }