
本文介绍如何基于正态分布概率密度函数(pdf)生成覆盖整数区间 [0, 10] 的平滑钟形高度序列,峰值可自由设定于任意位置(如 x=3),并按需缩放至目标最大值(如 100);同时提供 chart.js + dragdata 插件实现可视化拖拽调参的完整教程。
在图形生成、地形建模或 UI 动效设计中,常需一组“自然起伏”的数值——例如为 11 个离散点(索引 0 到 10)分配高度值,使其整体呈钟形(单峰、对称或偏斜),且严格满足:
- 起点 x = 0 对应曲线左端,终点 x = 10 对应右端;
- 所有高度 ∈ [0, 100],且最大值恰好为 100;
- 峰值位置(mode)可配置(如居中 x = 5,或偏左至 x = 3);
- 支持实时交互调整(如拖拽控制点动态更新曲线)。
虽然贝塞尔曲线(如 BezierJS)能灵活拟合任意形状,但对“钟形”这一具有明确统计意义的形态,正态分布的概率密度函数(PDF)是更简洁、可控且数学严谨的选择。其公式为:
$$ f(x) = \frac{1}{\sigma\sqrt{2\pi}} \exp\left(-\frac{(x - \mu)^2}{2\sigma^2}\right) $$
其中:
- μ(mean)决定峰值横坐标(即你想要的“最高点在 x=3”,就设 mean = 3);
- σ(stdDev)控制曲线“宽度”:σ 越小,峰越尖锐;越大,峰越平缓;
- 函数本身输出值 ∈ (0, ≈0.4],需线性归一化至 [0, 100]。
以下是完整实现步骤:
✅ 步骤 1:计算原始 PDF 值并归一化
function calculateNormalPDF(x, mean, stdDev) {
return 1 / (stdDev * Math.sqrt(2 * Math.PI)) * Math.exp(-Math.pow(x - mean, 2) / (2 * Math.pow(stdDev, 2)));
}
const left = 0, right = 10, step = 1, maxHeight = 100;
const mean = 3; // ← 峰值位置:可设为 3、5、7 等任意值
const stdDev = 1.8; // ← 控制“胖瘦”:建议 1.2–2.5 间调试
// 采样 [0,1,2,...,10]
const dataPoints = [];
let maxY = 0;
for (let x = left; x <= right; x += step) {
const y = calculateNormalPDF(x, mean, stdDev);
dataPoints.push(y);
if (y > maxY) maxY = y;
}
// 线性缩放:使 max(y) → 100
const scaledPoints = dataPoints.map(y => (y / maxY) * maxHeight);
// 结果示例(mean=3, stdDev=1.8): [0.2, 1.5, 6.8, 22.4, 52.1, 89.7, 100, 84.3, 48.9, 19.2, 5.1]✅ 步骤 2:用 Chart.js 可视化 + 拖拽交互
引入 Chart.js 与 chartjs-plugin-dragdata(支持点击拖动数据点实时更新曲线):
const ctx = document.getElementById('myChart').getContext('2d');
new Chart(ctx, {
type: 'line',
data: {
labels: Array.from({length: 11}, (_, i) => i), // [0,1,...,10]
datasets: [{
data: scaledPoints,
label: 'Bell Curve (Drag points to adjust!)',
borderColor: '#36A2EB',
tension: 0.4, // 添加轻微曲率,视觉更柔和
pointRadius: 6,
pointHoverRadius: 8,
fill: false
}]
},
options: {
responsive: true,
plugins: {
legend: { display: true },
tooltip: { enabled: false },
dragData: {
round: 1,
onDrag: (e, datasetIndex, index, value) => {
// 拖拽后,可触发重计算逻辑(如反推新 mean/stdDev)
console.log(`Point ${index} moved to ${value.toFixed(1)}`);
}
}
},
scales: {
y: { beginAtZero: true, max: 110, ticks: { stepSize: 20 } },
x: { title: { display: true, text: 'Index' } }
}
}
});⚠️ 注意事项与进阶提示
- 非对称钟形? 标准正态 PDF 天然对称。若需明显偏斜(如长右尾),可改用 Gamma 分布 或分段二次/三次插值(如通过 3 个控制点构造单调递增再递减的三次贝塞尔);
- 精确拟合自定义点? 若你有一组目标高度(如 [0,10,40,100,60,20,5,0]),推荐使用 least-squares fitting 拟合二次/三次多项式,而非依赖统计分布;
- 性能敏感场景? PDF 计算开销极低(11 次指数运算),无需优化;但若需实时拖拽重绘数百点,建议预计算 LUT(查找表);
-
替代库参考:
- d3-shape 提供 d3.curveBasis(平滑样条)适合手绘风格;
- cubic-bezier 可解析 CSS 贝塞尔时间函数并采样;
- 自研 Canvas 贝塞尔拟合需结合 de Casteljau 算法 与最小二乘法,复杂度较高。
总结:对于“可控钟形高度生成”,正态 PDF 是精度、简洁性与可解释性的最佳平衡点。配合 Chart.js 的拖拽插件,你能在浏览器中快速构建一个微型、可交互的曲线设计器——无需复杂几何库,一行 mean 参数即可移动峰值,一次 stdDev 调节即可掌控形态,真正实现“所见即所得”的高度数据生成。










