在上一篇文章中,我们梳理了 SVG 元素的“家族谱系”,知道了 SVGRectElement、SVGCircleElement 和 SVGEllipseElement 都共同继承自 SVGGeometryElement。换句话说,它们身上流淌着相同的“几何基因”——都拥有 getTotalLength()、getPointAtLength() 等几何计算能力。

但每个图形又有自己独特的“性格”:矩形有 width 和 height,圆形有 r(半径),椭圆则有 rx 和 ry(两个方向的半径)。理解这些异同,是你在 JavaScript 中精准操控它们的前提。

本文将从“声明 vs 操控” 的双重视角出发,带你逐一掌握这三种基础图形的接口用法,并最终通过一个综合交互 Demo,把理论知识转化为实战能力。

一、矩形:SVGRectElement

<rect> 是 SVG 中最基础的图形元素之一,用于绘制矩形——也可以带圆角。对应的 DOM 接口是 SVGRectElement。

1.1 核心属性一览

属性

对应标签属性

说明

x

x

矩形左上角的 x 坐标

y

y

矩形左上角的 y 坐标

width

width

矩形的宽度

height

height

矩形的高度

rx

rx

圆角矩形在 x 方向的圆角半径

ry

ry

圆角矩形在 y 方向的圆角半径

关键认知:这些属性都是只读的。这意味着你不能直接通过 rect.width = 200 来修改宽度。正确的修改方式是通过 setAttribute() 方法,或者操作 SVGAnimatedLength 对象的 baseVal 属性。

1.2 读取与修改的正确姿势
<svg viewBox="0 0 400 300">
  <rect id="box" x="50" y="50" width="120" height="80" fill="blue" />
</svg>

const rect = document.getElementById('box');

// 错误示范:直接赋值无效
// rect.width = 200;  // 不会报错,但也不会生效

// 正确方式一:使用 setAttribute
rect.setAttribute('width', 200);

// 正确方式二:操作 baseVal(适用于需要读取精确值的场景)
console.log(rect.width.baseVal.value);  // 120
rect.width.baseVal.value = 200;         // 修改成功

// 读取当前值
console.log(rect.x.baseVal.value);      // 50
console.log(rect.y.baseVal.value);      // 50
console.log(rect.height.baseVal.value); // 80
1.3 圆角矩形的特殊处理

rx 和 ry 用于控制矩形的圆角。当只设置 rx 而不设置 ry 时,ry 默认等于 rx 的值。如果 rx 或 ry 的值超过矩形宽度或高度的一半,浏览器会自动将其限制为有效值。

const rect = document.getElementById('box');

// 设置圆角
rect.setAttribute('rx', 20);
rect.setAttribute('ry', 20);

// 读取圆角半径
console.log(rect.rx.baseVal.value);  // 20
console.log(rect.ry.baseVal.value);  // 20

重要提示:rx 和 ry 除了作为 SVG 属性外,也可以作为 CSS 属性 使用。但要注意,CSS 属性优先级高于 SVG 属性,如果两者同时存在,CSS 值会覆盖 SVG 属性值。

二、圆形:SVGCircleElement

<circle> 元素用于基于圆心和半径绘制圆形。对应的 DOM 接口是 SVGCircleElement。

2.1 核心属性一览

SVGCircleElement 的实例属性同样全部是只读的 SVGAnimatedLength 类型:

属性

对应标签属性

说明

cx

cx

圆心的 x 坐标(默认为 0)

cy

cy

圆心的 y 坐标(默认为 0)

r

r

圆的半径

2.2 操作示范
<svg viewBox="0 0 400 300">
  <circle id="dot" cx="200" cy="150" r="60" fill="gold" />
</svg>

const circle = document.getElementById('dot');

// 读取圆心坐标和半径
console.log(circle.cx.baseVal.value);  // 200
console.log(circle.cy.baseVal.value);  // 150
console.log(circle.r.baseVal.value);   // 60

// 修改——通过 setAttribute
circle.setAttribute('r', 80);
circle.setAttribute('cx', 250);

// 修改——通过 baseVal
circle.r.baseVal.value = 100;
2.3 一个常见陷阱

很多开发者会直觉地写出 circle.r = 100,然后发现没有任何变化。记住:SVGCircleElement 的属性都是只读的 SVGAnimatedLength 对象,不是普通的数字属性。正确的做法是使用 setAttribute 或操作 baseVal.value。

三、椭圆:SVGEllipseElement

<ellipse> 元素是圆形的“广义版本”——它在 x 和 y 两个方向上可以有不同的半径。对应的 DOM 接口是 SVGEllipseElement。

3.1 核心属性一览

SVGEllipseElement 的实例属性同样全部是只读的 SVGAnimatedLength 类型:

属性

对应标签属性

说明

cx

cx

椭圆中心的 x 坐标

cy

cy

椭圆中心的 y 坐标

rx

rx

椭圆在 x 方向的半径(水平半径)

ry

ry

椭圆在 y 方向的半径(垂直半径)

3.2 操作示范
<svg viewBox="0 0 400 300">
  <ellipse id="oval" cx="200" cy="150" rx="120" ry="60" fill="coral" />
</svg>

const ellipse = document.getElementById('oval');

// 读取
console.log(ellipse.cx.baseVal.value);  // 200
console.log(ellipse.cy.baseVal.value);  // 150
console.log(ellipse.rx.baseVal.value);  // 120
console.log(ellipse.ry.baseVal.value);  // 60

// 修改——通过 setAttribute
ellipse.setAttribute('rx', 150);
ellipse.setAttribute('ry', 80);

// 读取时还可以用 valueAsString 获取带单位的原始值
console.log(ellipse.rx.baseVal.valueAsString);  // "150"

实用技巧:valueAsString 可以获取属性值的原始字符串形式(包括单位,如 "150px" 或 "50%"),而 value 只返回纯数值。

四、它们共同的“超能力”:继承自 SVGGeometryElement

虽然三种图形的属性各不相同,但它们的接口都继承自 SVGGeometryElement。这意味着它们共享以下实用方法:

方法

说明

getTotalLength()

返回图形轮廓的总长度

getPointAtLength(distance)

返回轮廓上指定距离处的坐标点

isPointInFill(point)

判断点是否在填充区域内

isPointInStroke(point)

判断点是否在描边区域内

这些方法在实现路径动画、精确点击检测等高级交互时非常有用。我们将在后续文章中深入探讨。

五、综合实战:构建一个“图形控制面板”

理论讲完了,让我们把这些知识串联起来,构建一个可交互的图形控制面板——用户可以通过滑块实时调整图形的尺寸和位置。

5.1 HTML 结构
<svg viewBox="0 0 500 400" id="canvas">
  <!-- 矩形 -->
  <rect id="myRect" x="50" y="50" width="100" height="80" fill="#4A90D9" />
  <!-- 圆形 -->
  <circle id="myCircle" cx="250" cy="90" r="50" fill="#E67E22" />
  <!-- 椭圆 -->
  <ellipse id="myEllipse" cx="400" cy="90" rx="60" ry="40" fill="#27AE60" />
</svg>

<!-- 控制面板 -->
<div id="controls">
  <h3>图形控制器</h3>
  
  <div class="control-group">
    <label>矩形宽度: <span id="rectWidthLabel">100</span></label>
    <input type="range" id="rectWidth" min="20" max="200" value="100" />
  </div>
  
  <div class="control-group">
    <label>矩形高度: <span id="rectHeightLabel">80</span></label>
    <input type="range" id="rectHeight" min="20" max="150" value="80" />
  </div>
  
  <div class="control-group">
    <label>圆形半径: <span id="circleRadiusLabel">50</span></label>
    <input type="range" id="circleRadius" min="10" max="120" value="50" />
  </div>
  
  <div class="control-group">
    <label>椭圆水平半径: <span id="ellipseRxLabel">60</span></label>
    <input type="range" id="ellipseRx" min="20" max="150" value="60" />
  </div>
  
  <div class="control-group">
    <label>椭圆垂直半径: <span id="ellipseRyLabel">40</span></label>
    <input type="range" id="ellipseRy" min="20" max="120" value="40" />
  </div>
</div>
5.2 JavaScript 交互逻辑
// 获取图形元素
const rect = document.getElementById('myRect');
const circle = document.getElementById('myCircle');
const ellipse = document.getElementById('myEllipse');
const ellipseRx = document.getElementById('ellipseRx');
const ellipseRy = document.getElementById('ellipseRy');

const rectWidthLabel = document.getElementById('rectWidthLabel');
const rectHeightLabel = document.getElementById('rectHeightLabel');
const circleRadiusLabel = document.getElementById('circleRadiusLabel');
const ellipseRxLabel = document.getElementById('ellipseRxLabel');
const ellipseRyLabel = document.getElementById('ellipseRyLabel');

// 矩形宽度控制
rectWidth.addEventListener('input', () => {
  const val = rectWidth.value;
  rect.setAttribute('width', val);
  rectWidthLabel.textContent = val;
});

// 矩形高度控制
rectHeight.addEventListener('input', () => {
  const val = rectHeight.value;
  rect.setAttribute('height', val);
  rectHeightLabel.textContent = val;
});

// 圆形半径控制
circleRadius.addEventListener('input', () => {
  const val = circleRadius.value;
  circle.setAttribute('r', val);
  circleRadiusLabel.textContent = val;
});

// 椭圆水平半径控制
ellipseRx.addEventListener('input', () => {
  const val = ellipseRx.value;
  ellipse.setAttribute('rx', val);
  ellipseRxLabel.textContent = val;
});

// 椭圆垂直半径控制
ellipseRy.addEventListener('input', () => {
  const val = ellipseRy.value;
  ellipse.setAttribute('ry', val);
  ellipseRyLabel.textContent = val;
});

// 额外功能:点击图形时在控制台输出其当前属性
[rect, circle, ellipse].forEach(el => {
  el.addEventListener('click', () => {
    console.log(`点击了 ${el.tagName}`);
    if (el.tagName === 'rect') {
      console.log(`  x: ${el.x.baseVal.value}, y: ${el.y.baseVal.value}`);
      console.log(`  width: ${el.width.baseVal.value}, height: ${el.height.baseVal.value}`);
    } else if (el.tagName === 'circle') {
      console.log(`  cx: ${el.cx.baseVal.value}, cy: ${el.cy.baseVal.value}`);
      console.log(`  r: ${el.r.baseVal.value}`);
    } else if (el.tagName === 'ellipse') {
      console.log(`  cx: ${el.cx.baseVal.value}, cy: ${el.cy.baseVal.value}`);
      console.log(`  rx: ${el.rx.baseVal.value}, ry: ${el.ry.baseVal.value}`);
    }
  });
});
 5.3 运行效果

打开页面后,你会看到三个不同颜色的图形并排排列。拖动下方的滑块,对应的图形会实时响应变化——矩形变宽变高、圆形放大缩小、椭圆在两个方向上分别拉伸。

点击任意图形,控制台会输出该图形的完整属性信息,这正是通过 baseVal.value 读取到的精确值。你可以直观地对比 setAttribute 和 baseVal 两种操作方式产生的效果。

六、常见错误与避坑指南

错误一:直接赋值属性
// 这样写不会生效!
rect.width = 200;
circle.r = 80;

正确做法:使用 setAttribute() 或操作 baseVal.value。

错误二:忘记属性是 SVGAnimatedLength
// 你以为得到了数字,其实得到的是一个对象
const w = rect.width;  // SVGAnimatedLength 对象,不是数字

正确做法:通过 baseVal.value 获取数值。

错误三:混淆 CSS 属性和 SVG 属性

rx、ry、width、height 等属性既可作为 SVG 属性,也可作为 CSS 属性。当两者同时存在时,CSS 属性优先级更高。

/* CSS 中设置 */
rect {
  width: 200px;
  height: 150px;
}

<!-- SVG 属性中设置 -->
<rect width="100" height="80" />

此时,CSS 的 200px 和 150px 会覆盖 SVG 属性中的 100 和 80。如果遇到“改了属性但没变化”的情况,记得检查是否有 CSS 在“捣乱”。

七、本章总结

接口

对应元素

核心属性

独特性

SVGRectElement

<rect>

x,y,width,height,rx,ry

支持圆角

SVGCircleElement

<circle>

cx,cy,r

最简单的图形——只需圆心和半径

SVGEllipseElement

<ellipse>

cx,xy,rx,ry

圆形的一般化——两个方向半径独立

三个核心要点
  1. 所有属性都是只读的 SVGAnimatedLength——修改时用 setAttribute 或操作 baseVal.value。

  2. 三者都继承自 SVGGeometryElement——共享 getTotalLength()、getPointAtLength() 等几何计算能力。

  3. 注意 CSS 与 SVG 属性的优先级——同名 CSS 属性会覆盖 SVG 属性。

下篇预告

在下一篇文章中,我们将迎来 SVG 中最强大、最复杂的图形元素——<path>(路径)。路径是 SVG 的“瑞士军刀”,几乎可以绘制任何形状。我们将深入探讨 SVGPathElement 接口,揭秘 d 属性的底层表示,并学习如何通过 JavaScript 动态拼接路径命令,绘制出属于你自己的贝塞尔曲线。

互动话题:动手运行文中的“图形控制面板”示例,试试把矩形和圆形的颜色也改成可调节的。你还可以为椭圆增加 cx 和 cy 的滑块,让它可以在画布上移动。欢迎在评论区分享你的扩展成果!