You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用JavaScript Canvas精准绘制y=log(29.565ln(x)-4.4898)曲线?

精准绘制目标函数的方案指导

一、替代方案:直接采样函数点绘制折线(最高精度)

如果追求完全贴合函数的表现,完全可以跳过贝塞尔曲线的拟合步骤,直接对函数在区间[1.64003, 4]内进行密集采样,用Canvas的lineTo连接成折线,视觉上和曲线几乎无差别:

  1. 确定采样步长:可根据精度需求调整,比如取0.005,或根据画布像素密度动态设置(每1像素对应一个采样点)
  2. 实现代码:
const ctx = canvas.getContext('2d');
ctx.beginPath();
const startX = 1.64003;
const endX = 4;
const step = 0.005; // 采样步长,越小精度越高

// 移动到起点
let x = startX;
let y = Math.log(29.565 * Math.log(x) - 4.4898);
ctx.moveTo(x, y);

// 遍历采样并绘制线段
for (x = startX + step; x <= endX; x += step) {
  y = Math.log(29.565 * Math.log(x) - 4.4898);
  ctx.lineTo(x, y);
}
ctx.stroke();

这种方法完全复刻函数行为,没有拟合误差,实现成本极低。

二、三次贝塞尔曲线精准拟合方法

如果一定要使用三次贝塞尔曲线,可通过函数端点导数计算控制点,步骤如下:

  1. 推导目标函数的一阶导数:
    目标函数为 ( y = \ln(29.565\ln x - 4.4898) ),根据链式法则可得:
    ( y' = \frac{29.565}{x(29.565\ln x - 4.4898)} )
  2. 计算起点和终点的导数值,再推导控制点:
    三次贝塞尔曲线的切线由端点导数决定,可按切线方向取固定比例偏移得到控制点,代码实现:
// 计算目标函数在指定x处的一阶导数
function getDerivative(x) {
  const lnX = Math.log(x);
  const denominator = x * (29.565 * lnX - 4.4898);
  return 29.565 / denominator;
}

const startX = 1.64003;
const startY = -4.07205084;
const endX = 4;
const endY = 1.562245182;

const startDeriv = getDerivative(startX);
const endDeriv = getDerivative(endX);

// 控制点偏移长度,可根据拟合效果调整(区间宽度的1/3是常用值)
const offset = (endX - startX) / 3;

// 计算两个贝塞尔控制点
const cp1x = startX + offset;
const cp1y = startY + offset * startDeriv;

const cp2x = endX - offset;
const cp2y = endY - offset * endDeriv;

// 绘制三次贝塞尔曲线
ctx.beginPath();
ctx.moveTo(startX, startY);
ctx.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, endX, endY);
ctx.stroke();

如果拟合精度不足,可调整offset的比例(比如改成1/4或1/2),或增加采样点做误差修正。

三、最大曲率点定位与二次贝塞尔拆分

若想用两段二次贝塞尔曲线拼接,可通过以下步骤精准定位最大曲率点:

  1. 推导目标函数的曲率公式:
    曲率 ( K = \frac{|y''|}{(1 + (y')2){3/2}} ),先求二阶导数 ( y'' ):
    由一阶导数推导可得:
    ( y'' = -\frac{29.565(29.565(\ln x + 1) - 4.4898)}{x^2(29.565\ln x - 4.4898)^2} )
  2. 用数值方法(黄金分割法)寻找曲率最大值点:
// 计算指定x处的曲率值
function getCurvature(x) {
  const lnX = Math.log(x);
  const inner = 29.565 * lnX - 4.4898;
  const yPrime = 29.565 / (x * inner);
  const yDoublePrime = -29.565 * (29.565 * (lnX + 1) - 4.4898) / (x*x * inner*inner);
  return Math.abs(yDoublePrime) / Math.pow(1 + yPrime*yPrime, 1.5);
}

// 黄金分割法寻找区间内的最大曲率点
function findMaxCurvature(start, end, tolerance = 1e-6) {
  const phi = (1 + Math.sqrt(5)) / 2;
  let a = start;
  let b = end;
  let c = b - (b - a)/phi;
  let d = a + (b - a)/phi;
  while (Math.abs(c - d) > tolerance) {
    if (getCurvature(c) < getCurvature(d)) {
      a = c;
    } else {
      b = d;
    }
    c = b - (b - a)/phi;
    d = a + (b - a)/phi;
  }
  return (a + b)/2;
}

// 获取最大曲率点的坐标
const maxCurvX = findMaxCurvature(1.64003, 4);
const maxCurvY = Math.log(29.565 * Math.log(maxCurvX) - 4.4898);

得到最大曲率点后,分别对[起点, 曲率点]和[曲率点, 终点]两段用二次贝塞尔拟合,二次贝塞尔的控制点可通过两端点的导数和中间点计算。


内容的提问来源于stack exchange,提问作者Fabr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 02:46:19