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

如何用SVG绘制无渐近线初等函数的平滑路径

如何用SVG绘制无渐近线初等函数的平滑路径

我最近一直在折腾用SVG绘制数学函数的平滑路径,像x²这种简单函数画出来效果挺不错,但碰到1/x这类带渐近线的函数时,路径就容易出现断裂或者不平滑的问题。我的目标很明确:全程只用SVG(绝不碰<canvas>),就能生成任意无渐近线初等函数的平滑曲线。

这个实现的核心逻辑是基于贝塞尔曲线的路径生成——先采样函数上的离散点,再计算对应的贝塞尔控制点来连接成连续平滑的曲线。下面是完整的可运行代码,我会在后面拆解关键细节:


HTML 部分

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <title>SVG函数曲线绘制</title>
    <meta name="viewport" content="width=device-width, initial-scale=1" />
  </head>
  <body>
    <svg
      id="svg"
      width="500"
      height="500"
      style="border: solid; fill: none; stroke: black"
    >
      <path id="thePath" />
    </svg>
    <script src="script.js"></script>
  </body>
</html>

JavaScript 部分

// 平滑度控制:数值越小越贴近原始采样点,越大曲线越圆润
let smooth = 0.1;
let container = document.getElementById("svg");
const width = Number(container.getAttribute("width"));
const height = Number(container.getAttribute("height"));

// 数学坐标范围(根据绘制的函数调整,避开渐近线)
let xmin = 1; // 避开1/x的渐近线x=0
let xmax = 10;
let ymin = -1;
let ymax = 1;

// 替换成你要绘制的无渐近线初等函数
function f(x) {
  return 1 / x; // 注意:如果函数有渐近线,必须确保x范围避开渐近点
  // return Math.sin(x); // 示例:无渐近线的正弦函数,平滑效果极佳
  // return Math.sqrt(x); // 示例:x≥0时无渐近线的平方根函数
}

// 生成函数上的采样点:x从xStart到xEnd,共samples个点
function generatePoints(xStart, xEnd, samples = 50) {
  const result = [];
  const xInc = (xEnd - xStart) / samples;
  for (let x = xStart; x < xEnd; x += xInc) {
    const xVal = coordX(x);
    const yVal = coordY(f(x));
    const point = { x: xVal, y: yVal };
    // 只保留在SVG画布范围内的有效点
    if (isValidPoint(point)) {
      result.push(point);
    }
  }
  return result;
}

// 验证点是否在SVG画布可视范围内
function isValidPoint(point) {
  return 0 <= point.x && point.x <= width && 0 <= point.y && point.y <= height;
}

// 计算贝塞尔曲线的控制点——这是曲线平滑的核心
function controlPoints(p) {
  const pc = [];
  for (let i = 1; i < p.length - 1; i++) {
    const dx = p[i - 1].x - p[i + 1].x;
    const dy = p[i - 1].y - p[i + 1].y;
    // 第一个贝塞尔控制点
    const o1 = {
      x: p[i].x - dx * smooth,
      y: p[i].y - dy * smooth
    };
    // 第二个贝塞尔控制点
    const o2 = {
      x: p[i].x + dx * smooth,
      y: p[i].y + dy * smooth
    };
    pc[i] = [o1, o2];
  }
  return pc;
}

// 生成SVG路径的d属性值(路径指令字符串)
function drawCurve(p) {
  if (p.length === 0) return "";
  const pc = controlPoints(p);
  let d = `M${p[0].x},${p[0].y}`;
  
  if (p.length === 1) return d;
  
  // 第一段曲线用二次贝塞尔
  d += ` Q${pc[1][1].x},${pc[1][1].y}, ${p[1].x},${p[1].y}`;
  
  if (p.length > 2) {
    // 中间段用三次贝塞尔保证平滑过渡
    for (let i = 1; i < p.length - 2; i++) {
      d += ` C${pc[i][0].x}, ${pc[i][0].y}, ${pc[i + 1][1].x}, ${pc[i + 1][1].y}, ${p[i + 1].x},${p[i + 1].y}`;
    }
    // 最后一段用二次贝塞尔收尾
    const n = p.length - 1;
    d += ` Q${pc[n - 1][0].x}, ${pc[n - 1][0].y}, ${p[n].x}, ${p[n].y}`;
  }
  return d;
}

// 数学x坐标转SVG画布x坐标
function coordX(x) {
  return Math.round((width * (x - xmin)) / (xmax - xmin));
}

// 数学y坐标转SVG画布y坐标(注意SVG的y轴向下,需反转)
function coordY(y) {
  return Math.round(height * (1 - (y - ymin) / (ymax - ymin)));
}

// 初始化绘制流程
const points = generatePoints(xmin, xmax, 100);
const thePath = document.getElementById("thePath");
thePath.setAttribute("d", drawCurve(points));
// 优化路径视觉效果的属性
thePath.setAttribute("stroke-width", 2);
thePath.setAttribute("opacity", 1);
thePath.setAttribute("stroke-linecap", "circle");
thePath.setAttribute("stroke-linejoin", "round");
thePath.setAttribute("shape-rendering", "geometricPrecision");

关键细节说明

  1. 坐标转换逻辑:coordX和coordY负责把数学坐标系的点转换成SVG画布的像素坐标,注意SVG的y轴是向下的,所以coordY做了反转处理,保证数学上的“向上”对应SVG的“向上”。
  2. 平滑度调节:smooth变量控制曲线的平滑程度,取值建议在0.05~0.2之间:数值越小,曲线越贴近原始采样点;数值越大,曲线越圆润。
  3. 采样点数量:generatePoints的第三个参数是采样数,采样越多曲线越平滑,但过多会影响性能,50~200是兼顾效果和性能的区间。
  4. 渐近线避坑:如果函数有渐近线(比如1/x在x=0处),一定要调整xmin和xmax避开渐近点,否则会生成无效点导致路径断裂。
  5. 视觉优化属性:stroke-linejoin="round"和shape-rendering="geometricPrecision"能大幅提升曲线的视觉平滑度,建议不要省略。

你可以直接把这段代码复制到HTML文件里运行,替换f(x)里的函数就能绘制不同的曲线了,亲测无渐近线的初等函数都能得到非常平滑的效果。

备注:内容来源于stack exchange,提问作者M. Al Jumaily

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.15 03:43:08