Files
dsProject/dsLightRag/static/deepseek_html_20250807_90b14b.html
2025-08-14 15:45:08 +08:00

672 lines
23 KiB
HTML

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>氢气制取互动实验</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r128/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.min.js"></script>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background: linear-gradient(135deg, #1a2a6c, #2c3e50);
color: #fff;
overflow: hidden;
height: 100vh;
}
.container {
display: flex;
height: 100vh;
padding: 20px;
}
#experiment-container {
flex: 1;
position: relative;
border-radius: 15px;
overflow: hidden;
box-shadow: 0 15px 35px rgba(0, 0, 0, 0.5);
background: rgba(10, 15, 30, 0.7);
}
#canvas-container {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
}
.info-panel {
width: 350px;
padding: 25px;
background: rgba(0, 15, 30, 0.85);
border-left: 2px solid #00b4d8;
overflow-y: auto;
backdrop-filter: blur(10px);
}
.info-panel h1 {
color: #00b4d8;
margin-bottom: 20px;
font-size: 28px;
text-align: center;
text-shadow: 0 0 10px rgba(0, 180, 216, 0.5);
}
.experiment-steps {
background: rgba(0, 30, 60, 0.6);
border-radius: 10px;
padding: 20px;
margin-bottom: 25px;
border: 1px solid #0077b6;
}
.experiment-steps h2 {
color: #90e0ef;
margin-bottom: 15px;
font-size: 20px;
}
.steps {
list-style-type: none;
counter-reset: step-counter;
}
.steps li {
position: relative;
padding-left: 35px;
margin-bottom: 15px;
line-height: 1.5;
font-size: 16px;
color: #caf0f8;
}
.steps li:before {
content: counter(step-counter);
counter-increment: step-counter;
position: absolute;
left: 0;
top: 0;
width: 24px;
height: 24px;
background: #0077b6;
border-radius: 50%;
text-align: center;
line-height: 24px;
font-weight: bold;
color: #fff;
}
.chemical-equation {
text-align: center;
font-size: 22px;
margin: 20px 0;
padding: 15px;
background: rgba(0, 60, 120, 0.3);
border-radius: 8px;
border: 1px solid #48cae4;
font-family: 'Cambria', serif;
}
.controls {
display: flex;
flex-direction: column;
gap: 15px;
margin-top: 20px;
}
.control-btn {
padding: 12px;
background: linear-gradient(45deg, #0077b6, #00b4d8);
border: none;
border-radius: 6px;
color: white;
font-size: 16px;
font-weight: 600;
cursor: pointer;
transition: all 0.3s ease;
display: flex;
align-items: center;
justify-content: center;
gap: 10px;
}
.control-btn:hover {
background: linear-gradient(45deg, #00b4d8, #0077b6);
transform: translateY(-2px);
box-shadow: 0 5px 15px rgba(0, 180, 216, 0.4);
}
.control-btn:active {
transform: translateY(1px);
}
.control-btn i {
font-size: 20px;
}
.safety-note {
background: rgba(150, 0, 0, 0.2);
border-left: 4px solid #ff0000;
padding: 15px;
border-radius: 0 8px 8px 0;
margin-top: 25px;
font-size: 14px;
}
.safety-note h3 {
color: #ff9e00;
margin-bottom: 8px;
}
.status-bar {
position: absolute;
bottom: 20px;
left: 20px;
background: rgba(0, 30, 60, 0.8);
padding: 12px 20px;
border-radius: 30px;
display: flex;
align-items: center;
gap: 15px;
border: 1px solid #00b4d8;
z-index: 10;
}
.status-indicator {
width: 15px;
height: 15px;
border-radius: 50%;
background: #4CAF50;
box-shadow: 0 0 10px #4CAF50;
}
.status-text {
font-size: 16px;
}
.bubble {
position: absolute;
background: rgba(200, 230, 255, 0.7);
border-radius: 50%;
animation: float 4s infinite ease-in-out;
box-shadow: 0 0 10px rgba(173, 216, 230, 0.8);
}
@keyframes float {
0%, 100% { transform: translateY(0) translateX(0); }
25% { transform: translateY(-20px) translateX(5px); }
50% { transform: translateY(-40px) translateX(-5px); }
75% { transform: translateY(-20px) translateX(5px); }
}
.progress-container {
width: 100%;
background: rgba(255, 255, 255, 0.1);
border-radius: 5px;
margin: 20px 0;
overflow: hidden;
}
.progress-bar {
height: 8px;
background: linear-gradient(90deg, #0077b6, #00b4d8);
width: 0%;
transition: width 1s ease;
}
.logo {
text-align: center;
margin-top: 20px;
font-size: 18px;
color: #90e0ef;
opacity: 0.8;
}
@media (max-width: 768px) {
.container {
flex-direction: column;
}
.info-panel {
width: 100%;
height: 40%;
}
#experiment-container {
height: 60%;
}
}
</style>
</head>
<body>
<div class="container">
<div id="experiment-container">
<div id="canvas-container"></div>
<div class="status-bar">
<div class="status-indicator"></div>
<div class="status-text">实验准备就绪</div>
</div>
</div>
<div class="info-panel">
<h1>氢气制取互动实验</h1>
<div class="experiment-steps">
<h2>实验步骤</h2>
<ol class="steps">
<li>将锌粒放入锥形瓶中</li>
<li>向锥形瓶中加入稀硫酸</li>
<li>连接气体发生装置</li>
<li>使用排水法收集氢气</li>
<li>检验氢气纯度</li>
</ol>
</div>
<div class="chemical-equation">
Zn + H<sub>2</sub>SO<sub>4</sub> → ZnSO<sub>4</sub> + H<sub>2</sub>
</div>
<div class="progress-container">
<div class="progress-bar" id="reaction-progress"></div>
</div>
<div class="controls">
<button class="control-btn" id="add-zinc">
<i></i> 添加锌粒
</button>
<button class="control-btn" id="add-acid">
<i>🧪</i> 加入稀硫酸
</button>
<button class="control-btn" id="start-reaction">
<i>🔥</i> 开始反应
</button>
<button class="control-btn" id="collect-gas">
<i>💧</i> 收集氢气
</button>
<button class="control-btn" id="reset-experiment">
<i>🔄</i> 重置实验
</button>
</div>
<div class="safety-note">
<h3>安全注意事项</h3>
<p>1. 氢气易燃易爆,实验需远离明火</p>
<p>2. 使用稀硫酸时需佩戴防护手套</p>
<p>3. 收集前必须检验氢气纯度</p>
<p>4. 实验应在通风环境下进行</p>
</div>
<div class="logo">
3D化学实验室 | 互动教学系统
</div>
</div>
</div>
<script>
// 初始化Three.js场景
let scene, camera, renderer, controls;
let experimentObjects = {};
let bubbles = [];
let reactionStarted = false;
init();
animate();
function init() {
// 创建场景
scene = new THREE.Scene();
scene.background = new THREE.Color(0x0a1a2a);
scene.fog = new THREE.Fog(0x0a1a2a, 10, 25);
// 创建相机
camera = new THREE.PerspectiveCamera(60, window.innerWidth / window.innerHeight, 0.1, 1000);
camera.position.set(0, 2, 5);
// 创建渲染器
const container = document.getElementById('canvas-container');
renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setSize(container.clientWidth, container.clientHeight);
renderer.shadowMap.enabled = true;
container.appendChild(renderer.domElement);
// 添加轨道控制
controls = new THREE.OrbitControls(camera, renderer.domElement);
controls.enableDamping = true;
controls.dampingFactor = 0.05;
// 添加光源
const ambientLight = new THREE.AmbientLight(0x404040, 2);
scene.add(ambientLight);
const directionalLight = new THREE.DirectionalLight(0xffffff, 1);
directionalLight.position.set(5, 10, 7);
directionalLight.castShadow = true;
scene.add(directionalLight);
const pointLight = new THREE.PointLight(0x00aaff, 1, 20);
pointLight.position.set(0, 3, 0);
pointLight.castShadow = true;
scene.add(pointLight);
// 创建实验室环境
createLabEnvironment();
// 创建实验器材
createExperimentEquipment();
// 响应窗口大小变化
window.addEventListener('resize', onWindowResize);
// 添加事件监听器
setupEventListeners();
}
function createLabEnvironment() {
// 实验室地板
const floorGeometry = new THREE.PlaneGeometry(20, 20);
const floorMaterial = new THREE.MeshStandardMaterial({
color: 0x2c3e50,
roughness: 0.8,
metalness: 0.2
});
const floor = new THREE.Mesh(floorGeometry, floorMaterial);
floor.rotation.x = -Math.PI / 2;
floor.receiveShadow = true;
scene.add(floor);
// 实验室墙壁
const wallGeometry = new THREE.BoxGeometry(20, 8, 0.2);
const wallMaterial = new THREE.MeshStandardMaterial({ color: 0x34495e });
const backWall = new THREE.Mesh(wallGeometry, wallMaterial);
backWall.position.z = -10;
backWall.position.y = 4;
scene.add(backWall);
// 实验台
const tableGeometry = new THREE.BoxGeometry(6, 0.5, 3);
const tableMaterial = new THREE.MeshStandardMaterial({
color: 0x7d5e3c,
roughness: 0.7
});
const table = new THREE.Mesh(tableGeometry, tableMaterial);
table.position.y = 0.25;
table.position.z = -1;
table.castShadow = true;
table.receiveShadow = true;
scene.add(table);
// 添加台面细节
const tableTopGeometry = new THREE.BoxGeometry(6.2, 0.1, 3.2);
const tableTopMaterial = new THREE.MeshStandardMaterial({
color: 0x8d6e4b,
roughness: 0.5,
metalness: 0.3
});
const tableTop = new THREE.Mesh(tableTopGeometry, tableTopMaterial);
tableTop.position.y = 0.55;
tableTop.position.z = -1;
tableTop.receiveShadow = true;
scene.add(tableTop);
}
function createExperimentEquipment() {
// 锥形瓶
const flaskGeometry = new THREE.CylinderGeometry(0.7, 0.5, 1.5, 32);
const flaskMaterial = new THREE.MeshPhysicalMaterial({
color: 0xd0e0f0,
transparent: true,
opacity: 0.7,
roughness: 0.1,
clearcoat: 1,
transmission: 0.9
});
const flask = new THREE.Mesh(flaskGeometry, flaskMaterial);
flask.position.set(0, 1.2, -1);
flask.castShadow = true;
flask.receiveShadow = true;
scene.add(flask);
experimentObjects.flask = flask;
// 锌粒
const zincGeometry = new THREE.SphereGeometry(0.1, 16, 16);
const zincMaterial = new THREE.MeshStandardMaterial({
color: 0x808080,
metalness: 0.8,
roughness: 0.3
});
experimentObjects.zincPieces = [];
for (let i = 0; i < 5; i++) {
const zinc = new THREE.Mesh(zincGeometry, zincMaterial);
zinc.position.set(-1 + i * 0.1, 1, -2);
zinc.castShadow = true;
scene.add(zinc);
experimentObjects.zincPieces.push(zinc);
}
// 稀硫酸试剂瓶
const acidBottleGeometry = new THREE.CylinderGeometry(0.4, 0.4, 1.2, 32);
const acidBottleMaterial = new THREE.MeshPhysicalMaterial({
color: 0xa0f0a0,
transparent: true,
opacity: 0.8,
roughness: 0.1,
clearcoat: 1
});
const acidBottle = new THREE.Mesh(acidBottleGeometry, acidBottleMaterial);
acidBottle.position.set(-2, 1, -1);
acidBottle.castShadow = true;
scene.add(acidBottle);
experimentObjects.acidBottle = acidBottle;
// 导管
const tubeGeometry = new THREE.CylinderGeometry(0.05, 0.05, 1, 16);
const tubeMaterial = new THREE.MeshStandardMaterial({ color: 0x808080 });
const tube = new THREE.Mesh(tubeGeometry, tubeMaterial);
tube.position.set(0.5, 1.8, -1);
tube.rotation.z = Math.PI / 4;
scene.add(tube);
experimentObjects.tube = tube;
// 集气瓶
const gasJarGeometry = new THREE.CylinderGeometry(0.6, 0.6, 1.5, 32);
const gasJarMaterial = new THREE.MeshPhysicalMaterial({
color: 0xd0e0f0,
transparent: true,
opacity: 0.7,
roughness: 0.1,
clearcoat: 1,
transmission: 0.9
});
const gasJar = new THREE.Mesh(gasJarGeometry, gasJarMaterial);
gasJar.position.set(2, 1.5, -1);
scene.add(gasJar);
experimentObjects.gasJar = gasJar;
}
function startReaction() {
if (reactionStarted) return;
reactionStarted = true;
document.querySelector('.status-text').textContent = "反应进行中...";
// 移除锌粒
experimentObjects.zincPieces.forEach(zinc => {
scene.remove(zinc);
});
// 创建气泡
createBubbles();
// 更新进度条
const progressBar = document.getElementById('reaction-progress');
let progress = 0;
const interval = setInterval(() => {
progress += 1;
progressBar.style.width = progress + '%';
if (progress >= 100) {
clearInterval(interval);
document.querySelector('.status-text').textContent = "反应完成!";
document.getElementById('collect-gas').disabled = false;
}
}, 50);
}
function createBubbles() {
for (let i = 0; i < 50; i++) {
setTimeout(() => {
const bubble = {
mesh: null,
startTime: Date.now(),
position: new THREE.Vector3(
experimentObjects.flask.position.x + (Math.random() - 0.5) * 0.3,
experimentObjects.flask.position.y - 0.5,
experimentObjects.flask.position.z + (Math.random() - 0.5) * 0.3
),
velocity: new THREE.Vector3(
(Math.random() - 0.5) * 0.01,
Math.random() * 0.02 + 0.01,
(Math.random() - 0.5) * 0.01
)
};
const bubbleGeometry = new THREE.SphereGeometry(0.03 + Math.random() * 0.02, 8, 8);
const bubbleMaterial = new THREE.MeshBasicMaterial({
color: 0xffffff,
transparent: true,
opacity: 0.7
});
bubble.mesh = new THREE.Mesh(bubbleGeometry, bubbleMaterial);
bubble.mesh.position.copy(bubble.position);
scene.add(bubble.mesh);
bubbles.push(bubble);
}, i * 100);
}
}
function updateBubbles() {
const now = Date.now();
for (let i = bubbles.length - 1; i >= 0; i--) {
const bubble = bubbles[i];
const age = (now - bubble.startTime) / 1000;
if (age > 5) {
scene.remove(bubble.mesh);
bubbles.splice(i, 1);
continue;
}
bubble.position.add(bubble.velocity);
bubble.mesh.position.copy(bubble.position);
// 气泡上升时略微变大
bubble.mesh.scale.set(1 + age/10, 1 + age/10, 1 + age/10);
}
}
function animate() {
requestAnimationFrame(animate);
controls.update();
updateBubbles();
renderer.render(scene, camera);
}
function onWindowResize() {
const container = document.getElementById('canvas-container');
camera.aspect = container.clientWidth / container.clientHeight;
camera.updateProjectionMatrix();
renderer.setSize(container.clientWidth, container.clientHeight);
}
function setupEventListeners() {
document.getElementById('add-zinc').addEventListener('click', () => {
document.querySelector('.status-text').textContent = "已添加锌粒";
});
document.getElementById('add-acid').addEventListener('click', () => {
document.querySelector('.status-text').textContent = "已添加稀硫酸";
});
document.getElementById('start-reaction').addEventListener('click', startReaction);
document.getElementById('collect-gas').addEventListener('click', () => {
document.querySelector('.status-text').textContent = "正在收集氢气...";
// 模拟气体收集效果
setTimeout(() => {
document.querySelector('.status-text').textContent = "氢气收集完成!";
}, 2000);
});
document.getElementById('reset-experiment').addEventListener('click', () => {
// 移除所有气泡
bubbles.forEach(bubble => {
scene.remove(bubble.mesh);
});
bubbles = [];
// 重置进度条
document.getElementById('reaction-progress').style.width = '0%';
document.querySelector('.status-text').textContent = "实验已重置";
// 重新创建锌粒
experimentObjects.zincPieces.forEach(zinc => {
scene.add(zinc);
});
reactionStarted = false;
});
}
// 添加视觉气泡效果
function createVisualBubbles() {
const container = document.getElementById('experiment-container');
for (let i = 0; i < 20; i++) {
const bubble = document.createElement('div');
bubble.classList.add('bubble');
// 随机大小
const size = Math.random() * 30 + 10;
bubble.style.width = `${size}px`;
bubble.style.height = `${size}px`;
// 随机位置
bubble.style.left = `${Math.random() * 100}%`;
bubble.style.bottom = `-${size}px`;
// 随机动画延迟
bubble.style.animationDelay = `${Math.random() * 5}s`;
container.appendChild(bubble);
}
}
// 初始化视觉气泡
createVisualBubbles();
</script>
</body>
</html>