logo

G

  • 教程
  • API
  • 示例
  • 插件
  • 所有产品antv logo arrow
  • 6.1.26
  • 画布
    • 简介
    • 初始化参数
    • 场景图能力与生命周期
    • 内置对象
    • 坐标系
    • 画布事件
    • OffscreenCanvas 和服务端渲染
    • CustomElementRegistry
    • 常见问题
  • 渲染器
    • 简介
    • Canvas 渲染器
    • Canvaskit 渲染器
    • SVG 渲染器
    • WebGL 渲染器
    • WebGPU 渲染器
    • 自定义渲染器
  • 相机
    • 简介
    • 相机参数
    • 相机动作
    • 相机动画
  • 事件
    • 简介
    • 事件对象
    • 手势和拖放
    • 常见问题
  • 动画
    • Web Animations API
    • Lottie 动画
  • 基础图形
    • 基础概念
    • DisplayObject
    • Group 图形分组
    • Text 文本
    • Circle 圆形
    • Ellipse 椭圆
    • Rect 矩形
    • Image 图片
    • Line 直线
    • Polygon 多边形
    • Polyline 折线
    • Path 路径
    • HTML 内容
  • 样式系统
    • 简介
    • 继承机制
    • CSS Typed OM
    • CSS Properties & Values API
    • CSS Layout API
    • Pattern
    • Gradient
  • 三维世界
    • 材质
    • 几何
    • 光源
    • Mesh
    • 雾
    • 交互
  • 内置对象
    • EventTarget
    • Node
    • Element
    • Document
    • MutationObserver
    • 工具方法
  • GPGPU
    • 简介
    • 编程模型
    • Kernel API
    • 经典 GPGPU 的实现原理
    • webgpu-graph
  • 声明式用法
    • 使用 Web Components
  • 开发调试工具
    • G 开发者工具
    • 内置的渲染统计信息
    • 第三方开发调试工具

基础概念

上一篇
Lottie 动画
下一篇
DisplayObject

资源

Ant Design
Galacea Effects
Umi-React 应用开发框架
Dumi-组件/文档研发工具
ahooks-React Hooks 库

社区

体验科技专栏
seeconfSEE Conf-蚂蚁体验科技大会

帮助

GitHub
StackOverflow

more products更多产品

Ant DesignAnt Design-企业级 UI 设计语言
yuque语雀-知识创作与分享工具
EggEgg-企业级 Node 开发框架
kitchenKitchen-Sketch 工具集
GalaceanGalacean-互动图形解决方案
xtech蚂蚁体验科技
© Copyright 2025 Ant Group Co., Ltd..备案号:京ICP备15032932号-38

Loading...

首先需要明确一些概念,例如包围盒、坐标、锚点、变换中心等。了解它们有助于更好地使用具体的 API。

层次结构

在场景图中我们了解到可以在图形之间构建父子关系,这种父子关系有时会与我们的直觉相悖,例如给一根直线(Line)添加一个子节点文本(Text):

line.appendChild(text);

但本质上这种层次结构只是定义了一种父子关系,在计算变换时把它考虑进去。例如我们不需要再单独移动直线以及文本,基于这种父子关系,移动直线即可,文本会跟随它移动。在变换过程中,文本相对于直线的位置始终并没有变,即文本在父节点直线的局部坐标系下的坐标没有变。

包围盒

为了简化计算,我们需要用一个规则的几何体包裹住图形,通常使用轴对齐包围盒(Axis Aligned Bounding Box),它是一个非旋转的立方体,下图来自:https://developer.mozilla.org/zh-CN/docs/Games/Techniques/3D_collision_detection#axis-aligned_bounding_boxes%EF%BC%88aabb%E5%8C%85%E5%9B%B4%E7%9B%92%EF%BC%89

我们使用如下定义:

interface AABB {
center: [number, number, number]; // 中心坐标
halfExtents: [number, number, number]; // 长宽高的一半
min: [number, number, number]; // 左上角坐标
max: [number, number, number]; // 右下角坐标
}

在不同情况下,包围盒有不同的含义。我们先看针对单一图形的包围盒代表什么。下图展示了一个半径为 100,边框宽度为 20 的圆,为了更好的说明我们把边框设置成了半透明,同时它还带有阴影效果。

对于用户而言,通常希望使用图形的几何定义,例如这个圆的尺寸就是 100 * 100,我们不希望鼠标滑过阴影区域也判定拾取到这个圆。

而对于渲染管线而言,这些样式属性显然都需要考虑进去,例如:

  • 在脏矩形渲染中正确的擦除绘制区域,一旦不考虑阴影带来的包围盒尺寸增加,就会出现擦除不干净的“残影”
  • 剔除插件也需要考虑,例如一个图形即使只有阴影部分出现在视口中,它也不应被剔除

我们很容易根据不同类型的图形定义几何包围盒:

  • Geometry Bounds。仅由图形的几何定义决定(因此 Group 会返回 null),不考虑绝大部分绘图属性(几何定义必须的除外,例如 Circle 的半径、Rect 的宽高、Path 的路径定义等),也不考虑变换(例如放大缩小并不会改变)。可通过 getGeometryBounds 获取

前面介绍过基于场景图的层次结构,一旦一个图形拥有了子节点,它在计算包围盒时也应当考虑,例如我们想对它做整体旋转时,需要找到这个包围盒的中心作为旋转中心。因此以下包围盒都是会考虑层次结构的:

  • Bounds。在世界坐标系下计算,合并自身以及所有子节点的 Geometry Bounds 得到。用户通常最常用这个包围盒。可通过 getBounds 获取
  • Local Bounds。和 Bounds 的唯一区别是在父节点的局部坐标系下计算。可通过 getLocalBounds 获取
  • Render Bounds。在世界坐标系下计算,在 Bounds 的基础上,受部分绘图属性影响,例如边框宽度,阴影,部分滤镜等,同时合并所有子节点的 Render Bounds。可通过 getRenderBounds 获取。用户通常不关心这个包围盒。

在下图中,ul1 拥有两个字节点 li1 和 li2,在计算自身的 Geometry Bounds 时不会考虑它们,而在计算 Bounds 时需要。由于 ul1 还有阴影,因此它的 Render Bounds 要大一圈:

锚点

一个图形的锚点(原点)应该如何定义呢?我们可以基于 Geometry Bounds 定义,取值范围 [0, 0] ~ [1, 1],其中 [0, 0] 代表 Geometry Bounds 左上角,[1, 1] 代表右下角。而不同图形由于几何定义不同,默认锚点如下:

  • Circle,Ellipse 为圆心位置 [0.5, 0.5]
  • Rect,Image,Line,Polyline,Polygon,Path 为包围盒左上角顶点位置 [0, 0]
  • Text 为文本锚点位置,应该使用 textBaseline 与 textAlign 这两个属性设置,因此设置此属性无效
  • Group 无几何定义,因此锚点始终为 [0, 0],设置此属性也无效

有时我们希望改变一个基础图形的原点定义,例如将 Rect 的原点定义为中心而非左上角,示例:

rect.style.anchor = [0.5, 0.5];

那锚点的改变会影响图形在局部/世界坐标系下的坐标吗?答案是不会。我们只是把图形的原点放在这个坐标下而已,无论原点的定义如何修改,这个“位置”坐标始终不会改变:

rect.getPosition(); // [200, 200]
rect.style.anchor = [0.5, 0.5];
rect.getPosition(); // [200, 200]

变换中心

对图形进行缩放、旋转变换时,需要指定一个变换中心。例如同样是 scale(2),以圆心作为变换中心与圆的 Geometry Bounds 左上角为变换中心,最终得到的效果完全不一样。在 gl-matrix 这样的库中,得到 RTS 变换矩阵通常也需要指定变换中心:

mat4.fromRotationTranslationScaleOrigin();

在某些场景下,用一些字面量或者百分比定义会更方便。例如 CSS 就提供了 transform-origin 属性,它正是相对于 Bounds 进行定义的,下图来自:https://developer.mozilla.org/en-US/docs/Web/CSS/transform-origin:

当我们想实现“绕中心点旋转”时,只需要使用字面量或者百分比,这样就能避免进行 Bounds 的获取:

group.style.transformOrigin = 'center';
group.style.transformOrigin = 'center center';
group.style.transformOrigin = '50% 50%';