<!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>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', 'Microsoft YaHei', sans-serif;
}
body {
background-color: #f0f2f5;
color: #333;
line-height: 1.6;
padding: 20px;
min-height: 100vh;
}
.container {
max-width: 1200px;
margin: 0 auto;
display: flex;
flex-direction: column;
gap: 25px;
}
header {
text-align: center;
padding: 20px;
background: linear-gradient(135deg, #6a11cb 0%, #2575fc 100%);
color: white;
border-radius: 12px;
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.1);
}
h1 {
font-size: 2.5rem;
margin-bottom: 10px;
}
.subtitle {
font-size: 1.2rem;
opacity: 0.9;
max-width: 800px;
margin: 0 auto;
}
.content {
display: flex;
flex-wrap: wrap;
gap: 25px;
}
.visualization-section {
flex: 2;
min-width: 300px;
background-color: white;
border-radius: 12px;
padding: 25px;
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.08);
}
.explanation-section {
flex: 1;
min-width: 300px;
background-color: white;
border-radius: 12px;
padding: 25px;
box-shadow: 0 6px 15px rgba(0, 0, 0, 0.08);
}
.section-title {
font-size: 1.8rem;
color: #2c3e50;
margin-bottom: 20px;
padding-bottom: 10px;
border-bottom: 2px solid #3498db;
}
.canvas-container {
position: relative;
width: 100%;
height: 400px;
background-color: #f8f9fa;
border-radius: 8px;
overflow: hidden;
border: 1px solid #ddd;
}
#ellipseCanvas {
width: 100%;
height: 100%;
cursor: pointer;
}
.controls {
display: flex;
flex-wrap: wrap;
gap: 15px;
margin-top: 20px;
padding: 20px;
background-color: #f8f9fa;
border-radius: 8px;
}
.control-group {
flex: 1;
min-width: 200px;
}
.control-label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: #2c3e50;
}
.slider-container {
display: flex;
align-items: center;
gap: 10px;
}
.slider-value {
min-width: 40px;
text-align: center;
font-weight: 600;
color: #3498db;
}
input[type="range"] {
flex: 1;
height: 8px;
border-radius: 4px;
background: #ddd;
outline: none;
-webkit-appearance: none;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: #3498db;
cursor: pointer;
transition: all 0.2s;
}
input[type="range"]::-webkit-slider-thumb:hover {
background: #2980b9;
transform: scale(1.1);
}
.definition-box {
background-color: #e8f4fc;
border-left: 4px solid #3498db;
padding: 20px;
margin: 20px 0;
border-radius: 0 8px 8px 0;
}
.math-expression {
font-family: 'Cambria', 'Times New Roman', serif;
font-size: 1.4rem;
text-align: center;
margin: 15px 0;
color: #2c3e50;
}
.variables {
display: flex;
justify-content: space-around;
margin-top: 15px;
flex-wrap: wrap;
}
.variable {
text-align: center;
padding: 10px;
background-color: #f1f8ff;
border-radius: 6px;
min-width: 100px;
margin: 5px;
}
.var-name {
font-weight: bold;
color: #3498db;
font-size: 1.2rem;
}
.var-desc {
font-size: 0.9rem;
color: #666;
margin-top: 5px;
}
.real-app {
background-color: #fff8e1;
border-left: 4px solid #f39c12;
padding: 20px;
margin: 25px 0;
border-radius: 0 8px 8px 0;
}
.app-title {
color: #e67e22;
margin-bottom: 10px;
display: flex;
align-items: center;
gap: 10px;
}
ul {
padding-left: 20px;
margin: 15px 0;
}
li {
margin-bottom: 10px;
}
.highlight {
color: #e74c3c;
font-weight: 600;
}
.note {
font-size: 0.9rem;
color: #7f8c8d;
font-style: italic;
margin-top: 20px;
padding: 10px;
background-color: #f9f9f9;
border-radius: 6px;
}
.instructions {
background-color: #e8f7ee;
border-left: 4px solid #27ae60;
padding: 15px;
margin: 20px 0;
border-radius: 0 8px 8px 0;
}
.footer {
text-align: center;
margin-top: 20px;
padding-top: 20px;
border-top: 1px solid #ddd;
color: #7f8c8d;
font-size: 0.9rem;
}
@media (max-width: 768px) {
.content {
flex-direction: column;
}
h1 {
font-size: 2rem;
}
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>椭圆第二定义可视化</h1>
<p class="subtitle">探索椭圆作为到定点(焦点)与定直线(准线)距离之比为常数的点的轨迹</p>
</header>
<div class="content">
<section class="visualization-section">
<h2 class="section-title">交互式可视化</h2>
<div class="instructions">
<p>💡 <strong>操作指南:</strong> 拖动椭圆上的红点可观察点P位置变化,拖动焦点(绿色)或准线(蓝色)可改变椭圆形状,调整离心率可控制椭圆扁平程度。</p>
</div>
<div class="canvas-container">
<canvas id="ellipseCanvas"></canvas>
</div>
<div class="controls">
<div class="control-group">
<label class="control-label">离心率 e</label>
<div class="slider-container">
<input type="range" id="eccentricitySlider" min="0.1" max="0.9" step="0.01" value="0.6">
<span id="eccentricityValue" class="slider-value">0.60</span>
</div>
</div>
<div class="control-group">
<label class="control-label">焦距 c</label>
<div class="slider-container">
<input type="range" id="focusSlider" min="50" max="200" step="1" value="120">
<span id="focusValue" class="slider-value">120</span>
</div>
</div>
<div class="control-group">
<label class="control-label">准线位置</label>
<div class="slider-container">
<input type="range" id="directrixSlider" min="300" max="500" step="1" value="400">
<span id="directrixValue" class="slider-value">400</span>
</div>
</div>
</div>
<div class="variables">
<div class="variable">
<div class="var-name" id="pfValue">PF = 200</div>
<div class="var-desc">点P到焦点F的距离</div>
</div>
<div class="variable">
<div class="var-name" id="pdValue">PD = 333</div>
<div class="var-desc">点P到准线D的距离</div>
</div>
<div class="variable">
<div class="var-name" id="ratioValue">PF/PD = 0.60</div>
<div class="var-desc">距离比 (离心率 e)</div>
</div>
</div>
</section>
<section class="explanation-section">
<h2 class="section-title">椭圆第二定义</h2>
<div class="definition-box">
<p>椭圆是平面内到定点(焦点)的距离与到定直线(准线)的距离之比为常数e (0 < e < 1) 的点的轨迹。</p>
<div class="math-expression">
\(\frac{|PF|}{|PD|} = e\)
</div>
<p>其中:
<ul>
<li>P为椭圆上的任意一点</li>
<li>F为椭圆的焦点</li>
<li>D为点P到准线的垂足</li>
<li>e为离心率,且0 < e < 1</li>
</ul>
</p>
</div>
<h3 class="section-title">数学推导</h3>
<p>从第二定义出发,设焦点坐标为F(c, 0),准线方程为x = d (d > c),离心率为e。</p>
<p>对任意点P(x, y),有:</p>
<div class="math-expression">
\(\frac{\sqrt{(x-c)^2 + y^2}}{|x-d|} = e\)
</div>
<p>两边平方并整理可得椭圆的标准方程:</p>
<div class="math-expression">
\(\frac{x^2}{a^2} + \frac{y^2}{b^2} = 1\)
</div>
<p>其中 \(a = \frac{ed}{\sqrt{1-e^2}}\),\(b = a\sqrt{1-e^2}\),且满足关系 \(c = ae\),\(d = \frac{a}{e}\)。</p>
<div class="real-app">
<h3 class="app-title">📡 实际应用案例</h3>
<ul>
<li><span class="highlight">光学性质:</span>从椭圆一个焦点发出的光线,经椭圆反射后必定通过另一个焦点。这一性质被应用于卫星天线、光学设备的设计中。</li>
<li><span class="highlight">天体力学:</span>行星轨道近似为椭圆,太阳位于一个焦点上。开普勒第一定律就是基于这一观察。</li>
<li><span class="highlight">建筑声学:</span>椭圆形的房间会产生"耳语廊"效应,在一个焦点处低声说话,在另一个焦点处可以清晰听到。</li>
<li><span class="highlight">医学成像:</span>某些超声波设备利用椭圆反射原理来聚焦能量。</li>
</ul>
</div>
<div class="note">
<p>注:椭圆的第一定义(到两焦点距离之和为常数)和第三定义(与两定点连线斜率积为常数)均可由第二定义推导出来,三个定义在数学上是等价的。</p>
</div>
</section>
</div>
<div class="footer">
<p>椭圆第二定义可视化 | 使用HTML5 Canvas实现 | 拖动元素以交互探索椭圆性质</p>
</div>
</div>
<!-- 引入MathJax以渲染数学公式 -->
<script src="https://polyfill.io/v3/polyfill.min.js?features=es6">\
<script id="MathJax-script" async src="https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js">\
<script>
// 获取Canvas元素和上下文
const canvas = document.getElementById('ellipseCanvas');
const ctx = canvas.getContext('2d');
// 设置Canvas尺寸
function resizeCanvas() {
const container = canvas.parentElement;
canvas.width = container.clientWidth;
canvas.height = container.clientHeight;
drawEllipse();
}
// 初始参数
let params = {
e: 0.6, // 离心率
c: 120, // 焦距 (焦点到中心的距离)
directrixX: 400, // 准线的x坐标
centerX: 200, // 椭圆中心的x坐标
centerY: 200, // 椭圆中心的y坐标
a: 0, // 椭圆长半轴 (将根据参数计算)
b: 0, // 椭圆短半轴 (将根据参数计算)
pointP: { x: 0, y: 0 } // 椭圆上的点P
};
// 更新椭圆参数
function updateEllipseParams() {
// 计算椭圆参数
// 从第二定义推导:a = e * d / sqrt(1 - e^2)
// 其中d是焦点到准线的距离
const d = Math.abs(params.directrixX - (params.centerX + params.c));
params.a = params.e * d / Math.sqrt(1 - params.e * params.e);
params.b = params.a * Math.sqrt(1 - params.e * params.e);
// 设置点P的初始位置 (椭圆上角度为60°的点)
const angle = Math.PI / 3; // 60度
params.pointP.x = params.centerX + params.a * Math.cos(angle);
params.pointP.y = params.centerY + params.b * Math.sin(angle);
}
// 获取滑块元素
const eccentricitySlider = document.getElementById('eccentricitySlider');
const focusSlider = document.getElementById('focusSlider');
const directrixSlider = document.getElementById('directrixSlider');
const eccentricityValue = document.getElementById('eccentricityValue');
const focusValue = document.getElementById('focusValue');
const directrixValue = document.getElementById('directrixValue');
// 滑块事件监听
eccentricitySlider.addEventListener('input', function() {
params.e = parseFloat(this.value);
eccentricityValue.textContent = params.e.toFixed(2);
updateEllipseParams();
drawEllipse();
});
focusSlider.addEventListener('input', function() {
params.c = parseInt(this.value);
focusValue.textContent = params.c;
updateEllipseParams();
drawEllipse();
});
directrixSlider.addEventListener('input', function() {
params.directrixX = parseInt(this.value);
directrixValue.textContent = params.directrixX;
updateEllipseParams();
drawEllipse();
});
// 初始化滑块值
eccentricityValue.textContent = params.e.toFixed(2);
focusValue.textContent = params.c;
directrixValue.textContent = params.directrixX;
// 鼠标交互状态
let dragging = false;
let dragTarget = null; // 'focus', 'directrix', 'pointP'
// 检查点是否在可拖动元素上
function getDragTarget(x, y) {
// 检查焦点
const focusX = params.centerX + params.c;
const focusY = params.centerY;
const focusDist = Math.sqrt((x - focusX) ** 2 + (y - focusY) ** 2);
if (focusDist < 10) return 'focus';
// 检查准线
const directrixDist = Math.abs(x - params.directrixX);
if (directrixDist < 10 && y > 50 && y < canvas.height - 50) return 'directrix';
// 检查椭圆上的点P
const pointPDist = Math.sqrt((x - params.pointP.x) ** 2 + (y - params.pointP.y) ** 2);
if (pointPDist < 10) return 'pointP';
return null;
}
// 处理鼠标按下事件
canvas.addEventListener('mousedown', function(e) {
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
dragTarget = getDragTarget(x, y);
if (dragTarget) {
dragging = true;
canvas.style.cursor = 'grabbing';
}
});
// 处理鼠标移动事件
canvas.addEventListener('mousemove', function(e) {
const rect = canvas.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
// 更新鼠标光标
if (!dragging) {
const target = getDragTarget(x, y);
canvas.style.cursor = target ? 'grab' : 'default';
}
// 处理拖动
if (dragging && dragTarget) {
if (dragTarget === 'focus') {
// 限制焦点只能在x轴上移动,且不能超过准线
const newFocusX = Math.max(params.centerX - params.a + 20, Math.min(x, params.directrixX - 20));
params.c = newFocusX - params.centerX;
focusSlider.value = params.c;
focusValue.textContent = params.c;
} else if (dragTarget === 'directrix') {
// 限制准线必须在焦点右侧
const focusX = params.centerX + params.c;
const newDirectrixX = Math.max(focusX + 50, Math.min(x, canvas.width - 50));
params.directrixX = newDirectrixX;
directrixSlider.value = params.directrixX;
directrixValue.textContent = params.directrixX;
} else if (dragTarget === 'pointP') {
// 计算椭圆上离鼠标最近的点
const angle = Math.atan2((y - params.centerY) / params.b, (x - params.centerX) / params.a);
params.pointP.x = params.centerX + params.a * Math.cos(angle);
params.pointP.y = params.centerY + params.b * Math.sin(angle);
}
updateEllipseParams();
drawEllipse();
}
});
// 处理鼠标释放事件
canvas.addEventListener('mouseup', function() {
dragging = false;
dragTarget = null;
});
canvas.addEventListener('mouseleave', function() {
dragging = false;
dragTarget = null;
});
// 绘制椭圆和所有元素
function drawEllipse() {
// 清除画布
ctx.clearRect(0, 0, canvas.width, canvas.height);
// 更新椭圆参数
updateEllipseParams();
// 绘制坐标轴
drawAxes();
// 绘制椭圆
drawEllipseCurve();
// 绘制焦点
drawFocus();
// 绘制准线
drawDirectrix();
// 绘制点P及其与焦点和准线的关系
drawPointP();
// 更新显示的距离值
updateDistanceValues();
}
// 绘制坐标轴
function drawAxes() {
ctx.save();
ctx.strokeStyle = '#aaa';
ctx.lineWidth = 1;
ctx.setLineDash([5, 5]);
// x轴
ctx.beginPath();
ctx.moveTo(0, params.centerY);
ctx.lineTo(canvas.width, params.centerY);
ctx.stroke();
// y轴
ctx.beginPath();
ctx.moveTo(params.centerX, 0);
ctx.lineTo(params.centerX, canvas.height);
ctx.stroke();
ctx.restore();
// 坐标轴标签
ctx.fillStyle = '#666';
ctx.font = '14px Arial';
ctx.fillText('x', canvas.width - 10, params.centerY - 5);
ctx.fillText('y', params.centerX + 5, 15);
// 原点标记
ctx.fillStyle = '#666';
ctx.beginPath();
ctx.arc(params.centerX, params.centerY, 3, 0, Math.PI * 2);
ctx.fill();
ctx.fillText('O', params.centerX + 8, params.centerY - 5);
}
// 绘制椭圆曲线
function drawEllipseCurve() {
ctx.save();
ctx.strokeStyle = '#e74c3c';
ctx.lineWidth = 2;
ctx.beginPath();
// 使用参数方程绘制椭圆
const steps = 100;
for (let i = 0; i <= steps; i++) {
const angle = (i / steps) * Math.PI * 2;
const x = params.centerX + params.a * Math.cos(angle);
const y = params.centerY + params.b * Math.sin(angle);
if (i === 0) {
ctx.moveTo(x, y);
} else {
ctx.lineTo(x, y);
}
}
ctx.stroke();
ctx.restore();
}
// 绘制焦点
function drawFocus() {
const focusX = params.centerX + params.c;
const focusY = params.centerY;
ctx.save();
// 焦点圆
ctx.fillStyle = '#27ae60';
ctx.beginPath();
ctx.arc(focusX, focusY, 8, 0, Math.PI * 2);
ctx.fill();
// 焦点标签
ctx.fillStyle = '#27ae60';
ctx.font = 'bold 16px Arial';
ctx.fillText('F', focusX + 10, focusY - 10);
// 显示焦点坐标
ctx.font = '14px Arial';
ctx.fillText(`(${focusX}, ${focusY})`, focusX + 15, focusY + 20);
ctx.restore();
}
// 绘制准线
function drawDirectrix() {
ctx.save();
// 准线
ctx.strokeStyle = '#3498db';
ctx.lineWidth = 2;
ctx.setLineDash([5, 3]);
ctx.beginPath();
ctx.moveTo(params.directrixX, 20);
ctx.lineTo(params.directrixX, canvas.height - 20);
ctx.stroke();
// 准线标签
ctx.fillStyle = '#3498db';
ctx.font = 'bold 16px Arial';
ctx.fillText('准线', params.directrixX + 10, 40);
// 准线方程
ctx.font = '14px Arial';
ctx.fillText(`x = ${params.directrixX}`, params.directrixX + 10, 60);
ctx.restore();
}
// 绘制点P及其关系
function drawPointP() {
const focusX = params.centerX + params.c;
const focusY = params.centerY;
// 计算点P到准线的垂足D
const dX = params.directrixX;
const dY = params.pointP.y;
ctx.save();
// 绘制点P到焦点的连线
ctx.strokeStyle = '#f39c12';
ctx.lineWidth = 2;
ctx.beginPath();
ctx.moveTo(params.pointP.x, params.pointP.y);
ctx.lineTo(focusX, focusY);
ctx.stroke();
// 绘制点P到准线的垂线
ctx.strokeStyle = '#9b59b6';
ctx.lineWidth = 2;
ctx.setLineDash([3, 3]);
ctx.beginPath();
ctx.moveTo(params.pointP.x, params.pointP.y);
ctx.lineTo(dX, dY);
ctx.stroke();
ctx.setLineDash([]);
// 绘制点P
ctx.fillStyle = '#e74c3c';
ctx.beginPath();
ctx.arc(params.pointP.x, params.pointP.y, 8, 0, Math.PI * 2);
ctx.fill();
// 绘制垂足D
ctx.fillStyle = '#9b59b6';
ctx.beginPath();
ctx.arc(dX, dY, 6, 0, Math.PI * 2);
ctx.fill();
// 点P标签
ctx.fillStyle = '#e74c3c';
ctx.font = 'bold 16px Arial';
ctx.fillText('P', params.pointP.x + 10, params.pointP.y - 10);
// 垂足D标签
ctx.fillStyle = '#9b59b6';
ctx.fillText('D', dX + 10, dY - 10);
// 显示点P坐标
ctx.font = '14px Arial';
ctx.fillText(`(${Math.round(params.pointP.x)}, ${Math.round(params.pointP.y)})`,
params.pointP.x + 15, params.pointP.y + 20);
// 标记距离PF
const midPFx = (params.pointP.x + focusX) / 2;
const midPFy = (params.pointP.y + focusY) / 2;
ctx.fillStyle = '#f39c12';
ctx.font = '14px Arial';
ctx.fillText('PF', midPFx, midPFy - 5);
// 标记距离PD
const midPDx = (params.pointP.x + dX) / 2;
const midPDy = (params.pointP.y + dY) / 2;
ctx.fillStyle = '#9b59b6';
ctx.fillText('PD', midPDx, midPDy - 5);
ctx.restore();
}
// 更新显示的距离值
function updateDistanceValues() {
const focusX = params.centerX + params.c;
const focusY = params.centerY;
// 计算PF距离
const pf = Math.sqrt(
(params.pointP.x - focusX) ** 2 +
(params.pointP.y - focusY) ** 2
);
// 计算PD距离
const pd = Math.abs(params.pointP.x - params.directrixX);
// 计算比值
const ratio = pf / pd;
// 更新显示
document.getElementById('pfValue').innerHTML = `PF = ${pf.toFixed(1)}`;
document.getElementById('pdValue').innerHTML = `PD = ${pd.toFixed(1)}`;
document.getElementById('ratioValue').innerHTML = `PF/PD = ${ratio.toFixed(2)}`;
}
// 初始化
window.addEventListener('load', function() {
resizeCanvas();
updateEllipseParams();
drawEllipse();
});
window.addEventListener('resize', resizeCanvas);
</script>
</body>
</html>