返回列表
编辑
数学

等腰直角三角形旋转

上传时间:2026-04-08 08:59

页面预览 请仅渲染可信 HTML
几何旋转全向探究

等腰直角三角形旋转:双向探究

调节滑块,观察三角形在 ±90° 范围内的全等变换

* 细灰线 = 三角形底边
* 粗色线 = 证明用的直角边
* 虚橙线 = C到BD的垂线(H)
顺时针 -90° 图① (0°) 逆时针 90°

结论:$\sqrt{2} CF = AF + BF$

AF + BF
0.00
=
√2 × CF
0.00
提示: 当你把角度调回 时,你会发现这就是题目里的图①;调到 45° 左右,就是第一问的平行状态。
查看源码
<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>几何旋转全向探究</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <style>
        body { background-color: #f1f5f9; }
        canvas { background: white; border-radius: 12px; box-shadow: 0 10px 15px -3px rgba(0,0,0,0.1); touch-action: none; }
    </style>
</head>
<body class="p-4 flex flex-col items-center">
    <div class="max-w-5xl w-full space-y-4">
        <div class="text-center space-y-1">
            <h1 class="text-2xl font-bold text-slate-800">等腰直角三角形旋转:双向探究</h1>
            <p class="text-slate-500 text-sm italic">调节滑块,观察三角形在 ±90° 范围内的全等变换</p>
        </div>
        
        <div class="bg-white p-6 rounded-2xl shadow-sm flex flex-col lg:flex-row gap-8">
            <div class="relative flex-shrink-0">
                <canvas id="geometryCanvas" width="500" height="500" class="w-full max-w-[500px]"></canvas>
                <div class="absolute bottom-4 left-4 bg-white/80 p-2 rounded text-[10px] text-slate-400 border shadow-sm">
                    * 细灰线 = 三角形底边<br>
                    * 粗色线 = 证明用的直角边<br>
                    * 虚橙线 = C到BD的垂线(H)
                </div>
            </div>
            
            <div class="flex-1 space-y-6">
                <div class="bg-slate-50 p-5 rounded-xl border border-slate-200 space-y-5">
                    <div>
                        <div class="flex justify-between mb-2">
                            <label class="font-bold text-slate-700">旋转角 &alpha; (当前: <span id="alphaValue" class="text-blue-600 font-mono">0</span>°)</label>
                        </div>
                        <input type="range" id="alphaSlider" min="-90" max="90" step="1" value="30" class="w-full h-3 bg-slate-200 rounded-lg appearance-none cursor-pointer accent-blue-600">
                        <div class="flex justify-between text-[10px] text-slate-400 mt-1">
                            <span>顺时针 -90°</span>
                            <span>图① (0°)</span>
                            <span>逆时针 90°</span>
                        </div>
                    </div>

                    <div class="grid grid-cols-1 gap-2">
                        <label class="flex items-center gap-3 p-2 hover:bg-white rounded cursor-pointer transition">
                            <input type="checkbox" id="showGhost" class="w-4 h-4" checked>
                            <span class="text-sm">保留图①虚线 (初始状态)</span>
                        </label>
                        <label class="flex items-center gap-3 p-2 hover:bg-white rounded cursor-pointer transition">
                            <input type="checkbox" id="showCongruent" class="w-4 h-4" checked>
                            <span class="text-sm font-bold text-blue-600">高亮显示全等翅膀 (ACE & BCD)</span>
                        </label>
                        <label class="flex items-center gap-3 p-2 hover:bg-white rounded cursor-pointer transition border border-orange-200 bg-orange-50/50">
                            <input type="checkbox" id="showPerpC" class="w-4 h-4 text-orange-500 accent-orange-500">
                            <span class="text-sm font-bold text-orange-600">显示过 C 作 BD 的垂线 (辅助线)</span>
                        </label>
                    </div>
                </div>

                <div class="p-5 bg-blue-600 rounded-xl text-white shadow-lg">
                    <h3 class="text-xs font-bold opacity-80 mb-2 uppercase tracking-widest">结论:$\sqrt{2} CF = AF + BF$</h3>
                    <div class="flex justify-around items-end">
                        <div class="text-center">
                            <div class="text-[10px] opacity-70">AF + BF</div>
                            <div id="valSum" class="text-2xl font-mono font-bold">0.00</div>
                        </div>
                        <div class="text-xl pb-1 opacity-50">=</div>
                        <div class="text-center">
                            <div class="text-[10px] opacity-70">√2 × CF</div>
                            <div id="valTarget" class="text-2xl font-mono font-bold text-yellow-300">0.00</div>
                        </div>
                    </div>
                </div>

                <div class="text-xs text-slate-500 bg-slate-100 p-3 rounded">
                    <strong>提示:</strong> 当你把角度调回 <strong>0°</strong> 时,你会发现这就是题目里的<strong>图①</strong>;调到 <strong>45°</strong> 左右,就是第一问的平行状态。
                </div>
            </div>
        </div>
    </div>

    <script>
        const canvas = document.getElementById('geometryCanvas');
        const ctx = canvas.getContext('2d');
        const alphaSlider = document.getElementById('alphaSlider');
        const showCongruent = document.getElementById('showCongruent');
        const showGhost = document.getElementById('showGhost');
        const showPerpC = document.getElementById('showPerpC');

        const SIZE_ABC = 220;
        const SIZE_CDE = 130;
        const cx = 130; // 稍微右移一点
        const cy = 350;

        function draw() {
            const alphaDeg = parseFloat(alphaSlider.value);
            const alpha = alphaDeg * Math.PI / 180;
            document.getElementById('alphaValue').innerText = alphaDeg;

            ctx.clearRect(0, 0, canvas.width, canvas.height);

            const C = { x: cx, y: cy };
            const A = { x: cx, y: cy - SIZE_ABC };
            const B = { x: cx + SIZE_ABC, y: cy };
            
            // 初始位置参考
            const E0 = { x: cx, y: cy - SIZE_CDE };
            const D0 = { x: cx + SIZE_CDE, y: cy };

            // 旋转后位置
            const E = { 
                x: cx + SIZE_CDE * Math.sin(alpha), 
                y: cy - SIZE_CDE * Math.cos(alpha) 
            };
            const D = { 
                x: cx + SIZE_CDE * Math.cos(alpha), 
                y: cy + SIZE_CDE * Math.sin(alpha) 
            };

            // 交点 F (AE 和 BD 的交点)
            function getIntersection(p1, p2, p3, p4) {
                const x1 = p1.x, y1 = p1.y, x2 = p2.x, y2 = p2.y;
                const x3 = p3.x, y3 = p3.y, x4 = p4.x, y4 = p4.y;
                const denom = (y4 - y3) * (x2 - x1) - (x4 - x3) * (y2 - y1);
                if (Math.abs(denom) < 0.1) return null;
                const ua = ((x4 - x3) * (y1 - y3) - (y4 - y3) * (x1 - x3)) / denom;
                return { x: x1 + ua * (x2 - x1), y: y1 + ua * (y2 - y1) };
            }
            const F = getIntersection(A, E, B, D) || {x:0, y:0};

            // 1. 绘制图①虚线背景
            if (showGhost.checked) {
                ctx.beginPath();
                ctx.setLineDash([5, 5]);
                ctx.moveTo(E0.x, E0.y); ctx.lineTo(C.x, C.y); ctx.lineTo(D0.x, D0.y); ctx.closePath();
                ctx.strokeStyle = '#cbd5e1'; ctx.stroke();
                ctx.setLineDash([]);
            }

            // 2. 绘制全等色块 (ACE & BCD)
            if (showCongruent.checked) {
                ctx.fillStyle = 'rgba(59, 130, 246, 0.1)';
                ctx.beginPath(); ctx.moveTo(A.x, A.y); ctx.lineTo(C.x, C.y); ctx.lineTo(E.x, E.y); ctx.closePath(); ctx.fill();
                
                ctx.fillStyle = 'rgba(239, 68, 68, 0.08)';
                ctx.beginPath(); ctx.moveTo(B.x, B.y); ctx.lineTo(C.x, C.y); ctx.lineTo(D.x, D.y); ctx.closePath(); ctx.fill();
            }

            // 3. 绘制完整的三角形实体 (细灰线边框)
            // ABC
            ctx.beginPath(); ctx.moveTo(A.x, A.y); ctx.lineTo(C.x, C.y); ctx.lineTo(B.x, B.y); ctx.closePath();
            ctx.strokeStyle = '#e2e8f0'; ctx.lineWidth = 1; ctx.stroke();
            // CDE
            ctx.beginPath(); ctx.moveTo(E.x, E.y); ctx.lineTo(C.x, C.y); ctx.lineTo(D.x, D.y); ctx.closePath();
            ctx.strokeStyle = '#e2e8f0'; ctx.lineWidth = 1; ctx.stroke();

            // 4. 突出证明的关键边
            ctx.lineWidth = 3;
            ctx.lineCap = 'round';
            // AC, BC
            ctx.strokeStyle = '#3b82f6'; ctx.beginPath(); ctx.moveTo(A.x, A.y); ctx.lineTo(C.x, C.y); ctx.stroke();
            ctx.beginPath(); ctx.moveTo(B.x, B.y); ctx.lineTo(C.x, C.y); ctx.stroke();
            // CE, CD
            ctx.strokeStyle = '#ef4444'; ctx.beginPath(); ctx.moveTo(E.x, E.y); ctx.lineTo(C.x, C.y); ctx.stroke();
            ctx.beginPath(); ctx.moveTo(D.x, D.y); ctx.lineTo(C.x, C.y); ctx.stroke();

            // AE, BD (AE是紫色, BD是深紫)
            ctx.lineWidth = 1.5;
            ctx.strokeStyle = '#8b5cf6';
            ctx.beginPath(); ctx.moveTo(A.x, A.y); ctx.lineTo(F.x, F.y); ctx.stroke();
            ctx.beginPath(); ctx.moveTo(B.x, B.y); ctx.lineTo(F.x, F.y); ctx.stroke();
            
            // CF (结论的核心)
            ctx.strokeStyle = '#10b981'; ctx.lineWidth = 3;
            ctx.beginPath(); ctx.moveTo(C.x, C.y); ctx.lineTo(F.x, F.y); ctx.stroke();

            // 5. 绘制 C 到 BD 的垂线 (新增辅助线)
            if (showPerpC.checked) {
                // 计算点 C 在线段/直线 BD 上的投影 H
                const vecBD = { x: D.x - B.x, y: D.y - B.y };
                const vecBC = { x: C.x - B.x, y: C.y - B.y };
                const dotProduct = vecBC.x * vecBD.x + vecBC.y * vecBD.y;
                const lenBDSq = vecBD.x * vecBD.x + vecBD.y * vecBD.y;

                if (lenBDSq > 0.001) {
                    const t = dotProduct / lenBDSq;
                    const H = {
                        x: B.x + t * vecBD.x,
                        y: B.y + t * vecBD.y
                    };

                    // 画虚线 CH
                    ctx.beginPath();
                    ctx.setLineDash([5, 5]);
                    ctx.moveTo(C.x, C.y);
                    ctx.lineTo(H.x, H.y);
                    ctx.strokeStyle = '#f97316'; // orange-500
                    ctx.lineWidth = 2;
                    ctx.stroke();
                    ctx.setLineDash([]);

                    // 绘制直角标记
                    const lenHC = Math.sqrt((C.x - H.x)**2 + (C.y - H.y)**2);
                    if (lenHC > 0.1) {
                        const lenBD = Math.sqrt(lenBDSq);
                        const uBD = { x: vecBD.x / lenBD, y: vecBD.y / lenBD };
                        const uHC = { x: (C.x - H.x) / lenHC, y: (C.y - H.y) / lenHC };
                        const s = 12; // 直角边长
                        ctx.beginPath();
                        ctx.moveTo(H.x + uBD.x * s, H.y + uBD.y * s);
                        ctx.lineTo(H.x + uBD.x * s + uHC.x * s, H.y + uBD.y * s + uHC.y * s);
                        ctx.lineTo(H.x + uHC.x * s, H.y + uHC.y * s);
                        ctx.strokeStyle = '#f97316';
                        ctx.lineWidth = 1;
                        ctx.stroke();
                    }

                    // 标注 H 点
                    ctx.beginPath();
                    ctx.arc(H.x, H.y, 4, 0, Math.PI * 2);
                    ctx.fillStyle = '#f97316';
                    ctx.fill();
                    ctx.font = 'bold 15px sans-serif';
                    ctx.fillStyle = '#c2410c'; // darker orange
                    // 稍微偏移动避开线条
                    ctx.fillText('H', H.x + 8, H.y + 16);
                }
            }

            // 6. 标注基本点
            const pts = [{p:A, n:'A'}, {p:B, n:'B'}, {p:C, n:'C'}, {p:D, n:'D'}, {p:E, n:'E'}, {p:F, n:'F'}];
            pts.forEach(pt => {
                ctx.beginPath(); ctx.arc(pt.p.x, pt.p.y, 4, 0, Math.PI*2);
                ctx.fillStyle = pt.n === 'F' ? '#8b5cf6' : '#1e293b';
                ctx.fill();
                ctx.font = 'bold 15px sans-serif';
                ctx.fillStyle = pt.n === 'F' ? '#8b5cf6' : '#1e293b';
                ctx.fillText(pt.n, pt.p.x + 8, pt.p.y - 8);
            });

            // 7. 数值
            const dist = (p1, p2) => Math.sqrt((p1.x - p2.x)**2 + (p1.y - p2.y)**2);
            const dAF = dist(A, F) / 10;
            const dBF = dist(B, F) / 10;
            const dCF = dist(C, F) / 10;
            document.getElementById('valSum').innerText = (dAF + dBF).toFixed(2);
            document.getElementById('valTarget').innerText = (Math.sqrt(2) * dCF).toFixed(2);
        }

        alphaSlider.addEventListener('input', draw);
        [showCongruent, showGhost, showPerpC].forEach(el => el.addEventListener('change', draw));
        window.onload = draw;
    </script>
</body>
</html>