gj/数学函数图像生成.html
2025-04-17 21:34:11 +08:00

558 lines
19 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!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.jsdelivr.net/npm/chart.js"></script>
<script src="https://cdn.jsdelivr.net/npm/mathjs@11.6.0/lib/browser/math.js"></script>
<style>
:root {
--primary-color: #4361ee;
--secondary-color: #3f37c9;
--accent-color: #4895ef;
--light-color: #f8f9fa;
--dark-color: #212529;
--success-color: #4cc9f0;
--warning-color: #f72585;
}
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
}
body {
background-color: #f5f7fa;
color: var(--dark-color);
line-height: 1.6;
padding: 20px;
min-width: 900px;
}
.container {
max-width: 1400px;
min-width: 900px;
margin: 0 auto;
padding: 30px;
background-color: white;
border-radius: 12px;
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.08);
}
header {
text-align: center;
margin-bottom: 30px;
padding-bottom: 20px;
border-bottom: 1px solid rgba(0, 0, 0, 0.1);
}
h1 {
color: var(--primary-color);
font-weight: 700;
margin-bottom: 10px;
font-size: 2.5rem;
}
.subtitle {
color: #6c757d;
font-size: 1.1rem;
margin-bottom: 15px;
}
.intro-box {
background-color: #f8f9fa;
padding: 20px;
border-radius: 8px;
margin-bottom: 30px;
border-left: 4px solid var(--accent-color);
}
.intro-box h3 {
color: var(--primary-color);
margin-bottom: 10px;
}
.intro-box ul {
padding-left: 20px;
}
.intro-box li {
margin-bottom: 5px;
}
.app-container {
display: grid;
grid-template-columns: 1fr 2fr;
gap: 30px;
}
.control-panel {
background-color: #f8f9fa;
padding: 25px;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
min-width: 350px;
}
.form-group {
margin-bottom: 20px;
}
label {
display: block;
margin-bottom: 8px;
font-weight: 600;
color: var(--dark-color);
}
input, select {
width: 100%;
padding: 12px 15px;
border: 1px solid #ced4da;
border-radius: 6px;
font-size: 1rem;
transition: all 0.3s;
}
input:focus, select:focus {
outline: none;
border-color: var(--primary-color);
box-shadow: 0 0 0 3px rgba(67, 97, 238, 0.2);
}
button {
background-color: var(--primary-color);
color: white;
border: none;
padding: 12px 20px;
border-radius: 6px;
cursor: pointer;
font-size: 1rem;
font-weight: 600;
transition: all 0.3s;
width: 100%;
margin-top: 10px;
}
button:hover {
background-color: var(--secondary-color);
transform: translateY(-2px);
}
.chart-container {
position: relative;
height: 500px;
background-color: white;
border-radius: 10px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.05);
padding: 15px;
min-width: 500px;
}
.examples-panel {
margin-top: 25px;
padding-top: 20px;
border-top: 1px solid rgba(0, 0, 0, 0.1);
}
.examples-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
cursor: pointer;
}
.examples-header h3 {
color: var(--primary-color);
margin: 0;
}
.examples-content {
display: none;
padding: 10px;
background-color: white;
border-radius: 6px;
}
.examples-content.show {
display: block;
}
.example-list {
list-style: none;
}
.example-list li {
margin-bottom: 10px;
padding: 10px;
background-color: white;
border-radius: 6px;
border-left: 4px solid var(--accent-color);
cursor: pointer;
transition: all 0.2s;
}
.example-list li:hover {
background-color: #e9ecef;
transform: translateX(5px);
}
.error-message {
color: var(--warning-color);
margin-top: 10px;
font-size: 0.9rem;
display: none;
}
.footer {
text-align: center;
margin-top: 40px;
color: #6c757d;
font-size: 0.9rem;
}
.toggle-icon {
transition: transform 0.3s;
}
.toggle-icon.rotated {
transform: rotate(180deg);
}
.real-time-btn {
background-color: var(--accent-color);
}
.real-time-btn.active {
background-color: var(--success-color);
}
.button-group {
display: flex;
gap: 10px;
}
.button-group button {
margin-top: 0;
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>数学函数图像生成器</h1>
<p class="subtitle">拥有基本运算,输入框不支持更高级参数填入</p>
</header>
<div class="intro-box">
<h3>使用说明</h3>
<p>手机请缩小屏幕,输入数学函数表达式,设置参数范围,即可生成精美的函数图像。</p>
<h3>支持的运算符和函数:</h3>
<ul>
<li>基本运算: +, -, *, /, ^ (幂)</li>
<li>三角函数: sin, cos, tan, asin, acos, atan</li>
<li>对数函数: log, log10, ln</li>
<li>其他函数: sqrt, abs, ceil, floor, round</li>
<li>常数: pi, e</li>
</ul>
<h3>注意事项:</h3>
<ul>
<li>使用 x 作为变量,区分大小写</li>
<li>不支持隐式乘法,必须使用 * 运算符</li>
<li>不支持分段函数和条件表达式</li>
<li>程序预留有示例方案</li>
<li>某些函数在特定区间可能无定义(如log(0))</li>
<li>复数结果将显示为NaN</li>
<li>无法生成创意图案,因为输入框限制,而且这个不是直角系</li>
<li>千万不要把所有数值输入过大和过小,否则会卡</li>
</ul>
</div>
<div class="app-container">
<div class="control-panel">
<div class="form-group">
<label for="function-input">函数表达式 (使用 x 作为变量)</label>
<input type="text" id="function-input" placeholder="例如: sin(x), x^2 + 2*x + 1" value="sin(x)">
</div>
<div class="form-group">
<label for="x-min">X 最小值</label>
<input type="number" id="x-min" value="-10" step="0.1">
</div>
<div class="form-group">
<label for="x-max">X 最大值</label>
<input type="number" id="x-max" value="10" step="0.1">
</div>
<div class="form-group">
<label for="step">步长 (精度)</label>
<input type="number" id="step" value="0.1" step="0.01" min="0.01">
</div>
<div class="form-group">
<label for="line-color">线条颜色</label>
<select id="line-color">
<option value="#4361ee">蓝色</option>
<option value="#f72585">粉色</option>
<option value="#4cc9f0">青色</option>
<option value="#7209b7">紫色</option>
<option value="#3a0ca3">深蓝</option>
</select>
</div>
<div class="button-group">
<button id="plot-button">绘制函数图像</button>
<button id="real-time-btn" class="real-time-btn">实时生成图像</button>
</div>
<div id="error-message" class="error-message"></div>
<div class="examples-panel">
<div class="examples-header" id="examples-toggle">
<h3>示例函数</h3>
<svg class="toggle-icon" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="6 9 12 15 18 9"></polyline>
</svg>
</div>
<div class="examples-content" id="examples-content">
<ul class="example-list">
<li onclick="useExample('sin(x)')">正弦函数: sin(x)</li>
<li onclick="useExample('cos(x)')">余弦函数: cos(x)</li>
<li onclick="useExample('tan(x)')">正切函数: tan(x)</li>
<li onclick="useExample('x^2')">二次函数: x^2</li>
<li onclick="useExample('x^3 - 3*x')">三次函数: x^3 - 3*x</li>
<li onclick="useExample('sqrt(x)')">平方根函数: sqrt(x)</li>
<li onclick="useExample('log(x)')">自然对数: log(x)</li>
<li onclick="useExample('log10(x)')">常用对数: log10(x)</li>
<li onclick="useExample('exp(x)')">指数函数: exp(x)</li>
<li onclick="useExample('abs(x)')">绝对值函数: abs(x)</li>
<li onclick="useExample('sin(x)/x')">sinc函数: sin(x)/x</li>
<li onclick="useExample('1/x')">反比例函数: 1/x</li>
</ul>
</div>
</div>
</div>
<div class="chart-container">
<canvas id="function-chart"></canvas>
</div>
</div>
<div class="footer">
<p>© 2023 高级数学可视化工具 | 使用 Chart.js 和 Math.js 构建 | 支持缩放和平移功能</p>
</div>
</div>
<script>
// 初始化图表
const ctx = document.getElementById('function-chart').getContext('2d');
let chart = new Chart(ctx, {
type: 'line',
data: {
datasets: [{
label: '函数图像(支持横向触摸图像有信息跟踪)',
borderColor: '#4361ee',
borderWidth: 3,
pointRadius: 0,
fill: false,
tension: 0.1
}]
},
options: {
responsive: true,
maintainAspectRatio: false,
scales: {
x: {
title: {
display: true,
text: 'X轴',
font: {
size: 14,
weight: 'bold'
}
},
grid: {
color: 'rgba(0, 0, 0, 0.05)'
}
},
y: {
title: {
display: true,
text: 'Y轴',
font: {
size: 14,
weight: 'bold'
}
},
grid: {
color: 'rgba(0, 0, 0, 0.05)'
}
}
},
plugins: {
legend: {
position: 'top',
labels: {
font: {
size: 14
}
}
},
tooltip: {
mode: 'index',
intersect: false,
callbacks: {
label: function(context) {
return `f(${context.parsed.x.toFixed(2)}) = ${context.parsed.y.toFixed(4)}`;
}
}
},
zoom: {
zoom: {
wheel: {
enabled: true,
},
pinch: {
enabled: true
},
mode: 'xy',
},
pan: {
enabled: true,
mode: 'xy',
}
}
}
}
});
// 实时生成状态
let isRealTime = false;
let inputElements = [
document.getElementById('function-input'),
document.getElementById('x-min'),
document.getElementById('x-max'),
document.getElementById('step'),
document.getElementById('line-color')
];
// 防抖函数,避免频繁更新
function debounce(func, wait) {
let timeout;
return function() {
const context = this;
const args = arguments;
clearTimeout(timeout);
timeout = setTimeout(() => {
func.apply(context, args);
}, wait);
};
}
// 绘制函数
function plotFunction() {
const funcInput = document.getElementById('function-input').value;
const xMin = parseFloat(document.getElementById('x-min').value);
const xMax = parseFloat(document.getElementById('x-max').value);
const step = parseFloat(document.getElementById('step').value);
const lineColor = document.getElementById('line-color').value;
const errorElement = document.getElementById('error-message');
errorElement.style.display = 'none';
try {
// 生成x值数组
const xValues = [];
for (let x = xMin; x <= xMax; x += step) {
xValues.push(x);
}
// 计算y值
const yValues = [];
const scope = {};
for (let i = 0; i < xValues.length; i++) {
scope.x = xValues[i];
try {
const y = math.evaluate(funcInput, scope);
yValues.push(y);
} catch (e) {
// 对于某些x值可能无法计算如log(0)
yValues.push(NaN);
}
}
// 更新图表
chart.data.labels = xValues;
chart.data.datasets[0].data = yValues.map((y, i) => ({x: xValues[i], y: y}));
chart.data.datasets[0].borderColor = lineColor;
chart.update();
} catch (error) {
errorElement.textContent = `错误: ${error.message}`;
errorElement.style.display = 'block';
}
}
// 使用示例函数
function useExample(func) {
document.getElementById('function-input').value = func;
if (isRealTime) {
plotFunction();
}
}
// 切换实时生成模式
function toggleRealTime() {
const realTimeBtn = document.getElementById('real-time-btn');
isRealTime = !isRealTime;
if (isRealTime) {
realTimeBtn.classList.add('active');
realTimeBtn.textContent = '实时生成中...';
// 立即绘制一次
plotFunction();
} else {
realTimeBtn.classList.remove('active');
realTimeBtn.textContent = '实时生成图像';
}
}
// 设置输入框监听
function setupInputListeners() {
const debouncedPlot = debounce(plotFunction, 500);
inputElements.forEach(element => {
element.addEventListener('input', () => {
if (isRealTime) {
debouncedPlot();
}
});
});
}
// 切换示例面板
document.getElementById('examples-toggle').addEventListener('click', function() {
const content = document.getElementById('examples-content');
const icon = this.querySelector('.toggle-icon');
content.classList.toggle('show');
icon.classList.toggle('rotated');
});
// 事件监听
document.getElementById('plot-button').addEventListener('click', plotFunction);
document.getElementById('real-time-btn').addEventListener('click', toggleRealTime);
// 设置输入监听
setupInputListeners();
// 初始绘制
plotFunction();
</script>
</body>
</html>