1
0

feat: Add company introduction, about us, feature descriptions, user agreement, and privacy policy documents

- Created company introduction document with details about the studio, contact information, and business scope.
- Added an "About Us" document outlining the team's mission, vision, and contact details.
- Developed a comprehensive feature introduction for the kevisual assistant, detailing various functionalities.
- Established a user agreement outlining terms of use, intellectual property rights, and privacy policy.
- Implemented a privacy policy document explaining information collection, usage, and security measures.

feat: Enhance the skill-beautiful app with interactive features and improved UI

- Integrated THREE.js for 3D rendering and added control modules for user interaction.
- Created a dynamic information panel with CSS3D rendering for better user experience.
- Implemented particle systems and lighting effects to enhance visual appeal.
- Added keyboard controls for camera manipulation and information display toggling.

fix: Update routing and rendering logic in the app provider

- Adjusted the app provider to ensure proper cleanup of resources on component unmount.
- Improved the layout and styling of the main application container for better responsiveness.

chore: Set up info page with proper metadata and styling

- Created an info page with relevant metadata for SEO and user engagement.
- Imported necessary styles for consistent design across the application.
This commit is contained in:
2025-12-28 11:27:34 +08:00
parent 2bc8822f60
commit 5d3c95d111
20 changed files with 5767 additions and 1097 deletions

View File

@@ -1,6 +0,0 @@
{
"workbench.editorAssociations": {
// "*.md": "vscode.markdown.preview.editor" // 预览打开
"*.md": "default" // 默认打开
}
}

View File

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 134 KiB

View File

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 74 KiB

View File

Before

Width:  |  Height:  |  Size: 464 KiB

After

Width:  |  Height:  |  Size: 464 KiB

View File

@@ -1,43 +1,33 @@
# Astro Starter Kit: Minimal # 可视化助手
```sh ## 我期望实现的功能
pnpm create astro@latest -- --template minimal
```
> 🧑‍🚀 **Seasoned astronaut?** Delete this file. Have fun! 人在网络上生活的方面的所有内容,都能更好地被管理和使用。学习、工作、娱乐、社交等各个方面的内容,都能被整合在自己的可视化的空间中。这空间具备自执行的能力,能主动帮助人完成各项任务,操作系统,浏览器,应用程序。
## 🚀 Project Structure ## 方向
Inside of your Astro project, you'll see the following folders and files: ### 1. 知识库
```text 构建一个个人知识库,能存储和管理各种类型的信息,包括文本、图片、音频、视频等。知识库支持标签、分类、搜索等功能,方便用户快速找到所需信息。非文本,则需要转换为文本进行存储和管理。
/
├── public/
├── src/
│ └── pages/
│ └── index.astro
└── package.json
```
Astro looks for `.astro` or `.md` files in the `src/pages/` directory. Each page is exposed as a route based on its file name. ### 2. 代码编辑器
There's nothing special about `src/components/`, but that's where we like to put any Astro/React/Vue/Svelte/Preact components. 一切底层运行必开发,如果没有开发环境,那么就需要一个云端代码编辑器,能让用户在浏览器中编写代码,并且能运行和调试代码。
Any static assets, like images, can be placed in the `public/` directory. ### 3. 可视化编程
## 🧞 Commands 对话式编程,图形化编程,模块化编程,积木式编程等方式,让用户能更方便地创建程序。
All commands are run from the root of the project, from a terminal: ### 4. 数据存储
| Command | Action | 云端如何对个人用户来说,如何存储数据,想实现的功能,网页如何更简便获取数据。
| :------------------------ | :----------------------------------------------- |
| `pnpm install` | Installs dependencies | ### 5. 自动化任务
| `pnpm dev` | Starts local dev server at `localhost:4321` |
| `pnpm build` | Build your production site to `./dist/` | 描述一个简单的需求,自动根据需求生成功能。
| `pnpm preview` | Preview your build locally, before deploying |
| `pnpm astro ...` | Run CLI commands like `astro add`, `astro check` | ### 6. AI 交互
| `pnpm astro -- --help` | Get help using the Astro CLI |
通过自然语言与 AI 进行交互完成各种任务如信息查询、内容生成、数据分析等。AI 能理解用户的意图,并提供相应的帮助和建议。
## 👀 Want to learn more?
Feel free to check [our documentation](https://docs.astro.build) or jump into our [Discord server](https://astro.build/chat).

View File

@@ -11,11 +11,13 @@
"pub": "envision deploy ./dist -k official -v 0.0.4 -u -o root" "pub": "envision deploy ./dist -k official -v 0.0.4 -u -o root"
}, },
"dependencies": { "dependencies": {
"astro": "^5.12.3" "astro": "^5.16.6",
"three": "^0.182.0"
}, },
"devDependencies": { "devDependencies": {
"@astrojs/mdx": "^4.3.1", "@astrojs/mdx": "^4.3.13",
"@astrojs/react": "^4.3.0", "@astrojs/react": "^4.4.2",
"@astrojs/sitemap": "^3.4.1" "@astrojs/sitemap": "^3.6.0",
"@types/three": "^0.182.0"
} }
} }

View File

@@ -0,0 +1,10 @@
export const Beian = () => {
return <div
className='mt-12 pt-8 border-t border-gray-800 text-center text-gray-400 fixed bottom-0 w-full bg-black'
onClick={() => {
window.open('https://beian.miit.gov.cn/', '_blank');
}}>
<p>ICP备2025158778号</p>
<p>© 2025 . All rights reserved.</p>
</div>
}

View File

@@ -1,4 +1,4 @@
import { Mail, Phone, MapPin, Book, Globe, Brain, Save } from 'lucide-react'; import { Mail, Phone, MapPin, Book, Globe, Brain, Save, Database, Bot, Code2, Workflow, Sparkles } from 'lucide-react';
import { ToastContainer } from 'react-toastify'; import { ToastContainer } from 'react-toastify';
import { Provider } from './Provider'; import { Provider } from './Provider';
import Logo from './assets/logo-1.png'; import Logo from './assets/logo-1.png';
@@ -16,7 +16,7 @@ export const Main = () => {
<div className='min-h-screen bg-gray-50'> <div className='min-h-screen bg-gray-50'>
{/* Hero Section */} {/* Hero Section */}
<div className=''> <div className=''>
<nav className='px-4 mx-auto h-16 flex justify-between items-center bg-white border-b border-b-gray-200 w-full'> <nav className='px-4 mx-auto h-16 flex justify-between items-center bg-white border-b border-b-gray-200 w-full mr-20'>
<div <div
className='flex items-center space-x-4 cursor-pointer' className='flex items-center space-x-4 cursor-pointer'
onClick={() => { onClick={() => {
@@ -29,108 +29,104 @@ export const Main = () => {
className='hover:text-gray-400 cursor-pointer' className='hover:text-gray-400 cursor-pointer'
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
const url = new URL('/root/center/', getOrigin()); const url = new URL('/root/home/', getOrigin());
window.open(url.toString(), '_blank'); window.open(url.toString(), '_blank');
}}> }}>
</a> </a>
<a href='#features' className='hover:text-gray-400 cursor-pointer'>
</a>
<a href='#contact' className='hover:text-gray-400 cursor-pointer'>
</a>
</div> </div>
</nav> </nav>
</div> </div>
<article className='container mx-auto px-6 py-10 bg-gray-50'> <article className='container mx-auto px-6 py-16 bg-gray-50'>
<div className='mx-auto bg-white p-8 py-10 rounded-lg shadow-lg'> {/* 主标题区域 */}
<h2 className='text-3xl font-bold mb-8 text-center'></h2> <div className='text-center mb-16'>
<p className='text-gray-700 mb-6'> <h1 className='text-5xl font-bold text-gray-900 mb-6'></h1>
Kevisual设计助手提供强大的网页部署平台AI生成的网页 <p className='text-xl text-gray-600 max-w-4xl mx-auto leading-relaxed'>
使
</p> </p>
<div className='grid md:grid-cols-2 gap-8 mt-10'>
<div>
<h3 className='text-xl font-semibold mb-4'></h3>
<ul className='list-disc pl-5 space-y-2 text-gray-600'>
<li></li>
<li></li>
<li></li>
<li>访</li>
</ul>
</div> </div>
<div>
<h3 className='text-xl font-semibold mb-4'>使</h3> {/* 功能方向卡片 */}
<ul className='list-disc pl-5 space-y-2 text-gray-600'> <div className='mb-12'>
<li></li> <h2 className='text-3xl font-bold text-gray-900 text-center mb-12'></h2>
<li className='text-blue-600 hover:underline cursor-pointer' onClick={openEditor}> <div className='grid md:grid-cols-2 lg:grid-cols-3 gap-8 max-w-7xl mx-auto'>
{/* 知识库 */}
</li> <div className='bg-white rounded-xl shadow-lg p-8 hover:shadow-xl transition-shadow duration-300 border border-gray-100'>
<li>AI生成内容的发布与管理</li> <div className='bg-linear-to-br from-blue-500 to-blue-600 w-14 h-14 rounded-lg flex items-center justify-center mb-6'>
<li>线</li> <Database className='w-7 h-7 text-white' />
</ul> </div>
<h3 className='text-2xl font-bold text-gray-900 mb-4'></h3>
<p className='text-gray-600 leading-relaxed'>
便
</p>
</div>
{/* 代码编辑器 */}
<div className='bg-white rounded-xl shadow-lg p-8 hover:shadow-xl transition-shadow duration-300 border border-gray-100'>
<div className='bg-linear-to-br from-purple-500 to-purple-600 w-14 h-14 rounded-lg flex items-center justify-center mb-6'>
<Code2 className='w-7 h-7 text-white' />
</div>
<h3 className='text-2xl font-bold text-gray-900 mb-4'></h3>
<p className='text-gray-600 leading-relaxed'>
</p>
</div>
{/* 可视化编程 */}
<div className='bg-white rounded-xl shadow-lg p-8 hover:shadow-xl transition-shadow duration-300 border border-gray-100'>
<div className='bg-linear-to-br from-green-500 to-green-600 w-14 h-14 rounded-lg flex items-center justify-center mb-6'>
<Workflow className='w-7 h-7 text-white' />
</div>
<h3 className='text-2xl font-bold text-gray-900 mb-4'></h3>
<p className='text-gray-600 leading-relaxed'>
便
</p>
</div>
{/* 数据存储 */}
<div className='bg-white rounded-xl shadow-lg p-8 hover:shadow-xl transition-shadow duration-300 border border-gray-100'>
<div className='bg-linear-to-br from-orange-500 to-orange-600 w-14 h-14 rounded-lg flex items-center justify-center mb-6'>
<Save className='w-7 h-7 text-white' />
</div>
<h3 className='text-2xl font-bold text-gray-900 mb-4'></h3>
<p className='text-gray-600 leading-relaxed'>
便
</p>
</div>
{/* 自动化任务 */}
<div className='bg-white rounded-xl shadow-lg p-8 hover:shadow-xl transition-shadow duration-300 border border-gray-100'>
<div className='bg-linear-to-br from-pink-500 to-pink-600 w-14 h-14 rounded-lg flex items-center justify-center mb-6'>
<Sparkles className='w-7 h-7 text-white' />
</div>
<h3 className='text-2xl font-bold text-gray-900 mb-4'></h3>
<p className='text-gray-600 leading-relaxed'>
</p>
</div>
{/* AI 交互 */}
<div className='bg-white rounded-xl shadow-lg p-8 hover:shadow-xl transition-shadow duration-300 border border-gray-100'>
<div className='bg-linear-to-br from-cyan-500 to-cyan-600 w-14 h-14 rounded-lg flex items-center justify-center mb-6'>
<Bot className='w-7 h-7 text-white' />
</div>
<h3 className='text-2xl font-bold text-gray-900 mb-4'>AI </h3>
<p className='text-gray-600 leading-relaxed'>
AI AI
</p>
</div> </div>
</div> </div>
<div className='mt-8 text-center'>
<a href='./docs/features' className='inline-flex items-center text-blue-600 hover:underline font-medium cursor-not-allowed'>
<svg className='w-4 h-4 ml-1' fill='none' stroke='currentColor' viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'>
<path strokeLinecap='round' strokeLinejoin='round' strokeWidth='2' d='M9 5l7 7-7 7'></path>
</svg>
</a>
</div> </div>
{/* 愿景声明 */}
<div className='mt-20 bg-linear-to-r from-indigo-600 to-purple-600 rounded-2xl p-12 text-center text-white shadow-2xl'>
<h2 className='text-3xl font-bold mb-4'></h2>
<p className='text-lg opacity-90 max-w-3xl mx-auto'>
</p>
</div> </div>
</article> </article>
{/* Features Section */}
<section id='features' className='container mx-auto px-6 py-10 bg-gray-50'>
<div className='mx-auto bg-white p-8 py-10 rounded-lg shadow-lg'>
<h2 className='text-3xl font-bold text-center mb-16'></h2>
<div className='grid md:grid-cols-3 gap-12'>
<div className='p-6 rounded-lg shadow-lg bg-white'>
<Globe className='w-12 h-12 text-blue-600 mb-4' />
<h3 className='text-xl font-semibold mb-4'></h3>
<p className='text-gray-600'></p>
</div>
<div className='p-6 rounded-lg shadow-lg bg-white'>
<Brain className='w-12 h-12 text-blue-600 mb-4' />
<h3 className='text-xl font-semibold mb-4'></h3>
<p className='text-gray-600'>访</p>
</div>
<div className='p-6 rounded-lg shadow-lg bg-white'>
<Book className='w-12 h-12 text-blue-600 mb-4' />
<h3 className='text-xl font-semibold mb-4'></h3>
<p className='text-gray-600'></p>
</div>
</div>
</div>
</section>
{/* Contact Section */}
<section id='contact' className='container mx-auto px-6 py-10 bg-gray-50'>
<div className='mx-auto bg-white p-8 py-10 rounded-lg shadow-lg'>
<h2 className='text-3xl font-bold text-center mb-16'></h2>
<div className='max-w-2xl mx-auto w-[300px]'>
<div className='space-y-6'>
<div className='flex items-center space-x-4'>
<Mail className='w-6 h-6 text-blue-600' />
<span>kevisual@kevisual.cn</span>
</div>
<div className='flex items-center space-x-4'>
<Phone className='w-6 h-6 text-blue-600' />
<span>18324451015</span>
</div>
<div className='flex items-center space-x-4'>
<MapPin className='w-6 h-6 text-blue-600' />
<span></span>
</div>
</div>
</div>
</div>
</section>
{/* Footer */} {/* Footer */}
<footer className='bg-gray-900 text-white py-12'> <footer className='bg-gray-900 text-white py-12'>

View File

@@ -1,5 +1,4 @@
import ConfigProvider from 'antd/lib/config-provider'; import ConfigProvider from 'antd/lib/config-provider';
import '@ant-design/v5-patch-for-react-19';
export const Provider = ({ children }: { children: React.ReactNode }) => { export const Provider = ({ children }: { children: React.ReactNode }) => {
return ( return (

View File

@@ -0,0 +1,676 @@
import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { CSS3DRenderer, CSS3DObject } from 'three/examples/jsm/renderers/CSS3DRenderer.js';
// ============ 控制模块配置 ============
interface ControlOptions {
scene: THREE.Scene;
camera: THREE.Camera;
renderer: THREE.WebGLRenderer;
}
interface ControlState {
autoRotate: boolean;
showHelp: boolean;
showInfo: boolean;
}
const createControlModule = (opts: ControlOptions) => {
const { scene, camera, renderer } = opts;
// 轨道控制器
const controls = new OrbitControls(camera as THREE.PerspectiveCamera, renderer.domElement);
controls.enableDamping = true; // 启用阻尼效果
controls.dampingFactor = 0.05;
controls.rotateSpeed = 0.5;
controls.zoomSpeed = 1.0;
controls.panSpeed = 0.8;
// 限制范围
controls.minDistance = 5;
controls.maxDistance = 30;
controls.maxPolarAngle = Math.PI / 2 - 0.1; // 防止看到地面以下
controls.minPolarAngle = Math.PI / 6;
// 目标点
controls.target.set(0, 2, 0);
// 控制状态
const state: ControlState = {
autoRotate: false,
showHelp: false,
showInfo: false
};
// 键盘控制
const handleKeyPress = (event: KeyboardEvent) => {
switch (event.key.toLowerCase()) {
case 'r':
// 重置视角
(camera as THREE.PerspectiveCamera).position.set(0, 8, 12);
controls.target.set(0, 2, 0);
break;
case 'a':
// 切换自动旋转
state.autoRotate = !state.autoRotate;
controls.autoRotate = state.autoRotate;
console.log(`自动旋转: ${state.autoRotate ? '开启' : '关闭'}`);
break;
case 'h':
// 切换帮助信息
state.showHelp = !state.showHelp;
break;
case 'i':
// 切换详细信息
state.showInfo = !state.showInfo;
break;
case 'arrowup':
// 向上平移视角
controls.target.y += 0.5;
break;
case 'arrowdown':
// 向下平移视角
controls.target.y -= 0.5;
break;
}
};
window.addEventListener('keydown', handleKeyPress);
// 创建帮助面板
const createHelpPanel = () => {
const panel = document.createElement('div');
panel.id = 'help-panel';
panel.style.cssText = `
position: fixed;
top: 20px;
right: 20px;
background: rgba(10, 10, 26, 0.9);
border: 1px solid rgba(0, 255, 255, 0.3);
border-radius: 10px;
padding: 20px;
color: #00ffff;
font-family: 'Microsoft YaHei', sans-serif;
font-size: 14px;
z-index: 1000;
box-shadow: 0 0 20px rgba(0, 255, 255, 0.2);
display: none;
`;
panel.innerHTML = `
<div style="margin-bottom: 15px; font-size: 16px; font-weight: bold; color: #ff00ff;">
🎮 控制说明
</div>
<div style="line-height: 1.8;">
<div>🖱️ <strong>鼠标左键</strong>: 旋转视角</div>
<div>🖱️ <strong>鼠标右键</strong>: 平移视角</div>
<div>🖱️ <strong>滚轮</strong>: 缩放</div>
<div>⌨️ <strong>R键</strong>: 重置视角</div>
<div>⌨️ <strong>A键</strong>: 自动旋转开关</div>
<div>⌨️ <strong>H键</strong>: 显示/隐藏帮助</div>
<div>⌨️ <strong>↑/↓</strong>: 上下移动视角</div>
<div>🔗 <strong>右侧按钮</strong>: 显示/隐藏信息面板</div>
</div>
`;
document.body.appendChild(panel);
return panel;
};
const helpPanel = createHelpPanel();
// 更新帮助面板显示
const updateHelpPanel = () => {
helpPanel.style.display = state.showHelp ? 'block' : 'none';
};
// 监听H键切换帮助面板
const originalHandleKeyPress = handleKeyPress;
window.removeEventListener('keydown', originalHandleKeyPress);
window.addEventListener('keydown', handleKeyPress);
// 创建信息面板
const createInfoPanel = () => {
const panel = document.createElement('div');
panel.id = 'info-panel';
panel.style.cssText = `
position: fixed;
bottom: 20px;
left: 20px;
background: rgba(10, 10, 26, 0.9);
border: 1px solid rgba(255, 0, 255, 0.3);
border-radius: 10px;
padding: 15px;
color: #ff00ff;
font-family: 'Microsoft YaHei', sans-serif;
font-size: 12px;
z-index: 1000;
box-shadow: 0 0 20px rgba(255, 0, 255, 0.2);
display: none;
`;
const updateInfo = () => {
const camPos = (camera as THREE.PerspectiveCamera).position;
panel.innerHTML = `
<div style="font-weight: bold; margin-bottom: 10px; color: #00ffff;">
📊 相机信息
</div>
<div style="line-height: 1.6;">
<div>位置 X: ${camPos.x.toFixed(2)}</div>
<div>位置 Y: ${camPos.y.toFixed(2)}</div>
<div>位置 Z: ${camPos.z.toFixed(2)}</div>
<div>目标: (${controls.target.x.toFixed(2)}, ${controls.target.y.toFixed(2)}, ${controls.target.z.toFixed(2)})</div>
<div>自动旋转: ${state.autoRotate ? '✅' : '❌'}</div>
</div>
`;
};
document.body.appendChild(panel);
return { panel, updateInfo };
};
const { panel: infoPanel, updateInfo: updateInfoPanel } = createInfoPanel();
// 返回控制接口
return {
controls,
state,
// 在动画循环中调用
update: () => {
controls.update();
if (state.showInfo) {
updateInfoPanel();
}
updateHelpPanel();
},
// 清理
dispose: () => {
window.removeEventListener('keydown', handleKeyPress);
controls.dispose();
helpPanel.remove();
infoPanel.remove();
}
};
};
export const render = () => {
// 场景设置
const container = document.getElementById('container');
if (!container) return;
const scene = new THREE.Scene();
scene.background = new THREE.Color(0x0a0a1a);
scene.fog = new THREE.Fog(0x0a0a1a, 10, 50);
const camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 8, 12);
camera.lookAt(0, 2, 0);
const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(window.innerWidth, window.innerHeight);
renderer.setPixelRatio(window.devicePixelRatio);
renderer.shadowMap.enabled = true;
renderer.shadowMap.type = THREE.PCFSoftShadowMap;
container.innerHTML = '';
container.appendChild(renderer.domElement);
// 创建 CSS3D 渲染器(用于 HTML/CSS 元素)
const cssRenderer = new CSS3DRenderer();
cssRenderer.setSize(window.innerWidth, window.innerHeight);
cssRenderer.domElement.style.position = 'absolute';
cssRenderer.domElement.style.top = '0';
cssRenderer.domElement.style.left = '0';
cssRenderer.domElement.style.pointerEvents = 'none'; // 允许点击穿透到 WebGL
container.appendChild(cssRenderer.domElement);
// ============ 模块1流光旋转地面 ============
const groundGeometry = new THREE.PlaneGeometry(60, 60, 100, 100);
// 创建流光效果的着色器材质
const groundMaterial = new THREE.ShaderMaterial({
uniforms: {
time: { value: 0 },
color1: { value: new THREE.Color(0x00ffff) },
color2: { value: new THREE.Color(0xff00ff) },
color3: { value: new THREE.Color(0x0066ff) }
},
vertexShader: `
varying vec2 vUv;
varying float vElevation;
uniform float time;
void main() {
vUv = uv;
vec3 pos = position;
float elevation = sin(pos.x * 0.3 + time) * 0.3 +
sin(pos.y * 0.2 + time * 0.8) * 0.2 +
sin((pos.x + pos.y) * 0.1 + time * 1.2) * 0.15;
vElevation = elevation;
pos.z += elevation;
gl_Position = projectionMatrix * modelViewMatrix * vec4(pos, 1.0);
}
`,
fragmentShader: `
varying vec2 vUv;
varying float vElevation;
uniform float time;
uniform vec3 color1;
uniform vec3 color2;
uniform vec3 color3;
void main() {
vec2 center = vec2(0.5, 0.5);
float dist = distance(vUv, center);
// 旋转流光效果
float angle = atan(vUv.y - 0.5, vUv.x - 0.5);
float rotatingAngle = angle + time * 0.5;
// 多层流光
float light1 = sin(rotatingAngle * 3.0 + dist * 20.0 - time * 2.0) * 0.5 + 0.5;
float light2 = sin(rotatingAngle * 5.0 - dist * 15.0 + time * 1.5) * 0.5 + 0.5;
float light3 = sin(dist * 30.0 - time * 3.0) * 0.5 + 0.5;
// 流光线条
float lines = smoothstep(0.4, 0.6, light1) * smoothstep(0.6, 0.4, light1);
lines += smoothstep(0.45, 0.55, light2) * smoothstep(0.55, 0.45, light2) * 0.5;
// 距离衰减
float fade = 1.0 - smoothstep(0.0, 0.5, dist);
// 颜色混合
vec3 color = mix(color1, color2, light3);
color = mix(color, color3, light2 * 0.5);
// 添加发光
float glow = lines * 2.0 * fade;
color += glow * color1;
// 基础网格
float gridX = step(0.98, fract(vUv.x * 40.0));
float gridY = step(0.98, fract(vUv.y * 40.0));
float grid = max(gridX, gridY) * 0.1 * fade;
color += grid * vec3(0.2, 0.4, 0.6);
// 边缘渐隐
float edgeFade = smoothstep(0.5, 0.3, dist);
gl_FragColor = vec4(color * edgeFade, edgeFade * 0.9);
}
`,
transparent: true,
side: THREE.DoubleSide
});
const ground = new THREE.Mesh(groundGeometry, groundMaterial);
ground.rotation.x = -Math.PI / 2;
ground.receiveShadow = true;
scene.add(ground);
// ============ 添加信息面板模块(使用 CSS3DObject居中显示============
const createIframePanel3D = (camera: THREE.PerspectiveCamera, controls: OrbitControls) => {
// ============ 相机移动配置 ============
// 可调整的目标相机位置参数
const cameraTargetConfig = {
position: {
x: 0, // 相机目标位置 X
y: 3.5, // 相机目标位置 Y与面板中心对齐
z: 8 // 相机目标位置 Z距离面板的距离可调整
},
target: {
x: 0, // 控制器目标点 X面板中心
y: 3.5, // 控制器目标点 Y面板中心高度
z: 0 // 控制器目标点 Z面板中心
},
duration: 1500 // 动画持续时间(毫秒),可调整
};
// 平滑移动相机到目标位置的函数
const moveCameraToPanel = () => {
const startPosition = {
x: camera.position.x,
y: camera.position.y,
z: camera.position.z
};
const startTarget = {
x: controls.target.x,
y: controls.target.y,
z: controls.target.z
};
const startTime = Date.now();
const animateCamera = () => {
const elapsed = Date.now() - startTime;
const progress = Math.min(elapsed / cameraTargetConfig.duration, 1);
// 使用缓动函数easeInOutCubic
const easeProgress = progress < 0.5
? 4 * progress * progress * progress
: 1 - Math.pow(-2 * progress + 2, 3) / 2;
// 插值更新相机位置
camera.position.x = startPosition.x + (cameraTargetConfig.position.x - startPosition.x) * easeProgress;
camera.position.y = startPosition.y + (cameraTargetConfig.position.y - startPosition.y) * easeProgress;
camera.position.z = startPosition.z + (cameraTargetConfig.position.z - startPosition.z) * easeProgress;
// 插值更新控制器目标点
controls.target.x = startTarget.x + (cameraTargetConfig.target.x - startTarget.x) * easeProgress;
controls.target.y = startTarget.y + (cameraTargetConfig.target.y - startTarget.y) * easeProgress;
controls.target.z = startTarget.z + (cameraTargetConfig.target.z - startTarget.z) * easeProgress;
controls.update();
if (progress < 1) {
requestAnimationFrame(animateCamera);
}
};
animateCamera();
};
// 创建 HTML 元素
const div = document.createElement('div');
div.style.cssText = `
width: 1400px;
height: 700px;
background: rgba(10, 10, 26, 0.95);
border: 2px solid rgba(0, 255, 255, 0.5);
border-radius: 15px;
box-shadow: 0 0 30px rgba(0, 255, 255, 0.3), inset 0 0 20px rgba(0, 255, 255, 0.1);
overflow: hidden;
`;
// 创建标题栏
const header = document.createElement('div');
header.style.cssText = `
background: linear-gradient(90deg, rgba(0, 255, 255, 0.2), rgba(255, 0, 255, 0.2));
padding: 12px 15px;
border-bottom: 1px solid rgba(0, 255, 255, 0.3);
display: flex;
justify-content: space-between;
align-items: center;
`;
const title = document.createElement('div');
title.textContent = '我的信息面板';
title.style.cssText = `
color: #00ffff;
font-family: 'Microsoft YaHei', sans-serif;
font-size: 16px;
font-weight: bold;
text-shadow: 0 0 10px rgba(0, 255, 255, 0.5);
cursor: pointer;
user-select: none;
transition: all 0.3s;
`;
// 鼠标悬停效果
title.onmouseover = () => {
title.style.color = '#ff00ff';
title.style.textShadow = '0 0 15px rgba(255, 0, 255, 0.8)';
};
title.onmouseout = () => {
title.style.color = '#00ffff';
title.style.textShadow = '0 0 10px rgba(0, 255, 255, 0.5)';
};
// 点击标题时移动相机到面板前方
title.onclick = () => {
moveCameraToPanel();
};
const closeButton = document.createElement('button');
closeButton.textContent = '✕';
closeButton.style.cssText = `
background: rgba(255, 0, 100, 0.3);
border: 1px solid rgba(255, 0, 100, 0.5);
color: #fff;
width: 28px;
height: 28px;
border-radius: 5px;
cursor: pointer;
font-size: 16px;
transition: all 0.2s;
`;
closeButton.onmouseover = () => {
closeButton.style.background = 'rgba(255, 0, 100, 0.6)';
};
closeButton.onmouseout = () => {
closeButton.style.background = 'rgba(255, 0, 100, 0.3)';
};
closeButton.onclick = () => {
// 隐藏面板
css3dObject.visible = false;
};
header.appendChild(title);
header.appendChild(closeButton);
// 创建 iframe 容器
const iframeContainer = document.createElement('div');
iframeContainer.style.cssText = `
width: 100%;
height: calc(100% - 50px);
position: relative;
background: rgba(0, 0, 0, 0.3);
`;
// 创建 iframe
const iframe = document.createElement('iframe');
iframe.src = '/info/';
iframe.style.cssText = `
width: 100%;
height: 100%;
border: none;
background: transparent;
`;
iframeContainer.appendChild(iframe);
// 添加发光装饰角
const corners = ['top-left', 'top-right', 'bottom-left', 'bottom-right'];
corners.forEach(corner => {
const cornerEl = document.createElement('div');
cornerEl.style.cssText = `
position: absolute;
width: 20px;
height: 20px;
border: 2px solid #00ffff;
${corner.includes('top') ? 'top: -2px;' : 'bottom: -2px;'}
${corner.includes('left') ? 'left: -2px;' : 'right: -2px;'}
${corner.includes('top') ? corner.includes('left') ? 'border-right: none;' : 'border-left: none;' : corner.includes('left') ? 'border-right: none;' : 'border-left: none;'}
${corner.includes('top') ? corner.includes('left') ? 'border-bottom: none;' : 'border-bottom: none;' : corner.includes('left') ? 'border-top: none;' : 'border-top: none;'}
box-shadow: 0 0 10px rgba(0, 255, 255, 0.5);
`;
div.appendChild(cornerEl);
});
div.appendChild(header);
div.appendChild(iframeContainer);
// 创建 CSS3DObject
const css3dObject = new CSS3DObject(div);
css3dObject.position.set(0, 3.5, 0); // 居中显示
css3dObject.rotation.y = 0; // 面向正前方
css3dObject.scale.set(0.01, 0.01, 0.01); // 缩放以适应场景
scene.add(css3dObject);
// 添加打开按钮(固定在屏幕上)
const openButton = document.createElement('button');
openButton.id = 'open-iframe-btn';
openButton.textContent = '🔗 打开信息面板';
openButton.style.cssText = `
position: fixed;
top: 50%;
right: 20px;
transform: translateY(-50%);
background: linear-gradient(135deg, rgba(0, 255, 255, 0.2), rgba(255, 0, 255, 0.2));
border: 2px solid rgba(0, 255, 255, 0.5);
color: #00ffff;
padding: 12px 24px;
border-radius: 25px;
cursor: pointer;
font-family: 'Microsoft YaHei', sans-serif;
font-size: 14px;
font-weight: bold;
text-shadow: 0 0 10px rgba(0, 255, 255, 0.5);
box-shadow: 0 0 20px rgba(0, 255, 255, 0.3);
transition: all 0.3s;
z-index: 50;
pointer-events: auto;
`;
openButton.onmouseover = () => {
openButton.style.background = 'linear-gradient(135deg, rgba(0, 255, 255, 0.4), rgba(255, 0, 255, 0.4))';
openButton.style.transform = 'translateY(-50%) scale(1.05)';
};
openButton.onmouseout = () => {
openButton.style.background = 'linear-gradient(135deg, rgba(0, 255, 255, 0.2), rgba(255, 0, 255, 0.2))';
openButton.style.transform = 'translateY(-50%) scale(1)';
};
openButton.onclick = () => {
css3dObject.visible = !css3dObject.visible;
};
document.body.appendChild(openButton);
return {
css3dObject,
openButton,
dispose: () => {
scene.remove(css3dObject);
openButton.remove();
}
};
};
// ============ 控制模块初始化 ============
const controlModule = createControlModule({
scene,
camera,
renderer
});
// 创建信息面板(传入 camera 和 controls
const iframePanel3D = createIframePanel3D(camera, controlModule.controls);
// ============ 灯光系统 ============
const ambientLight = new THREE.AmbientLight(0x222244, 0.5);
scene.add(ambientLight);
// 主光源
const mainLight = new THREE.PointLight(0x00ffff, 2, 50);
mainLight.position.set(5, 10, 5);
mainLight.castShadow = true;
scene.add(mainLight);
// 辅助光源
const auxLight = new THREE.PointLight(0xff00ff, 1.5, 50);
auxLight.position.set(-5, 8, -5);
scene.add(auxLight);
// 聚光灯照亮广告牌
const spotLight = new THREE.SpotLight(0xffffff, 3);
spotLight.position.set(0, 15, 0);
spotLight.angle = Math.PI / 6;
spotLight.penumbra = 0.5;
spotLight.castShadow = true;
scene.add(spotLight);
// ============ 粒子系统 ============
const particleCount = 500;
const particleGeometry = new THREE.BufferGeometry();
const positions = new Float32Array(particleCount * 3);
const colors = new Float32Array(particleCount * 3);
for (let i = 0; i < particleCount; i++) {
positions[i * 3] = (Math.random() - 0.5) * 50;
positions[i * 3 + 1] = Math.random() * 10;
positions[i * 3 + 2] = (Math.random() - 0.5) * 50;
const color = new THREE.Color();
color.setHSL(Math.random() * 0.2 + 0.5, 1, 0.5);
colors[i * 3] = color.r;
colors[i * 3 + 1] = color.g;
colors[i * 3 + 2] = color.b;
}
particleGeometry.setAttribute('position', new THREE.BufferAttribute(positions, 3));
particleGeometry.setAttribute('color', new THREE.BufferAttribute(colors, 3));
const particleMaterial = new THREE.PointsMaterial({
size: 0.1,
vertexColors: true,
transparent: true,
opacity: 0.8,
blending: THREE.AdditiveBlending
});
const particles = new THREE.Points(particleGeometry, particleMaterial);
scene.add(particles);
// ============ 动画循环 ============
let time = 0;
const animate = () => {
requestAnimationFrame(animate);
time += 0.016;
// 更新地面着色器时间
groundMaterial.uniforms.time.value = time;
// 粒子动画
const particlePositions = particleGeometry.attributes.position.array as Float32Array;
for (let i = 0; i < particleCount; i++) {
particlePositions[i * 3 + 1] += 0.02;
if (particlePositions[i * 3 + 1] > 10) {
particlePositions[i * 3 + 1] = 0;
}
}
particleGeometry.attributes.position.needsUpdate = true;
// 灯光旋转
mainLight.position.x = Math.sin(time * 0.5) * 8;
mainLight.position.z = Math.cos(time * 0.5) * 8;
auxLight.position.x = Math.sin(time * 0.3 + Math.PI) * 8;
auxLight.position.z = Math.cos(time * 0.3 + Math.PI) * 8;
// 更新控制模块
controlModule.update();
// 渲染 WebGL 和 CSS3D
renderer.render(scene, camera);
cssRenderer.render(scene, camera);
};
animate();
// ============ 窗口自适应 ============
const handleResize = () => {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize(window.innerWidth, window.innerHeight);
cssRenderer.setSize(window.innerWidth, window.innerHeight);
};
window.addEventListener('resize', handleResize);
// 返回清理函数
return () => {
window.removeEventListener('resize', handleResize);
controlModule.dispose();
iframePanel3D.dispose();
renderer.dispose();
groundMaterial.dispose();
particleMaterial.dispose();
};
};

View File

@@ -0,0 +1,37 @@
import { ToastContainer } from "react-toastify";
import { render } from "./app.ts";
import { useEffect } from "react";
export const AppProvider = () => {
return (
<>
<ToastContainer autoClose={2000}></ToastContainer>
<App />
</>
);
};
export const App = () => {
useEffect(() => {
const cleanup = render();
return () => {
cleanup?.();
};
}, []);
return (
<div
id='container'
style={{
width: '100vw',
height: '100vh',
overflow: 'hidden',
margin: 0,
padding: 0,
position: 'fixed',
top: 0,
left: 0
}}
/>
);
};

View File

@@ -1,6 +1,7 @@
--- ---
import '../apps/index/index.css'; import '../apps/index/index.css';
import { App } from '../apps/index/App.tsx'; // import { App as AppProvider } from '../apps/index/App.tsx';
import { AppProvider } from '../apps/skill-beautiful/index.tsx';
--- ---
<!doctype html> <!doctype html>
@@ -11,11 +12,11 @@ import { App } from '../apps/index/App.tsx';
<meta name='viewport' content='width=device-width, initial-scale=1.0' /> <meta name='viewport' content='width=device-width, initial-scale=1.0' />
<meta name='description' content='Kevisual 是一个专注于可视化设计, AI使用助手的工作室' /> <meta name='description' content='Kevisual 是一个专注于可视化设计, AI使用助手的工作室' />
<meta name='generator' content={Astro.generator} /> <meta name='generator' content={Astro.generator} />
<meta name="baidu-site-verification" content="codeva-qAEXUzv0tn" /> <meta name='baidu-site-verification' content='codeva-qAEXUzv0tn' />
<title>逸文设计工作室</title> <title>逸文设计工作室</title>
</head> </head>
<body> <body>
<App client:only /> <AppProvider client:only />
</body> </body>
</html> </html>

View File

@@ -0,0 +1,21 @@
---
import '../apps/index/index.css';
import { App as AppProvider } from '../apps/index/App.tsx';
---
<!doctype html>
<html lang='zh-CN'>
<head>
<meta charset='UTF-8' />
<link rel='icon' type='image/svg+xml' href='https://kevisual.cn/root/center/panda.jpg' />
<meta name='viewport' content='width=device-width, initial-scale=1.0' />
<meta name='description' content='Kevisual 是一个专注于可视化设计, AI使用助手的工作室' />
<meta name='generator' content={Astro.generator} />
<meta name='baidu-site-verification' content='codeva-qAEXUzv0tn' />
<title>逸文设计工作室</title>
</head>
<body>
<AppProvider client:only />
</body>
</html>

View File

@@ -16,24 +16,24 @@
"type": "module", "type": "module",
"dependencies": { "dependencies": {
"@ant-design/v5-patch-for-react-19": "^1.0.3", "@ant-design/v5-patch-for-react-19": "^1.0.3",
"@kevisual/query": "^0.0.29", "@kevisual/query": "^0.0.33",
"antd": "^5.26.2", "antd": "^6.1.2",
"clsx": "^2.1.1", "clsx": "^2.1.1",
"lucide-react": "^0.522.0", "lucide-react": "^0.562.0",
"react": "^19.1.0", "react": "^19.2.3",
"react-dom": "^19.1.0", "react-dom": "^19.2.3",
"rollup-plugin-visualizer": "^6.0.3" "rollup-plugin-visualizer": "^6.0.5"
}, },
"devDependencies": { "devDependencies": {
"@kevisual/cache": "^0.0.3", "@kevisual/cache": "^0.0.4",
"@kevisual/codemirror": "^0.0.12", "@kevisual/codemirror": "^0.0.12",
"@tailwindcss/vite": "^4.1.10", "@tailwindcss/vite": "^4.1.18",
"@types/react": "^19.1.8", "@types/react": "^19.2.7",
"@types/react-dom": "^19.1.6", "@types/react-dom": "^19.2.3",
"@vitejs/plugin-react": "^4.6.0", "@vitejs/plugin-react": "^5.1.2",
"react-feather": "^2.0.10", "react-feather": "^2.0.10",
"react-toastify": "^11.0.5", "react-toastify": "^11.0.5",
"tailwindcss": "^4.1.10", "tailwindcss": "^4.1.18",
"vite": "^7.0.0" "vite": "^7.3.0"
} }
} }

5844
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff