This commit is contained in:
abearxiong 2025-03-31 14:47:04 +08:00
parent 0da5a89e2b
commit 61ca1b362d
7 changed files with 249 additions and 17 deletions

View File

@ -1 +1,6 @@
# vite-react-template
# virtual-assistant
## 项目简介
运行threejs

View File

@ -1,13 +1,24 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Vite + React + TS</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>virtual-assistant</title>
<style>
html,
body {
margin: 0;
padding: 0;
overflow: hidden;
}
</style>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>

View File

@ -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",

69
pnpm-lock.yaml generated
View File

@ -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: {}

View File

@ -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(<App />);
// createRoot(document.getElementById('root')!).render(<App />);
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,
});
});

124
src/vr.ts Normal file
View File

@ -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<br>World';
for (let i = 0; i < 2000; i++) {
div.innerHTML += 'Hello<br>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<br>World';
for (let i = 0; i < 2000; i++) {
div.innerHTML += 'Hello<br>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);
};

View File

@ -3,5 +3,5 @@
"references": [
{ "path": "./tsconfig.app.json" },
{ "path": "./tsconfig.node.json" }
]
],
}