湿地公园问题演示动画
上传时间:2026-04-09 10:20 · 作者:ytry1983
最短距离:湿地公园观光路线 (第4题)
综合运用“轴对称”与“平移”解决复杂路径问题
📐 数学解析
1. 设坐标系:$A(0,0)$,$E(0,80)$,$D(50,80)$,$C(50,0)$,$B(20,80)$。湖边线 $CF$ 为 $x=50$。
2. **轴对称**:作 $A$ 关于 $x=50$ 的对称点 $A'(100, 0)$。
3. **平移**:将 $B$ 沿 $MN$ 方向(垂直向下)平移 $40$m 得到 $B'(20, 40)$。
4. **求解**:最短路径长为 $A'B' + MN = \sqrt{80^2 + 40^2} + 40 = 40\sqrt{5} + 40$。
💡 解题核心
本题难点在于 $A, B$ 在湖边线 $CF$ 的同侧。我们需要先通过**对称**让它们分布在直线两侧,再通过**平移**扣除固定长度的观景台 $MN$。这种“对称+平移”的组合拳是解决同侧定长路径问题的终极模板。
查看解析页源码
<!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://cdn.tailwindcss.com"></script>
<style>
.step-transition {
transition: all 0.5s ease-in-out;
}
@keyframes pulse-orange {
0%, 100% { opacity: 1; transform: scale(1); }
50% { opacity: 0.7; transform: scale(1.2); }
}
.animate-pulse-custom {
animation: pulse-orange 2s infinite;
}
.math-font {
font-family: 'Times New Roman', serif;
font-style: italic;
}
</style>
</head>
<body class="bg-slate-50 min-h-screen font-sans p-4 md:p-8 flex flex-col items-center">
<div class="max-w-4xl w-full bg-white rounded-2xl shadow-xl overflow-hidden">
<!-- Header -->
<div class="bg-green-600 p-6 text-white text-center">
<h1 class="text-2xl font-bold">最短距离:湿地公园观光路线 (第4题)</h1>
<p class="mt-2 opacity-90">综合运用“轴对称”与“平移”解决复杂路径问题</p>
</div>
<!-- Animation Area -->
<div class="relative h-[450px] md:h-[550px] w-full bg-slate-50 border-y border-slate-200 overflow-hidden">
<svg id="animation-svg" class="w-full h-full" viewBox="0 0 600 500" preserveAspectRatio="xMidYMid meet">
<!-- 场景背景:花海和人工湖 -->
<!-- 花海 AEDC -->
<rect x="100" y="100" width="150" height="240" fill="#fdf2f8" stroke="#f472b6" stroke-width="2" />
<text x="140" y="230" fill="#db2777" class="text-sm font-bold opacity-30">🌸 花海景区 (AEDC)</text>
<!-- 人工湖 CFGH -->
<rect x="250" y="100" width="180" height="240" fill="#f0f9ff" stroke="#38bdf8" stroke-width="2" />
<text x="300" y="230" fill="#0284c7" class="text-sm font-bold opacity-30">💧 人工湖 (CFGH)</text>
<!-- 边界线 CF -->
<line x1="250" y1="100" x2="250" y2="340" stroke="#0ea5e9" stroke-width="3" />
<text x="255" y="360" fill="#0ea5e9" class="text-xs font-bold">直线 CF (湖边)</text>
<!-- 关键顶点 A, E, D, C -->
<g class="math-font text-xs">
<text x="85" y="350">A</text>
<text x="85" y="100">E</text>
<text x="255" y="90">D</text>
<text x="255" y="355">C</text>
</g>
<!-- 点 A 和 B -->
<g id="points-static">
<!-- A 点 (0,0) -> SVG (100, 340) -->
<circle cx="100" cy="340" r="6" fill="#ef4444" />
<!-- B 点 在 ED 上, BD=30, DE=50 -> B 距离 E 为 20 -->
<!-- 比例尺: 1m = 3px. DE=50m -> 150px. BD=30m -> 90px. B(100+60, 100) -->
<circle cx="160" cy="100" r="6" fill="#ef4444" />
<text x="155" y="85" class="font-bold">终点 B</text>
<text x="200" y="95" class="text-[10px] fill-slate-400 italic">BD=30m</text>
</g>
<!-- 动态图层 -->
<g id="dynamic-layers"></g>
</svg>
<!-- Overlay Info -->
<div id="info-box" class="absolute bottom-6 left-6 right-6 bg-white/95 backdrop-blur-sm p-4 rounded-xl shadow-md border border-slate-100 transition-opacity duration-300">
<h3 id="step-title" class="text-lg font-bold text-slate-800"></h3>
<p id="step-desc" class="text-slate-600 text-sm mt-1"></p>
</div>
</div>
<!-- Controls -->
<div class="p-6 flex justify-between items-center bg-slate-50">
<button id="prev-btn" class="px-6 py-2 rounded-full border border-slate-300 text-slate-600 hover:bg-white transition-all active:scale-95 disabled:opacity-50">
上一步
</button>
<div id="dot-indicators" class="flex gap-2"></div>
<button id="next-btn" class="px-6 py-2 rounded-full bg-green-600 text-white hover:bg-green-700 transition-all shadow-lg shadow-green-200 active:scale-95">
下一步
</button>
</div>
</div>
<!-- Solution Grid -->
<div class="mt-8 grid grid-cols-1 md:grid-cols-2 gap-6 max-w-4xl w-full">
<div class="bg-white p-6 rounded-xl shadow-sm border border-slate-200">
<h4 class="font-bold text-green-800 flex items-center gap-2">
<span>📐</span> 数学解析
</h4>
<div class="text-sm text-slate-600 mt-2 space-y-2 leading-relaxed">
<p>1. 设坐标系:$A(0,0)$,$E(0,80)$,$D(50,80)$,$C(50,0)$,$B(20,80)$。湖边线 $CF$ 为 $x=50$。</p>
<p>2. **轴对称**:作 $A$ 关于 $x=50$ 的对称点 $A'(100, 0)$。</p>
<p>3. **平移**:将 $B$ 沿 $MN$ 方向(垂直向下)平移 $40$m 得到 $B'(20, 40)$。</p>
<p>4. **求解**:最短路径长为 $A'B' + MN = \sqrt{80^2 + 40^2} + 40 = 40\sqrt{5} + 40$。</p>
</div>
</div>
<div class="bg-white p-6 rounded-xl shadow-sm border border-slate-200">
<h4 class="font-bold text-green-800 flex items-center gap-2">
<span>💡</span> 解题核心
</h4>
<p class="text-sm text-slate-600 mt-2">
本题难点在于 $A, B$ 在湖边线 $CF$ 的同侧。我们需要先通过**对称**让它们分布在直线两侧,再通过**平移**扣除固定长度的观景台 $MN$。这种“对称+平移”的组合拳是解决同侧定长路径问题的终极模板。
</p>
</div>
</div>
<script>
const steps = [
{ title: "1. 理解题意", desc: "起点 A 在花海一角,终点 B 在 ED 上。观景台 MN 长度固定为 40m,且必须在直线 CF 上滑动。" },
{ title: "2. 作 A 的对称点 A'", desc: "由于 A, B 在直线 CF 同侧,先作 A 关于 CF 的对称点 A'。此时 AM = A'M。" },
{ title: "3. 平移终点 B 到 B'", desc: "将 B 沿垂直方向向下平移一个观景台的长度(40m),得到 B'。此时 BN 等于平移后的对应线段。" },
{ title: "4. 连接 A'B'", desc: "连接 A'B'。根据两点之间线段最短,A'B' 即为除去观景台后的最短路径。其与 CF 的交点确定了 M 的位置。" },
{ title: "5. 确定最终路径", desc: "向上平移 40m 找到 N。最终最短路径为 AM + MN + NB,总长度为 40√5 + 40 米。" }
];
let currentStep = 0;
const dynamicLayer = document.getElementById('dynamic-layers');
const stepTitle = document.getElementById('step-title');
const stepDesc = document.getElementById('step-desc');
const prevBtn = document.getElementById('prev-btn');
const nextBtn = document.getElementById('next-btn');
const dotIndicators = document.getElementById('dot-indicators');
steps.forEach((_, i) => {
const dot = document.createElement('div');
dot.className = `h-2 w-8 rounded-full transition-all ${i === 0 ? 'bg-green-600 w-12' : 'bg-slate-300'}`;
dotIndicators.appendChild(dot);
});
function updateUI() {
stepTitle.textContent = steps[currentStep].title;
stepDesc.textContent = steps[currentStep].desc;
prevBtn.disabled = currentStep === 0;
nextBtn.textContent = currentStep === steps.length - 1 ? "重新开始" : "下一步";
Array.from(dotIndicators.children).forEach((dot, i) => {
dot.className = `h-2 transition-all rounded-full ${i === currentStep ? 'bg-green-600 w-12' : 'bg-slate-300 w-8'}`;
});
dynamicLayer.innerHTML = '';
// 坐标映射 (1m = 3px)
const pA = { x: 100, y: 340 };
const pB = { x: 160, y: 100 };
const pAPrime = { x: 400, y: 340 }; // A 关于 x=250 对称
const pBPrime = { x: 160, y: 220 }; // B 向下平移 40m (120px)
// 计算 M 点 (直线 A'B' 与 x=250 的交点)
// A'(400, 340), B'(160, 220). 斜率 k = (340-220)/(400-160) = 120/240 = 0.5
// y - 340 = 0.5 * (x - 400) -> 当 x=250, y = 0.5*(-150) + 340 = -75 + 340 = 265
const pM = { x: 250, y: 265 };
const pN = { x: 250, y: 145 }; // M 向上平移 120px
if (currentStep >= 1) {
// 对称点 A'
createSVGElement('line', { x1: pA.x, y1: pA.y, x2: pAPrime.x, y2: pAPrime.y, stroke: '#94a3b8', 'stroke-width': 1, 'stroke-dasharray': '4' });
createSVGElement('circle', { cx: pAPrime.x, cy: pAPrime.y, r: 6, fill: '#f59e0b', class: 'animate-pulse-custom' });
createSVGElement('text', { x: pAPrime.x + 10, y: pAPrime.y + 10, class: 'font-bold fill-amber-600 text-xs', textContent: "A' (对称点)" });
}
if (currentStep >= 2) {
// 平移点 B'
createSVGElement('line', { x1: pB.x, y1: pB.y, x2: pBPrime.x, y2: pBPrime.y, stroke: '#10b981', 'stroke-width': 2, 'stroke-dasharray': '4' });
createSVGElement('circle', { cx: pBPrime.x, cy: pBPrime.y, r: 6, fill: '#10b981' });
createSVGElement('text', { x: pBPrime.x - 45, y: pBPrime.y + 5, class: 'font-bold fill-emerald-600 text-xs', textContent: "B' (平移点)" });
// 平移箭头
createSVGElement('path', { d: `M${pB.x-5} ${pBPrime.y-10} L${pB.x} ${pBPrime.y} L${pB.x+5} ${pBPrime.y-10}`, fill: 'none', stroke: '#10b981', 'stroke-width': 2 });
}
if (currentStep >= 3) {
// 辅助线 A'B'
createSVGElement('line', { x1: pAPrime.x, y1: pAPrime.y, x2: pBPrime.x, y2: pBPrime.y, stroke: '#6366f1', 'stroke-width': 2, 'stroke-dasharray': '8' });
}
if (currentStep >= 4) {
// 最终路径
// AM (等于 A'M)
createSVGElement('line', { x1: pA.x, y1: pA.y, x2: pM.x, y2: pM.y, stroke: '#ef4444', 'stroke-width': 3 });
// MN (观景台)
createSVGElement('line', { x1: pM.x, y1: pM.y, x2: pN.x, y2: pN.y, stroke: '#10b981', 'stroke-width': 5 });
// NB
createSVGElement('line', { x1: pN.x, y1: pN.y, x2: pB.x, y2: pB.y, stroke: '#ef4444', 'stroke-width': 3 });
// 标注 M, N
createSVGElement('circle', { cx: pM.x, cy: pM.y, r: 4, fill: '#10b981' });
createSVGElement('circle', { cx: pN.x, cy: pN.y, r: 4, fill: '#10b981' });
createSVGElement('text', { x: pM.x + 10, y: pM.y + 5, class: 'text-[10px] font-bold fill-slate-700', textContent: 'M' });
createSVGElement('text', { x: pN.x + 10, y: pN.y + 5, class: 'text-[10px] font-bold fill-slate-700', textContent: 'N' });
}
}
function createSVGElement(type, props) {
const el = document.createElementNS('http://www.w3.org/2000/svg', type);
for (let key in props) {
if (key === 'textContent') {
el.textContent = props[key];
} else {
el.setAttribute(key, props[key]);
}
}
dynamicLayer.appendChild(el);
return el;
}
nextBtn.addEventListener('click', () => {
currentStep = (currentStep + 1) % steps.length;
updateUI();
});
prevBtn.addEventListener('click', () => {
if (currentStep > 0) {
currentStep--;
updateUI();
}
});
updateUI();
</script>
</body>
</html>