×

用户登录

忘记密码?

用户注册

空气中氧气含量测定实验误差分析

点击网址即可观看并下载

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>
    <style>
        * {
            box-sizing: border-box;
            margin: 0;
            padding: 0;
        }
        
        body {
            font-family: 'Microsoft YaHei', sans-serif;
            background: linear-gradient(135deg, #1a2980, #26d0ce);
            color: #333;
            min-height: 100vh;
            padding: 20px;
            line-height: 1.6;
        }
        
        .container {
            max-width: 1400px;
            margin: 0 auto;
            display: grid;
            grid-template-columns: 300px 1fr;
            grid-template-rows: auto 1fr auto;
            grid-gap: 20px;
            grid-template-areas:
                "header header"
                "controls particles"
                "knowledge knowledge";
            min-height: 95vh;
        }
        
        .header {
            grid-area: header;
            text-align: center;
            background-color: rgba(255, 255, 255, 0.95);
            padding: 20px;
            border-radius: 15px;
            box-shadow: 0 8px 20px rgba(0, 0, 0, 0.25);
        }
        
        h1 {
            color: #2c3e50;
            margin-bottom: 10px;
            font-size: 2.5rem;
            background: linear-gradient(to right, #3498db, #e74c3c);
            -webkit-background-clip: text;
            background-clip: text;
            color: transparent;
        }
        
        .controls {
            grid-area: controls;
            background-color: rgba(255, 255, 255, 0.95);
            padding: 25px;
            border-radius: 15px;
            box-shadow: 0 8px 20px rgba(0, 0, 0, 0.2);
            display: flex;
            flex-direction: column;
        }
        
        .particles {
            grid-area: particles;
            background-color: rgba(255, 255, 255, 0.95);
            padding: 20px;
            border-radius: 15px;
            box-shadow: 0 8px 20px rgba(0, 0, 0, 0.2);
            display: flex;
            flex-direction: column;
        }
        
        .knowledge {
            grid-area: knowledge;
            background-color: rgba(255, 255, 255, 0.95);
            padding: 25px;
            border-radius: 15px;
            box-shadow: 0 8px 20px rgba(0, 0, 0, 0.2);
            margin-top: 20px;
        }
        
        h2 {
            font-size: 1.6rem;
            color: #2c3e50;
            margin-bottom: 20px;
            border-bottom: 3px solid #3498db;
            padding-bottom: 8px;
        }
        
        .btn-group {
            display: flex;
            flex-direction: column;
            gap: 15px;
        }
        
        .btn {
            background: linear-gradient(145deg, #3498db, #2980b9);
            color: white;
            border: none;
            padding: 14px 18px;
            border-radius: 10px;
            font-size: 1.1rem;
            cursor: pointer;
            transition: all 0.3s ease;
            position: relative;
            overflow: hidden;
            text-align: center;
            font-weight: bold;
            letter-spacing: 0.5px;
            box-shadow: 0 4px 10px rgba(52, 152, 219, 0.3);
        }
        
        .btn:hover {
            background: linear-gradient(145deg, #2980b9, #3498db);
            transform: translateY(-3px);
            box-shadow: 0 6px 15px rgba(0, 0, 0, 0.25);
        }
        
        .btn:active {
            transform: translateY(1px);
        }
        
        .btn.active {
            background: linear-gradient(145deg, #e74c3c, #c0392b);
            box-shadow: 0 4px 10px rgba(231, 76, 60, 0.3);
        }
        
        .normal-btn {
            background: linear-gradient(145deg, #27ae60, #219653);
        }
        
        .normal-btn:hover {
            background: linear-gradient(145deg, #219653, #27ae60);
        }
        
        .particles-container {
            width: 100%;
            height: 550px;
            position: relative;
            border: 2px solid #3498db;
            border-radius: 10px;
            overflow: hidden;
            background: linear-gradient(to bottom, #f1f2f6, #dfe4ea);
            box-shadow: inset 0 0 15px rgba(0, 0, 0, 0.1);
        }
        
        .particle {
            position: absolute;
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            font-size: 16px;
            font-weight: bold;
            color: white;
            transition: all 10s ease;
            animation: float 10s infinite ease-in-out;
            box-shadow: 0 6px 12px rgba(0, 0, 0, 0.2);
            text-shadow: 0 1px 3px rgba(0, 0, 0, 0.5);
            z-index: 5;
        }
        
        .O2 {
            background: radial-gradient(circle at 30% 30%, #ffff00, #ff9900);
            width: 42px;
            height: 42px;
        }
        
        .N2 {
            background: linear-gradient(145deg, #3498db, #2980b9);
            width: 38px;
            height: 38px;
        }
        
        .P {
            background: radial-gradient(circle at 30% 30%, #ff0000, #b30000);
            width: 36px;
            height: 36px;
        }
        
        .P2O5 {
            background: linear-gradient(145deg, #ffffff, #e0e0e0);
            color: #333;
            border: 2px solid #ccc;
            animation: none !important;
            z-index: 2;
            width: 42px;
            height: 42px;
            box-shadow: 0 4px 10px rgba(0, 0, 0, 0.15);
        }
        
        .heating .particle {
            animation: float 5s infinite ease-in-out, heat 10s infinite;
            transform: scale(1.3);
        }
        
        @keyframes float {
            0%, 100% { transform: translate(0, 0); }
            25% { transform: translate(8px, 10px); }
            50% { transform: translate(-8px, 6px); }
            75% { transform: translate(9px, -6px); }
        }
        
        @keyframes heat {
            0%, 100% { box-shadow: 0 0 15px rgba(255, 100, 0, 0.9); }
            50% { box-shadow: 0 0 25px rgba(255, 50, 0, 1); }
        }
        
        .knowledge-content {
            line-height: 1.8;
            font-size: 1.15rem;
        }
        
        .knowledge-point {
            margin: 12px 0;
            padding: 15px;
            background-color: rgba(236, 240, 241, 0.7);
            border-radius: 8px;
            transition: all 0.4s ease;
            border-left: 3px solid transparent;
        }
        
        .knowledge-point.active {
            background-color: rgba(52, 152, 219, 0.2);
            border-left: 4px solid #2980b9;
            padding-left: 18px;
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.05);
        }
        
        .value-display {
            position: absolute;
            top: 25px;
            right: 25px;
            background: rgba(255, 255, 255, 0.95);
            padding: 15px;
            border-radius: 10px;
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.15);
            font-weight: bold;
            text-align: center;
            z-index: 10;
            opacity: 0;
            transition: opacity 1s ease;
            border: 2px solid #3498db;
            width: 160px;
        }
        
        .value-display.visible {
            opacity: 1;
        }
        
        .value-display h3 {
            color: #2c3e50;
            font-size: 1.1rem;
            margin-bottom: 8px;
        }
        
        .oxygen-value {
            font-size: 1.8rem;
            color: #e74c3c;
            font-weight: bold;
        }
        
        .explanation-panel {
            position: absolute;
            top: 60px;
            left: 50%;
            transform: translateX(-50%);
            background: rgba(255, 255, 255, 0.95);
            padding: 12px 20px;
            border-radius: 8px;
            box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
            z-index: 10;
            max-height: 80px;
            font-size: 1.1rem;
            line-height: 1.5;
            max-width: 80%;
            text-align: center;
            opacity: 0;
            transition: opacity 0.5s ease;
            border: 2px solid #e74c3c;
        }
        
        .explanation-panel.visible {
            opacity: 1;
        }
        
        .status-bar {
            position: absolute;
            top: 0;
            left: 0;
            width: 100%;
            background: linear-gradient(to right, #3498db, #2980b9);
            padding: 12px;
            text-align: center;
            font-weight: bold;
            color: white;
            font-size: 1.2rem;
            z-index: 15;
            box-shadow: 0 3px 8px rgba(0, 0, 0, 0.2);
            letter-spacing: 0.5px;
            display: flex;
            justify-content: space-between;
            align-items: center;
        }
        
        .water-level {
            position: absolute;
            bottom: 0;
            left: 0;
            width: 100%;
            height: 0;
            background: linear-gradient(to top, rgba(52, 152, 219, 0.8), rgba(41, 128, 185, 0.9));
            transition: height 10s ease;
            z-index: 1;
        }
        
        .division-line {
            position: absolute;
            left: 0;
            width: 100%;
            height: 2px;
            background-color: rgba(0, 0, 0, 0.4);
            z-index: 2;
        }
        
        .division-label {
            position: absolute;
            right: 10px;
            font-size: 14px;
            color: rgba(0, 0, 0, 0.7);
            z-index: 3;
            font-weight: bold;
            background: rgba(255, 255, 255, 0.7);
            padding: 2px 5px;
            border-radius: 3px;
        }
        
        .error-indicator {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
            background: rgba(231, 76, 60, 0.9);
            color: white;
            padding: 12px 25px;
            border-radius: 8px;
            font-weight: bold;
            font-size: 1.4rem;
            z-index: 20;
            opacity: 0;
            transition: opacity 0.5s ease;
        }
        
        .error-indicator.visible {
            opacity: 1;
        }
        
        .pause-btn {
            cursor: pointer;
            padding: 5px 10px;
            background: rgba(255, 255, 255, 0.3);
            border-radius: 5px;
            font-size: 1.3rem;
        }
        
        .experiment-tips {
            margin-top: 20px;
            padding: 15px;
            background-color: rgba(255, 255, 240, 0.9);
            border-radius: 8px;
            border-left: 4px solid #f1c40f;
            font-size: 1.05rem;
            line-height: 1.7;
        }
        
        @media (max-width: 900px) {
            .container {
                grid-template-columns: 1fr;
                grid-template-areas:
                    "header"
                    "controls"
                    "particles"
                    "knowledge";
            }
            
            .particles-container {
                height: 450px;
            }
            
            h1 {
                font-size: 1.8rem;
            }
            
            .value-display {
                top: 15px;
                right: 15px;
                padding: 10px;
                width: 140px;
            }
            
            .explanation-panel {
                top: 50px;
                font-size: 1rem;
                padding: 10px 15px;
                max-height: 70px;
            }
        }
    </style>
</head>
<body>
    <div class="container">
        <div class="header">
            <h1>空气中氧气含量测定实验误差分析</h1>
            <p>气体粒子微观模型与误差原理可视化</p>
        </div>
        
        <div class="controls">
            <h2>误差模拟选择</h2>
            <div class="btn-group">
                <button class="btn normal-btn" id="normalBtn">正常操作实验</button>
                <button class="btn" id="error1Btn">止水夹未夹紧</button>
                <button class="btn" id="error2Btn">未及时塞紧瓶塞</button>
                <button class="btn" id="error3Btn">红磷量不足</button>
                <button class="btn" id="error4Btn">未冷却至室温</button>
                <button class="btn" id="error5Btn">装置漏气</button>
            </div>
            
            <div style="margin-top: 35px;">
                <h2>实验原理</h2>
                <p style="margin-top: 15px; line-height: 1.8; font-size: 1.15rem;">
                    <strong>红磷燃烧反应:</strong> 4P + 5O₂ → 2P₂O₅₅<br>
                    氧气消耗后,气体体积减少,冷却后水进入集气瓶占据被消耗氧气的空间。<br>
                    <strong>理想结果:</strong> 水位上升至空气总体积的1/5处(约21%)。
                </p>
            </div>
            
            <div style="margin-top: 25px;">
                <h2>实验说明</h2>
                <p style="margin-top: 15px; line-height: 1.7;">
                    1. 点击上方按钮选择实验类型<br>
                    2. 观察气体粒子微观行为变化<br>
                    3. 查看水位变化和氧气含量结果<br>
                    4. 阅读下方误差分析原理<br>
                    5. 使用暂停按钮控制动画
                </p>
            </div>
            
            <div class="experiment-tips">
                <strong>教学提示:</strong><br>
                • 红磷燃烧后生成白色固体五氧化二磷(P₂O₅)<br>
                • 粒子颜色:黄色-氧气(O₂), 蓝色-氮气(N₂), 红色-红磷(P)<br>
                • 固体五氧化二磷会沉到容器底部<br>
                • 不同误差类型导致水位变化不同
            </div>
        </div>
        
        <div class="particles">
            <h2>气体粒子微观模型</h2>
            <div class="particles-container" id="particlesContainer">
                <div class="status-bar" id="statusBar">
                    <span>准备开始实验</span>
                    <div class="pause-btn" id="pauseBtn">⏸⏸⏸</div>
                </div>
                
                <div class="error-indicator" id="errorIndicator">出现误差!</div>
                
                <div class="value-display" id="valueDisplay">
                    <h3>氧气含量测定结果</h3>
                    <div class="oxygen-value" id="oxygenValue">21.0%</div>
                </div>
                
                <div class="water-level" id="waterLevel"></div>
                
                <div class="explanation-panel" id="explanationPanel">
                    正常操作:红磷燃烧消耗氧气,冷却后水进入集气瓶约占空气体积的1/5(约21%)。
                </div>
            </div>
        </div>
        
        <div class="knowledge">
            <h2>误差原理详细分析</h2>
            <div class="knowledge-content">
                <div class="knowledge-point active" id="knowledgePoint1">
                    <strong>正常操作:</strong> 红磷燃烧消耗氧气,反应式为:4P + 5O₂ → 2P₂O₅₅。待冷却至室温后,水进入集气瓶占据被消耗氧气的空间。理想情况下水位上升至空气总体积的1/5处(约21%),证明氧气约占空气体积的21%。
                </div>
                <div class="knowledge-point" id="knowledgePoint2">
                    <strong>止水夹未夹紧:</strong> 实验过程中气体从导管逸出,使瓶内气体总量减少。冷却后打开止水夹,水进入量大于正常值(结果偏大)。<strong>微观解释:</strong> 气体粒子(主要是N₂)从导管逸出,导致瓶内粒子总数减少,水进入比例增加。
                </div>
                <div class="knowledge-point" id="knowledgePoint3">
                    <strong>未及时塞紧瓶塞:</strong> 红磷放入时瓶塞未及时塞紧,部分气体(含氧气和氮气)从瓶口逸出。冷却后瓶内气体总量少于正常情况,水进入量大于正常值(结果偏大)。<strong>微观解释:</strong> 粒子从瓶口逸出,包括O₂和N₂,导致粒子总数减少。
                </div>
                <div class="knowledge-point" id="knowledgePoint4">
                    <strong>红磷量不足:</strong> 红磷量不足无法完全消耗氧气,瓶中仍有部分氧气残留。冷却后打开止水夹,水进入量小于正常值(结果偏小)。<strong>微观解释:</strong> 部分O₂粒子未被消耗,瓶内气体总量减少程度不足,水上升高度不足。
                </div>
                <div class="knowledge-point" id="knowledgePoint5">
                    <strong>未冷却至室温:</strong> 未冷却即打开止水夹,瓶内气体温度高,体积膨胀(压强较大),进入的水量小于冷却后应有的量(结果偏小)。<strong>微观解释:</strong> 高温使粒子运动加剧,占据更大空间,水无法充分进入。
                </div>
                <div class="knowledge-point" id="knowledgePoint6">
                    <strong>装置漏气:</strong> 装置存在漏气点,冷却过程中外界空气进入瓶内补充氧气。打开止水夹后,水进入量少于正常值(结果偏小)。<strong>微观解释:</strong> 外界空气(含O₂和N₂)进入瓶内,补充了被消耗的O₂,导致气体总量减少不足。
                </div>
            </div>
        </div>
    </div>

    <script>
        // 粒子定义
        const particles = {
            O2: { count: 21, label: "O₂", className: "O2" },
            N2: { count: 79, label: "N₂", className: "N2" },
            P: { count: 20, label: "P", className: "P" }
        };
        
        // 误差解释文本
        const explanations = {
            normal: "正常操作:红磷燃烧消耗氧气,冷却后水进入集气瓶约占空气体积的1/5(约21%)。",
            error1: "止水夹未夹紧:气体从导管逸出(主要是N₂),瓶内粒子总数减少,水进入比例增加(结果偏大)。",
            error2: "未及时塞紧瓶塞:气体从瓶口逸出(含O₂和N₂),瓶内粒子总数减少,水进入比例增加(结果偏大)。",
            error3: "红磷量不足:部分O₂粒子未被消耗,瓶内气体总量减少程度不足,水上升高度不足(结果偏小)。",
            error4: "未冷却至室温:高温使粒子运动加剧,占据更大空间,水无法充分进入(结果偏小)。",
            error5: "装置漏气:外界空气(含O₂和N₂)进入瓶内,补充了被消耗的O₂,导致气体总量减少不足(结果偏小)。"
        };
        
        // 实验状态
        let currentError = null;
        let isHeating = false;
        let animationTimer = null;
        let isPaused = false;
        let activeTimers = [];
        
        // 初始化粒子系统
        function initParticles() {
            const container = document.getElementById('particlesContainer');
            container.innerHTML = '';
            
            // 添加状态条
            const statusBar = document.createElement('div');
            statusBar.id = 'statusBar';
            statusBar.className = 'status-bar';
            statusBar.innerHTML = '<span>准备开始实验</span><div class="pause-btn" id="pauseBtn">⏸⏸⏸</div>';
            container.appendChild(statusBar);
            
            // 添加暂停按钮事件
            document.getElementById('pauseBtn').addEventListener('click', togglePause);
            
            // 添加五等分标记线
            for (let i = 1; i <= 4; i++) {
                const line = document.createElement('div');
                line.className = 'division-line';
                line.style.top = `${i * 20}%`;
                container.appendChild(line);
                
                const label = document.createElement('div');
                label.className = 'division-label';
                label.style.top = `${i * 20}%`;
                label.textContent = `${i * 20}%`;
                container.appendChild(label);
            }
            
            // 添加水位
            const waterLevel = document.createElement('div');
            waterLevel.id = 'waterLevel';
            waterLevel.className = 'water-level';
            container.appendChild(waterLevel);
            
            // 添加粒子
            Object.entries(particles).forEach(([key, config]) => {
                for (let i = 0; i < config.count; i++) {
                    createParticle(container, config.className, config.label);
                }
            });
            
            // 添加误差指示器
            const errorIndicator = document.createElement('div');
            errorIndicator.id = 'errorIndicator';
            errorIndicator.className = 'error-indicator';
            errorIndicator.textContent = '出现误差!';
            container.appendChild(errorIndicator);
            
            // 添加解释面板
            const explanationPanel = document.createElement('div');
            explanationPanel.id = 'explanationPanel';
            explanationPanel.className = 'explanation-panel';
            explanationPanel.textContent = explanations.normal;
            container.appendChild(explanationPanel);
            
            // 添加值显示
            const valueDisplay = document.createElement('div');
            valueDisplay.id = 'valueDisplay';
            valueDisplay.className = 'value-display';
            valueDisplay.innerHTML = `
                <h3>氧气含量测定结果</h3>
                <div class="oxygen-value" id="oxygenValue">21.0%</div>
            `;
            container.appendChild(valueDisplay);
        }
        
        // 暂停/继续动画
        function togglePause() {
            isPaused = !isPaused;
            const pauseBtn = document.getElementById('pauseBtn');
            
            if (isPaused) {
                pauseBtn.textContent = '▶';
                pauseAllAnimations();
                updateStatus('实验暂停');
            } else {
                pauseBtn.textContent = '⏸⏸⏸';
                resumeAllAnimations();
                updateStatus('实验继续');
            }
        }
        
        // 暂停所有动画
        function pauseAllAnimations() {
            // 暂停粒子动画
            const allParticles = document.querySelectorAll('.particle');
            allParticles.forEach(p => {
                p.style.animationPlayState = 'paused';
            });
            
            // 暂停水位上升
            const waterLevel = document.getElementById('waterLevel');
            if (waterLevel) {
                const computedStyle = getComputedStyle(waterLevel);
                const currentHeight = parseFloat(computedStyle.height);
                waterLevel.style.height = `${currentHeight}%`;
                waterLevel.style.transition = 'none';
            }
            
            // 清除所有计时器
            activeTimers.forEach(timer => clearTimeout(timer));
            activeTimers = [];
        }
        
        // 继续所有动画
        function resumeAllAnimations() {
            // 继续粒子动画
            const allParticles = document.querySelectorAll('.particle');
            allParticles.forEach(p => {
                p.style.animationPlayState = 'running';
            });
            
            // 恢复水位过渡效果
            const waterLevel = document.getElementById('waterLevel');
            if (waterLevel) {
                waterLevel.style.transition = 'height 10s ease';
            }
        }
        
        // 创建定时器(可被暂停)
        function createTimer(func, delay) {
            if (isPaused) return;
            
            const timer = setTimeout(() => {
                if (!isPaused) {
                    func();
                }
            }, delay);
            
            activeTimers.push(timer);
            return timer;
        }
        
        // 更新状态条
        function updateStatus(text) {
            const statusBar = document.getElementById('statusBar');
            if (statusBar) {
                statusBar.querySelector('span').textContent = text;
            }
        }
        
        // 显示/隐藏误差指示器
        function toggleErrorIndicator(show) {
            const indicator = document.getElementById('errorIndicator');
            if (indicator) {
                indicator.className = show ? 'error-indicator visible' : 'error-indicator';
            }
        }
        
        // 显示误差解释面板
        function showErrorExplanation(text) {
            const explanationPanel = document.getElementById('explanationPanel');
            if (explanationPanel) {
                explanationPanel.textContent = text;
                explanationPanel.classList.add('visible');
                
                // 5秒后自动隐藏
                setTimeout(() => {
                    explanationPanel.classList.remove('visible');
                }, 5000);
            }
        }
        
        // 创建单个粒子
        function createParticle(container, className, label) {
            const particle = document.createElement('div');
            particle.className = `particle ${className}`;
            particle.innerHTML = label;
            
            // 随机位置(上半部分)
            const posX = Math.random() * (container.offsetWidth - 50);
            const posY = Math.random() * (container.offsetHeight * 0.8);
            
            particle.style.left = `${posX}px`;
            particle.style.top = `${posY}px`;
            
            // 随机动画延迟
            particle.style.animationDelay = `${Math.random() * 10}s`;
            
            container.appendChild(particle);
        }
        
        // 移除部分氧气
        function removeOxygen(ratio) {
            const O2Particles = document.querySelectorAll('.particle.O2');
            const toRemove = Math.floor(O2Particles.length * ratio);
            
            // 移除部分O2粒子
            for (let i = 0; i < toRemove; i++) {
                if (i < O2Particles.length) {
                    O2Particles[i].style.opacity = '0';
                    O2Particles[i].style.transform = 'scale(0)';
                }
            }
        }
        
        // 模拟气体逸出
        function simulateLeak(count, direction) {
            const allParticles = document.querySelectorAll('.particle:not(.P)');
            const toRemove = Math.min(count, allParticles.length);
            
            // 移除部分粒子
            for (let i = 0; i < toRemove; i++) {
                const particle = allParticles[Math.floor(Math.random() * allParticles.length)];
                
                // 创建逸出动画
                particle.style.transition = 'all 10s ease';
                if (direction === 'right') {
                    particle.style.transform = `translateX(${Math.random() * 100 + 100}px)`;
                } else {
                    particle.style.transform = `translateY(${-Math.random() * 100 - 100}px)`;
                }
                particle.style.opacity = '0';
            }
        }
        
        // 转换红磷为五氧化二磷
        function convertPhosphorus(convertCount) {
            const PParticles = document.querySelectorAll('.particle.P');
            const count = Math.min(convertCount, PParticles.length);
            
            // 随机选择部分红磷进行转换
            const indices = new Set();
            while (indices.size < count) {
                indices.add(Math.floor(Math.random() * PParticles.length));
            }
            
            Array.from(indices).forEach(index => {
                const particle = PParticles[index];
                // 转换红磷为五氧化二磷
                particle.className = 'particle P2O5';
                particle.innerHTML = 'P₂O₅';
                
                // 添加转换动画
                particle.style.transition = 'all 10s ease';
                particle.style.transform = 'scale(1.5)';
                
                // 移动到容器底部(固体沉底)
                const container = document.getElementById('particlesContainer');
                const containerHeight = container.offsetHeight;
                const particleHeight = particle.offsetHeight;
                
                createTimer(() => {
                    particle.style.top = `${containerHeight - particleHeight - 20}px`;
                    particle.style.transform = 'scale(1)';
                    particle.style.zIndex = '2';
                }, 500);
            });
        }
        
        // 温度升高(粒子运动加剧)
        function heatParticles() {
            if (isHeating) return;
            isHeating = true;
            
            const allParticles = document.querySelectorAll('.particle');
            allParticles.forEach(p => p.classList.add('heating'));
        }
        
        // 冷却粒子
        function coolParticles() {
            isHeating = false;
            const allParticles = document.querySelectorAll('.particle');
            allParticles.forEach(p => p.classList.remove('heating'));
        }
        
        // 添加外界空气(装置漏气)
        function addExternalAir() {
            const container = document.getElementById('particlesContainer');
            
            // 添加4个氮气粒子
            for (let i = 0; i < 4; i++) {
                createTimer(() => {
                    const particle = document.createElement('div');
                    particle.className = 'particle N2';
                    particle.innerHTML = 'N₂';
                    
                    // 从顶部进入
                    particle.style.left = `${Math.random() * (container.offsetWidth - 50)}px`;
                    particle.style.top = '-50px';
                    particle.style.opacity = '0';
                    
                    container.appendChild(particle);
                    
                    // 动画:向下移动并显示
                    createTimer(() => {
                        particle.style.transition = 'all 10s ease';
                        particle.style.top = `${Math.random() * (container.offsetHeight * 0.8)}px`;
                        particle.style.opacity = '1';
                    }, 100);
                }, i * 1000);
            }
            
            // 添加1个氧气粒子
            createTimer(() => {
                const particle = document.createElement('div');
                particle.className = 'particle O2';
                particle.innerHTML = 'O₂';
                
                // 从顶部进入
                particle.style.left = `${Math.random() * (container.offsetWidth - 50)}px`;
                particle.style.top = '-50px';
                particle.style.opacity = '0';
                
                container.appendChild(particle);
                
                // 动画:向下移动并显示
                createTimer(() => {
                    particle.style.transition = 'all 10s ease';
                    particle.style.top = `${Math.random() * (container.offsetHeight * 0.8)}px`;
                    particle.style.opacity = '1';
                }, 100);
            }, 4000);
        }
        
        // 更新氧气含量显示
        function updateOxygenValue(percent) {
            const oxygenValue = document.getElementById('oxygenValue');
            oxygenValue.textContent = `${percent.toFixed(1)}%`;
            
            // 根据值调整颜色
            if (percent > 21.1) {
                oxygenValue.style.color = '#27ae60';
            } else if (percent < 20.9) {
                oxygenValue.style.color = '#e74c3c';
            } else {
                oxygenValue.style.color = '#3498db';
            }
        }
        
        // 更新水位高度
        function updateWaterLevel(percent) {
            const waterLevel = document.getElementById('waterLevel');
            waterLevel.style.height = `${percent}%`;
        }
        
        // 更新解释面板
        function updateExplanation(type) {
            const explanationPanel = document.getElementById('explanationPanel');
            explanationPanel.textContent = explanations[type];
        }
        
        // 显示面板(值显示和解释面板)
        function showPanels() {
            const valueDisplay = document.getElementById('valueDisplay');
            const explanationPanel = document.getElementById('explanationPanel');
            
            if (valueDisplay) valueDisplay.classList.add('visible');
            if (explanationPanel) explanationPanel.classList.add('visible');
        }
        
        // 隐藏面板
        function hidePanels() {
            const valueDisplay = document.getElementById('valueDisplay');
            const explanationPanel = document.getElementById('explanationPanel');
            
            if (valueDisplay) valueDisplay.classList.remove('visible');
            if (explanationPanel) explanationPanel.classList.remove('visible');
        }
        
        // 重置实验
        function resetExperiment() {
            // 清除之前的定时器
            activeTimers.forEach(timer => clearTimeout(timer));
            activeTimers = [];
            
            // 重置粒子
            initParticles();
            
            // 重置水位
            updateWaterLevel(0);
            
            // 重置加热状态
            coolParticles();
            
            // 隐藏面板
            hidePanels();
            toggleErrorIndicator(false);
            
            // 重置知识点高亮
            document.querySelectorAll('.knowledge-point').forEach(point => {
                point.classList.remove('active');
            });
            document.getElementById('knowledgePoint1').classList.add('active');
            
            // 更新解释
            updateExplanation('normal');
            
            // 更新状态
            updateStatus('准备开始实验');
            
            // 重置按钮状态
            document.querySelectorAll('.btn').forEach(btn => {
                btn.classList.remove('active');
            });
            document.getElementById('normalBtn').classList.add('active');
            
            // 重置暂停状态
            isPaused = false;
            document.getElementById('pauseBtn').textContent = '⏸⏸⏸';
        }
        
        // 执行实验
        function runExperiment(errorType = null) {
            resetExperiment();
            currentError = errorType;
            
            // 高亮当前按钮
            document.querySelectorAll('.btn').forEach(btn => btn.classList.remove('active'));
            if (errorType === null) {
                document.getElementById('normalBtn').classList.add('active');
            } else {
                document.getElementById(`error${errorType}Btn`).classList.add('active');
            }
            
            // 开始燃烧
            updateStatus('点燃红磷...');
            heatParticles(); // 产生热量
            
            createTimer(() => {
                // 显示误差提示(如果有)
                if (errorType) {
                    toggleErrorIndicator(true);
                    
                    // 根据错误类型显示不同的解释
                    switch (errorType) {
                        case 1:
                            showErrorExplanation("止水夹未夹紧:气体从导管逸出");
                            break;
                        case 2:
                            showErrorExplanation("未及时塞紧瓶塞:气体从瓶口逸出");
                            break;
                        case 3:
                            showErrorExplanation("红磷量不足:氧气未被完全消耗");
                            break;
                        case 4:
                            showErrorExplanation("未冷却至室温:气体体积膨胀");
                            break;
                        case 5:
                            showErrorExplanation("装置漏气:外界空气进入瓶内");
                            break;
                    }
                }
                
                // 消耗氧气并转换红磷
                switch (errorType) {
                    case 1: // 止水夹未夹紧
                        simulateLeak(15, 'right'); // 逸出约15个粒子
                        removeAllOxygen(); // 移除所有氧气(完全消耗)
                        convertPhosphorus(16); // 转换16个红磷(保留4个)
                        updateExplanation('error1');
                        break;
                        
                    case 2: // 未及时塞紧瓶塞
                        simulateLeak(12, 'top'); // 逸出12个粒子
                        removeAllOxygen(); // 移除所有氧气(完全消耗)
                        convertPhosphorus(16); // 转换16个红磷(保留4个)
                        updateExplanation('error2');
                        break;
                        
                    case 3: // 红磷量不足
                        // 先转换8个红磷(因为量不足)
                        convertPhosphorus(8); 
                        // 移除所有未被转换的红磷(因为量不足)
                        createTimer(() => {
                            removeAllPhosphorus(); 
                        }, 1000);
                        removeOxygen(0.5); // 只消耗50%氧气
                        updateExplanation('error3');
                        break;
                        
                    case 4: // 未冷却至室温
                        // 正常消耗氧气,但不冷却
                        removeAllOxygen();
                        convertPhosphorus(16); // 转换16个红磷(保留4个)
                        updateExplanation('error4');
                        break;
                        
                    case 5: // 装置漏气
                        removeAllOxygen();
                        convertPhosphorus(16); // 转换16个红磷(保留4个)
                        updateExplanation('error5');
                        break;
                        
                    default: // 正常操作
                        removeAllOxygen(); // 消耗全部氧气
                        convertPhosphorus(16); // 转换16个红磷(保留4个)
                        updateExplanation('normal');
                        break;
                }
                
                // 等待五氧化二磷沉降
                createTimer(() => {
                    updateStatus('燃烧结束');
                    
                    // 冷却阶段(如果适用)
                    createTimer(() => {
                        if (errorType !== 4) {
                            updateStatus('冷却至室温...');
                            coolParticles();
                        }
                        
                        // 装置漏气:添加外界空气
                        if (errorType === 5) {
                            updateStatus('装置漏气,外界空气进入...');
                            addExternalAir();
                        }
                        
                        // 打开止水夹
                        createTimer(() => {
                            updateStatus('打开止水夹,水进入集气瓶...');
                            
                            // 计算最终氧气含量和水位高度
                            let oxygenPercentage, waterLevel;
                            switch (errorType) {
                                case 1: // 止水夹未夹紧(结果偏大)
                                    oxygenPercentage = 21 + Math.random() * 5 + 3;
                                    waterLevel = oxygenPercentage;
                                    break;
                                    
                                case 2: // 未及时塞紧瓶塞(结果偏大)
                                    oxygenPercentage = 21 + Math.random() * 4 + 2.5;
                                    waterLevel = oxygenPercentage;
                                    break;
                                    
                                case 3: // 红磷量不足(结果偏小)
                                    oxygenPercentage = 21 - Math.random() * 5 - 5;
                                    waterLevel = oxygenPercentage;
                                    break;
                                    
                                case 4: // 未冷却至室温(结果偏小)
                                    oxygenPercentage = 21 - Math.random() * 4 - 4;
                                    waterLevel = oxygenPercentage;
                                    break;
                                    
                                case 5: // 装置漏气(结果偏小)
                                    oxygenPercentage = 21 - Math.random() * 6 - 4;
                                    waterLevel = oxygenPercentage;
                                    break;
                                    
                                default: // 正常情况
                                    oxygenPercentage = 21;
                                    waterLevel = 21;
                                    break;
                            }
                            
                            // 更新水位(装置漏气需要等待外界空气进入后再更新)
                            if (errorType === 5) {
                                createTimer(() => {
                                    updateOxygenValue(oxygenPercentage);
                                    updateWaterLevel(waterLevel);
                                    
                                    // 水位动画结束后10秒显示面板
                                    createTimer(() => {
                                        updateStatus('实验完成');
                                        showPanels();
                                        toggleErrorIndicator(false);
                                    }, 10000); // 10秒后显示面板
                                }, 5000);
                            } else {
                                updateOxygenValue(oxygenPercentage);
                                updateWaterLevel(waterLevel);
                                
                                // 水位动画结束后10秒显示面板
                                createTimer(() => {
                                    updateStatus('实验完成');
                                    showPanels();
                                    toggleErrorIndicator(false);
                                }, 10000); // 10秒后显示面板
                            }
                            
                            // 高亮对应知识点
                            document.querySelectorAll('.knowledge-point').forEach(point => {
                                point.classList.remove('active');
                            });
                            const pointId = errorType === null ? 1 : errorType + 1;
                            document.getElementById(`knowledgePoint${pointId}`).classList.add('active');
                        }, 5000); // 等待5秒后打开止水夹
                    }, 5000); // 冷却阶段延长至5秒
                }, 10000); // 等待10秒让五氧化二磷沉降
            }, 5000); // 燃烧阶段延长至5秒
        }
        
        // 移除所有氧气
        function removeAllOxygen() {
            const O2Particles = document.querySelectorAll('.particle.O2');
            O2Particles.forEach(p => {
                p.style.opacity = '0';
                p.style.transform = 'scale(0)';
            });
        }
        
        // 移除所有红磷
        function removeAllPhosphorus() {
            const PParticles = document.querySelectorAll('.particle.P');
            PParticles.forEach(p => p.remove());
        }
        
        // 绑定按钮事件
        document.getElementById('normalBtn').addEventListener('click', () => runExperiment(null));
        document.getElementById('error1Btn').addEventListener('click', () => runExperiment(1));
        document.getElementById('error2Btn').addEventListener('click', () => runExperiment(2));
        document.getElementById('error3Btn').addEventListener('click', () => runExperiment(3));
        document.getElementById('error4Btn').addEventListener('click', () => runExperiment(4));
        document.getElementById('error5Btn').addEventListener('click', () => runExperiment(5));
        
        // 初始化
        window.addEventListener('load', () => {
            initParticles();
            resetExperiment();
        });
    </script>
</body>
</html>

https://yuanbao.tencent.com/sc/pbd1QItvZ5CcJZg

发表评论