SVG 之椭圆弧

绘制直线段相对简单,因为路径上的两个点就唯一确定了它们之间的线段。但如果是曲线的话,由于在两个点之间可以绘制无限条曲线,因此我们必须给出额外信息,以便在它们之间绘制一条曲线路径。这里要研究的最简单的曲线为椭圆弧,也就是绘制一个连接两个点的椭圆的一部分。

尽管弧是视觉上最简单的曲线,但是指定一条唯一的曲线所需要的信息却是最多的。需要指定的第一部分信息是点所在椭圆的 x 半径和 y 半径。椭圆的范围可以缩小为两个,正如在图中(a)部分可以看到的。两个点将两个椭圆划分为 4 个圆弧。其中(b)和(c)是小于 180 度的弧,(d)和(e)都大于 180 度。看看(b)和(c),你会发现它们的方向不同,(b)是按照负角度增加(逆时针)方向绘制的,(c)是按照正角度增加(顺时针)方向绘制的。同样的关系在(d)和(e)之间也成立。

椭圆弧命令

这还并没有唯一确定潜在的弧!因为并没有规定说椭圆的 x 半径必须平行于 x 轴。图中的(f)部分展示了两个点和它们所在的相对于 x 轴旋转了 30 度的椭圆。(上图改编自 W3C SVG 规范的 8.3.8 小节。)

圆弧命令以字母 A(绝对坐标的缩写)或者 a(相对坐标的缩写)开始,后面紧跟以下 7 个参数。

下面的路径绘制了图上中(b)到(e)部分的椭圆弧:

<path d="M 125,75 A100,50 0 0,0 225,125"/> <!-- b -->
<path d="M 125,75 A100,50 0 0,1 225,125"/> <!-- c -->
<path d="M 125,75 A100,50 0 1,0 225,125"/> <!-- d -->
<path d="M 125,75 A100,50 0 1,1 225,125"/> <!-- e -->

在下面的互动示例中,你可以尝试所有圆弧参数来看看它们的作用:

我们不能使用一个路径命令绘制一个完整的椭圆;如果弧形的起点和终点相同,则有无数种方式定位椭圆。SVG 阅读器会跳过这样的圆弧命令。如果你指定的椭圆半径太小,导致不能覆盖起点和终点,则 SVG 阅读器会扩大椭圆直到它足够覆盖起点和终点。

绘制要一个独立的半椭圆弧的代码如下:

<svg width="600" height="600" viewBox="0 0 600 600" style="border: 1px solid #ccc;">
    <!-- 左上角 - 上半圆 -->
    <path d="M 50 150 A 100 100 0 1 1 250 150" stroke="#ffff00" stroke-width="4" fill="none" />
    
    <!-- 右上角 - 右半圆 -->
    <path d="M 450 50 A 100 100 0 1 1 450 250" stroke="#ff0000" stroke-width="4" fill="none" />
    
    <!-- 右下角 - 下半圆 -->
    <path d="M 350 450 A 100 100 0 1 0 550 450" stroke="#00ff00" stroke-width="4" fill="none" />
    
    <!-- 左下角 - 左半圆 -->
    <path d="M 150 350 A 100 100 0 1 0 150 550" stroke="#0000ff" stroke-width="4" fill="none" />
</svg>

A 标签也可以也可以连续设置,下面太极图中的右侧黑色部分就是如此绘制出来的。代码如下:

<svg width="400" height="400" viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg"
  style="border: 1px solid #ccc; margin: auto; display: block">
  <!-- 从200,50出发的黑色填充图形 -->
  <path d="M 200 50 A 150 150 0 0 1 200 350 A 75 75 0 0 1 200 200 A 75 75 0 0 0 200 50"
     style="fill: #000000;" />
  <!-- 正中心无色,黑色边框,半径150的圆 -->
  <circle cx="200" cy="200" r="150" fill="none" stroke="#000000" stroke-width="4" />
  <!-- 200,87.5处黑色圆,半径20 -->
  <circle cx="200" cy="125.5" r="20" fill="#000000" />
  <!-- 200,312.5处白色圆,半径20 -->
  <circle cx="200" cy="274.5" r="20" fill="#ffffff" />
</svg>

从其他弧线格式转换

你可能疑惑,为什么 SVG 选择一种看似古怪的方法来指定弧形,为什么不能像其他矢量图形系统那样,通过给椭圆定义一个中心点、xy 半径、起始角度和弧形角度来指定一个弧形。这是因为在 SVG 中,弧形并不能单独存在,它要成为线和曲线连接路径的一部分(比如,一个圆角矩形就是由一系列线和椭圆弧组成的)。因此,通过终点指定弧形是合理的。

若想要绘制一个指定中心和角度的任意弧,并将它转换为 SVG 的终点和范围格式,可以用下面的 JavaScript 代码实现,用于将中心和角度格式的圆弧转换为适当的形式,放到 SVG <path> 中。在上面的互动程序中,右侧的选项卡“变换的圆弧参数”演示的就是这个功能。

/*
将一个围绕中心的椭圆弧转换为适用于SVG的参数化的椭圆弧

参数:
中心x坐标
中心y坐标
椭圆x半径
椭圆y半径
圆弧开始角度
圆弧所跨度数
x轴旋转角度

返回一个数组,包含:
弧起点的x坐标
弧起点的y坐标
椭圆x半径
椭圆y半径
x轴旋转角度
SVG规范中定义的大弧形标志
SVG规范中定义的范围标志
弧末端的x坐标
弧末端的y坐标
*/

function centeredToSVG(cx,cy,rx,ry,theta,delta,phi)
{
    var endTheta, phiRad;
    var x0, y0, x1, y1, largeArc, sweep;

    /* 将角度转换为弧度。需要一个单独的变量把phi变为弧度,因为必须保持phi为度数形式用于返回值 */
    theta = theta * Math.PI / 180.0;
    endTheta = (theta + delta) * Math.PI / 180.0;
    phiRad = phi * Math.PI / 180.0;

    /*
    找出起点和终点的坐标
    */
    x0 = cx + Math.cos(phiRad) * rx * Math.cos(theta) +
    Math.sin(-phiRad) * ry * Math.sin(theta);

    y0 = cy + Math.sin(phiRad) * rx * Math.cos(theta) +
    Math.cos(phiRad) * ry * Math.sin(theta);

    x1 = cx + Math.cos(phiRad) * rx * Math.cos(endTheta) +
    Math.sin(-phiRad) * ry * Math.sin(endTheta);

    y2 = cy + Math.sin(phiRad) * rx * Math.cos(endTheta) +
    Math.cos(phiRad) * ry * Math.sin(endTheta);

    largeArc = (delta > 180) ? 1 : 0;
    sweep = (delta > 0) ? 1 : 0;

    return [x0, y0, rx, ry, phi, largeArc, sweep, x1, y1];
}

发布时间:2026/1/22 下午10:23:26  阅读次数:101

2006 - 2026,推荐分辨率 1024*768 以上,推荐浏览器 Chrome、Edge 等现代浏览器,截止 2021 年 12 月 5 日的访问次数:1872 万 9823 站长邮箱

沪 ICP 备 18037240 号-1

沪公网安备 31011002002865 号