This commit is contained in:
2025-10-15 16:42:09 +08:00
parent 69cdadeda9
commit 87da6371b6
5 changed files with 241 additions and 0 deletions

View File

@@ -34,6 +34,7 @@
"react-dom": "^19.2.0",
"react-toastify": "^11.0.5",
"tailwind-merge": "^3.3.1",
"three": "^0.180.0",
"wavesurfer.js": "^7.11.0",
"zustand": "^5.0.8"
},
@@ -44,6 +45,7 @@
"@kevisual/types": "^0.0.10",
"@types/react": "^19.2.2",
"@types/react-dom": "^19.2.2",
"@types/three": "^0.180.0",
"@vitejs/plugin-basic-ssl": "^2.1.0",
"dotenv": "^17.2.3",
"tailwindcss": "^4.1.14",

59
pnpm-lock.yaml generated
View File

@@ -62,6 +62,9 @@ importers:
tailwind-merge:
specifier: ^3.3.1
version: 3.3.1
three:
specifier: ^0.180.0
version: 0.180.0
wavesurfer.js:
specifier: ^7.11.0
version: 7.11.0
@@ -78,6 +81,9 @@ importers:
'@types/react-dom':
specifier: ^19.2.2
version: 19.2.2(@types/react@19.2.2)
'@types/three':
specifier: ^0.180.0
version: 0.180.0
'@vitejs/plugin-basic-ssl':
specifier: ^2.1.0
version: 2.1.0(vite@6.3.6(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1))
@@ -219,6 +225,9 @@ packages:
resolution: {integrity: sha512-+ntATQe1AlL7nTOYjwjj6w3299CgRot48wL761TUGYpYgAou3AaONZazp0PKZyCyWhudWsjhq1nvRHOvbMzhTA==}
engines: {node: '>=18'}
'@dimforge/rapier3d-compat@0.12.0':
resolution: {integrity: sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==}
'@emnapi/runtime@1.5.0':
resolution: {integrity: sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==}
@@ -843,6 +852,9 @@ packages:
peerDependencies:
vite: ^5.2.0 || ^6 || ^7
'@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==}
@@ -902,12 +914,21 @@ packages:
'@types/sax@1.2.7':
resolution: {integrity: sha512-rO73L89PJxeYM3s3pPPjiPgVVcymqU490g0YO5n5By0k2Erzj6tay/4lr1CHAAU4JyOWd1rpQ8bCf6cZfHU96A==}
'@types/stats.js@0.17.4':
resolution: {integrity: sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==}
'@types/three@0.180.0':
resolution: {integrity: sha512-ykFtgCqNnY0IPvDro7h+9ZeLY+qjgUWv+qEvUt84grhenO60Hqd4hScHE7VTB9nOQ/3QM8lkbNE+4vKjEpUxKg==}
'@types/unist@2.0.11':
resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==}
'@types/unist@3.0.3':
resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
'@types/webxr@0.5.24':
resolution: {integrity: sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==}
'@ungap/structured-clone@1.3.0':
resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
@@ -923,6 +944,9 @@ packages:
peerDependencies:
vite: ^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0
'@webgpu/types@0.1.66':
resolution: {integrity: sha512-YA2hLrwLpDsRueNDXIMqN9NTzD6bCDkuXbOSe0heS+f8YE8usA6Gbv1prj81pzVHrbaAma7zObnIC+I6/sXJgA==}
acorn-jsx@5.3.2:
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies:
@@ -1242,6 +1266,9 @@ packages:
picomatch:
optional: true
fflate@0.8.2:
resolution: {integrity: sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==}
flattie@1.1.1:
resolution: {integrity: sha512-9UbaD6XdAL97+k/n+N7JwX46K/M6Zc6KcFYskrYL8wbBV/Uyk0CTAMY0VT+qiK5PM7AIc9aTWYtq65U7T+aCNQ==}
engines: {node: '>=8'}
@@ -1585,6 +1612,9 @@ packages:
mdn-data@2.12.2:
resolution: {integrity: sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==}
meshoptimizer@0.22.0:
resolution: {integrity: sha512-IebiK79sqIy+E4EgOr+CAw+Ke8hAspXKzBd0JdgEmPHiAwmvEj2S4h1rfvo+o/BnfEYd/jAOg5IeeIjzlzSnDg==}
micromark-core-commonmark@2.0.3:
resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==}
@@ -2049,6 +2079,9 @@ packages:
resolution: {integrity: sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==}
engines: {node: '>=18'}
three@0.180.0:
resolution: {integrity: sha512-o+qycAMZrh+TsE01GqWUxUIKR1AL0S8pq7zDkYOQw8GqfX8b8VoCKYUoHbhiX5j+7hr8XsuHDVU6+gkQJQKg9w==}
tiny-inflate@1.0.3:
resolution: {integrity: sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==}
@@ -2575,6 +2608,8 @@ snapshots:
dependencies:
fontkit: 2.0.4
'@dimforge/rapier3d-compat@0.12.0': {}
'@emnapi/runtime@1.5.0':
dependencies:
tslib: 2.8.1
@@ -3066,6 +3101,8 @@ snapshots:
tailwindcss: 4.1.14
vite: 6.3.6(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1)
'@tweenjs/tween.js@23.1.3': {}
'@types/babel__core@7.20.5':
dependencies:
'@babel/parser': 7.28.4
@@ -3137,10 +3174,24 @@ snapshots:
dependencies:
'@types/node': 17.0.45
'@types/stats.js@0.17.4': {}
'@types/three@0.180.0':
dependencies:
'@dimforge/rapier3d-compat': 0.12.0
'@tweenjs/tween.js': 23.1.3
'@types/stats.js': 0.17.4
'@types/webxr': 0.5.24
'@webgpu/types': 0.1.66
fflate: 0.8.2
meshoptimizer: 0.22.0
'@types/unist@2.0.11': {}
'@types/unist@3.0.3': {}
'@types/webxr@0.5.24': {}
'@ungap/structured-clone@1.3.0': {}
'@vitejs/plugin-basic-ssl@2.1.0(vite@6.3.6(@types/node@24.7.2)(jiti@2.6.1)(lightningcss@1.30.1))':
@@ -3159,6 +3210,8 @@ snapshots:
transitivePeerDependencies:
- supports-color
'@webgpu/types@0.1.66': {}
acorn-jsx@5.3.2(acorn@8.15.0):
dependencies:
acorn: 8.15.0
@@ -3540,6 +3593,8 @@ snapshots:
optionalDependencies:
picomatch: 4.0.3
fflate@0.8.2: {}
flattie@1.1.1: {}
fontace@0.3.1:
@@ -4049,6 +4104,8 @@ snapshots:
mdn-data@2.12.2: {}
meshoptimizer@0.22.0: {}
micromark-core-commonmark@2.0.3:
dependencies:
decode-named-character-reference: 1.2.0
@@ -4763,6 +4820,8 @@ snapshots:
minizlib: 3.1.0
yallist: 5.0.0
three@0.180.0: {}
tiny-inflate@1.0.3: {}
tinyexec@1.0.1: {}

4
src/apps/aura/index.tsx Normal file
View File

@@ -0,0 +1,4 @@
export const AuraApp = () => {
return <div>Aura App</div>;
}

17
src/pages/aura.astro Normal file
View File

@@ -0,0 +1,17 @@
---
import { AuraApp } from '../apps/aura/index';
import '../styles/global.css';
---
<html>
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>语音录制与转文字</title>
</head>
<body>
<div class="min-h-screen bg-gray-100 py-8">
<AuraApp client:load />
</div>
</body>
</html>

159
src/pages/test/01.html Normal file
View File

@@ -0,0 +1,159 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Three.js Torus Ripple Effect</title>
<style>
body { margin: 0; overflow: hidden; }
canvas { display: block; }
</style>
</head>
<body>
<script type="importmap">
{
"imports": {
"three": "https://unpkg.com/three@latest/build/three.module.js",
"three/": "https://unpkg.com/three@latest/"
}
}
</script>
<script type="module">
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
// 场景、相机、渲染器
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x000000);
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 0, 5);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
document.body.appendChild(renderer.domElement);
// 控制器
const controls = new OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
// 光源
const ambientLight = new THREE.AmbientLight(0xffffff, 0.5);
scene.add(ambientLight);
const pointLight = new THREE.PointLight(0xffffff, 1);
pointLight.position.set(5, 5, 5);
scene.add(pointLight);
// 自定义着色器材质(带波纹效果)
const vertexShader = `
uniform float time;
uniform vec3 rippleCenter;
uniform float rippleRadius;
varying vec3 vPosition;
float rippleEffect(vec3 pos, vec3 center, float radius) {
float dist = distance(pos.xz, center.xz);
if (dist < radius + 1.0 && dist > radius - 1.0) {
return sin((dist - radius) * 3.1415 * 2.0) * 0.2 * (1.0 - abs(dist - radius));
}
return 0.0;
}
void main() {
vPosition = position;
float effect = 0.0;
if (rippleRadius > 0.0) {
effect = rippleEffect(position, rippleCenter, rippleRadius);
}
vec3 pos = position + normal * effect;
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}
`;
const fragmentShader = `
varying vec3 vPosition;
void main() {
vec3 color = vec3(0.2, 0.5, 1.0); // 蓝色调
float brightness = 0.5 + 0.5 * sin(vPosition.x * 2.0 + vPosition.y * 2.0 + vPosition.z * 2.0);
gl_FragColor = vec4(color * brightness, 1.0);
}
`;
// 材质参数
const materialUniforms = {
time: { value: 0.0 },
rippleCenter: { value: new THREE.Vector3(0, 0, 0) },
rippleRadius: { value: 0.0 }
};
const material = new THREE.ShaderMaterial({
uniforms: materialUniforms,
vertexShader: vertexShader,
fragmentShader: fragmentShader,
wireframe: false
});
// 创建圆环
const geometry = new THREE.TorusGeometry(2, 0.4, 32, 100);
const torus = new THREE.Mesh(geometry, material);
torus.rotation.x = Math.PI * 0.5; // 平放
scene.add(torus);
// 射线拾取
const raycaster = new THREE.Raycaster();
const mouse = new THREE.Vector2();
function onMouseClick(event) {
// 标准化鼠标坐标
mouse.x = (event.clientX / window.innerWidth) * 2 - 1;
mouse.y = -(event.clientY / window.innerHeight) * 2 + 1;
raycaster.setFromCamera(mouse, camera);
const intersects = raycaster.intersectObject(torus);
if (intersects.length > 0) {
const hitPoint = intersects[0].point;
console.log('点击位置:', hitPoint);
// 重置波纹
materialUniforms.rippleCenter.value.copy(hitPoint);
materialUniforms.rippleRadius.value = 0.0;
// 开始波纹动画
let radius = 0;
const animateRipple = () => {
radius += 0.1; // 波纹扩散速度
materialUniforms.rippleRadius.value = radius;
if (radius < 10.0) {
requestAnimationFrame(animateRipple);
} else {
materialUniforms.rippleRadius.value = 0.0; // 结束
}
};
animateRipple();
}
}
window.addEventListener('click', onMouseClick, false);
// 自适应窗口
window.addEventListener('resize', () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
});
// 动画循环
function animate() {
requestAnimationFrame(animate);
materialUniforms.time.value += 0.01;
controls.update();
renderer.render(scene, camera);
}
animate();
</script>
</body>
</html>