PAGX 格式规范
最后更新: 2026 年 5 月 20 日
当前版本: https://pag.io/pagx/1.0/zh/
1. 介绍(Introduction)
PAGX(Portable Animated Graphics XML)是一种基于 XML 的矢量动画标记语言。它提供了统一且强大的矢量图形与动画描述能力,旨在成为跨所有主要工具与运行时的矢量动画交换标准。
1.1 设计目标
开放可读:纯文本 XML 格式,易于阅读和编辑,天然支持版本控制与差异对比,便于调试及 AI 理解与生成。
特性完备:完整覆盖矢量图形、图片、富文本、滤镜效果、混合模式、遮罩等能力,满足复杂动效的描述需求。
精简高效:提供简洁且强大的统一结构,兼顾静态矢量与动画的优化描述,同时预留未来交互和脚本的扩展能力。
生态兼容:可作为 After Effects、Figma、腾讯设计等设计工具的通用交换格式,实现设计资产无缝流转。
高效部署:设计资产可一键导出并部署到研发环境,转换为二进制 PAG 格式后获得极高压缩比和运行时性能。
1.2 文件结构
PAGX 是纯 XML 文件(.pagx),可引用外部资源文件(图片、视频、音频、字体等),也支持通过数据 URI 内嵌资源。PAGX 与二进制 PAG 格式可双向互转:发布时转换为 PAG 以优化加载性能;开发、审查或编辑时可使用 PAGX 格式以便阅读和修改。
1.3 文档组织
本规范按以下顺序组织:
- 基础数据类型:定义文档中使用的基本数据格式
- 文档结构:描述 PAGX 文档的整体组织方式
- 自动布局:定义布局尺寸、容器布局和约束定位机制
- 图层系统:定义图层及其相关功能(样式、滤镜、遮罩)
- 矢量元素系统:定义图层内的矢量元素及其处理模型
- 导入指令:定义嵌入外部内容的导入指令(内联
<svg>和import属性)
附录(方便速查):
- 附录 A:节点层级与包含关系
- 附录 B:枚举类型速查
- 附录 C:常见用法示例
2. 基础数据类型(Basic Data Types)
本节定义 PAGX 文档中使用的基础数据类型和命名规范。
2.1 命名规范
| 类别 | 规范 | 示例 |
|---|---|---|
| 元素名 | PascalCase,不缩写 | Group、Rectangle、Fill |
| 属性名 | camelCase,尽量简短 | antiAlias、blendMode、fontSize |
| 默认单位 | 像素(无需标注) | width="100" |
| 角度单位 | 度 | rotation="45" |
2.2 属性表格约定
本规范中的属性表格统一使用"默认值"列描述属性的必填性:
| 默认值格式 | 含义 |
|---|---|
(必填) |
属性必须指定,没有默认值 |
具体值(如 0、true、normal) |
属性可选,未指定时使用该默认值 |
- |
属性可选,未指定时不生效 |
2.3 通用属性
以下属性可用于任意元素,不在各节点的属性表中重复列出:
| 属性 | 类型 | 说明 |
|---|---|---|
id |
string | 唯一标识符,用于被其他元素引用(如遮罩、颜色源)。在文档中必须唯一,不能为空或包含空白字符 |
data-* |
string | 自定义数据属性,用于存储应用特定的私有数据。* 可替换为任意名称(如 data-name、data-layer-id),运行时忽略这些属性 |
自定义属性说明:
- 属性名必须以
data-开头,后跟至少一个字符 - 属性名只能包含小写字母、数字和连字符(
-),不能以连字符结尾 - 属性值为任意字符串,由创建该属性的应用自行解释
- 运行时不处理这些属性,仅用于工具间传递元数据或存储调试信息
示例:
<Layer data-name="背景图层" data-figma-id="12:34" data-exported-by="PAGExporter">
<Rectangle width="100" height="100"/>
<Fill color="#FF0000"/>
</Layer>
2.4 基本数值类型
| 类型 | 说明 | 示例 |
|---|---|---|
float |
浮点数 | 1.5、-0.5、100 |
int |
整数 | 400、0、-1 |
bool |
布尔值 | true、false |
string |
字符串 | "Arial"、"myLayer" |
enum |
枚举值 | normal、multiply |
idref |
ID 引用 | @gradientId、@maskLayer |
Dimension |
非负浮点数(像素),或其后跟 % 表示相对父容器对应轴(内缩 padding 后)的百分比。 |
100、0.5、50%、100% |
2.5 点(Point)
点使用逗号分隔的两个浮点数表示:
"x,y"
示例:"100,200"、"0.5,0.5"、"-50,100"
2.6 矩形(Rect)
矩形使用逗号分隔的四个浮点数表示:
"x,y,width,height"
示例:"0,0,100,100"、"10,20,200,150"
2.7 变换矩阵(Matrix)
2D 变换矩阵
2D 变换使用 6 个逗号分隔的浮点数表示,对应标准 2D 仿射变换矩阵:
"a,b,c,d,tx,ty"
矩阵形式:
| a c tx |
| b d ty |
| 0 0 1 |
单位矩阵:"1,0,0,1,0,0"
3D 变换矩阵
3D 变换使用 16 个逗号分隔的浮点数表示,列优先顺序:
"m00,m10,m20,m30,m01,m11,m21,m31,m02,m12,m22,m32,m03,m13,m23,m33"
2.8 颜色(Color)
PAGX 支持两种颜色表示方式:
HEX 格式(十六进制)
HEX 格式用于表示 sRGB 色域的颜色,使用 # 前缀的十六进制值:
| 格式 | 示例 | 说明 |
|---|---|---|
#RGB |
#F00 |
3 位简写,每位扩展为两位(等价于 #FF0000) |
#RRGGBB |
#FF0000 |
6 位标准格式,不透明 |
#RRGGBBAA |
#FF000080 |
8 位带 Alpha,Alpha 在末尾(与 CSS 一致) |
浮点数格式
浮点数格式使用 色域(r, g, b) 或 色域(r, g, b, a) 的形式表示颜色,支持 sRGB 和 Display P3 两种色域:
| 色域 | 格式 | 示例 | 说明 |
|---|---|---|---|
| sRGB | srgb(r, g, b) |
srgb(1.0, 0.5, 0.2) |
sRGB 色域,各分量 0.0~1.0 |
| sRGB | srgb(r, g, b, a) |
srgb(1.0, 0.5, 0.2, 0.8) |
带透明度 |
| Display P3 | p3(r, g, b) |
p3(1.0, 0.5, 0.2) |
Display P3 广色域 |
| Display P3 | p3(r, g, b, a) |
p3(1.0, 0.5, 0.2, 0.8) |
带透明度 |
注意:
- 色域标识符(
srgb或p3)和括号不能省略 - 广色域颜色(Display P3)的分量值可以超出 [0, 1] 范围,以表示超出 sRGB 色域的颜色
- sRGB 浮点格式与 HEX 格式表示相同的色域,可根据需要选择
颜色源引用
使用 @resourceId 引用 Resources 中定义的颜色源(渐变、图案等)。
2.9 混合模式(Blend Mode)
混合模式定义源颜色(S)如何与目标颜色(D)组合。
| 值 | 公式 | 说明 |
|---|---|---|
normal |
S | 正常(覆盖) |
multiply |
S × D | 正片叠底 |
screen |
1 - (1-S)(1-D) | 滤色 |
overlay |
multiply/screen 组合 | 叠加 |
darken |
min(S, D) | 变暗 |
lighten |
max(S, D) | 变亮 |
colorDodge |
D / (1-S) | 颜色减淡 |
colorBurn |
1 - (1-D)/S | 颜色加深 |
hardLight |
multiply/screen 反向组合 | 强光 |
softLight |
柔和版叠加 | 柔光 |
difference |
|S - D| | 差值 |
exclusion |
S + D - 2SD | 排除 |
hue |
D 的饱和度和亮度 + S 的色相 | 色相 |
saturation |
D 的色相和亮度 + S 的饱和度 | 饱和度 |
color |
D 的亮度 + S 的色相和饱和度 | 颜色 |
luminosity |
S 的亮度 + D 的色相和饱和度 | 亮度 |
plusLighter |
S + D | 相加(趋向白色) |
plusDarker |
S + D - 1 | 相加减一(趋向黑色) |
2.10 路径数据语法(Path Data Syntax)
路径数据使用 SVG 路径语法,由一系列命令和坐标组成。
路径命令:
| 命令 | 参数 | 说明 |
|---|---|---|
| M/m | x y | 移动到(绝对/相对) |
| L/l | x y | 直线到 |
| H/h | x | 水平线到 |
| V/v | y | 垂直线到 |
| C/c | x1 y1 x2 y2 x y | 三次贝塞尔曲线 |
| S/s | x2 y2 x y | 平滑三次贝塞尔 |
| Q/q | x1 y1 x y | 二次贝塞尔曲线 |
| T/t | x y | 平滑二次贝塞尔 |
| A/a | rx ry rotation large-arc sweep x y | 椭圆弧 |
| Z/z | - | 闭合路径 |
2.11 外部资源引用(External Resource Reference)
外部资源通过相对路径或数据 URI 引用,适用于图片、视频、音频、字体等文件。
<!-- 相对路径引用 -->
<Image source="photo.png"/>
<Image source="assets/icons/logo.png"/>
<!-- 数据 URI 内嵌 -->
<Image source="data:image/png;base64,iVBORw0KGgo..."/>
路径解析规则:
- 相对路径:相对于 PAGX 文件所在目录解析,支持
../引用父目录 - 数据 URI:以
data:开头,格式为data:<mediatype>;base64,<data>,仅支持 base64 编码 - 路径分隔符统一使用
/(正斜杠),不支持\(反斜杠)
3. 文档结构(Document Structure)
本节定义 PAGX 文档的整体结构。
3.1 坐标系统
PAGX 使用标准的 2D 笛卡尔坐标系:
- 原点:位于画布左上角
- X 轴:向右为正方向
- Y 轴:向下为正方向
- 角度:顺时针方向为正(0° 指向 X 轴正方向)
- 单位:所有长度值默认为像素,角度值默认为度
3.2 根元素(pagx)
<pagx> 是 PAGX 文档的根元素,定义画布尺寸并直接包含图层列表。
<?xml version="1.0" encoding="UTF-8"?>
<!-- Demonstrates basic PAGX document structure -->
<pagx width="400" height="400">
<!-- Main content card with modern gradient -->
<Layer name="content">
<Rectangle left="80" top="80" width="240" height="240" roundness="40"/>
<Fill>
<LinearGradient>
<ColorStop color="#6366F1" offset="0"/>
<ColorStop color="#8B5CF6" offset="1"/>
</LinearGradient>
</Fill>
<DropShadowStyle offsetY="16" blurX="48" blurY="48" color="#6366F160"/>
</Layer>
</pagx>
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
width |
float | (必填) | 画布宽度 |
height |
float | (必填) | 画布高度 |
画布裁剪:由 width 和 height 定义的画布作为渲染边界,超出画布区域的内容会被裁剪,不参与渲染。
图层渲染顺序:图层按文档顺序依次渲染,文档中靠前的图层先渲染(位于下方),靠后的图层后渲染(位于上方)。
3.3 资源区(Resources)
<Resources> 定义可复用的资源,包括图片、路径数据、颜色源和合成。资源通过 id 属性标识,在文档其他位置通过 @id 形式引用。
元素位置:Resources 元素可放置在根元素内的任意位置,对位置没有限制。解析器必须支持元素引用在文档后面定义的资源或图层(即前向引用)。
<?xml version="1.0" encoding="UTF-8"?>
<!-- Demonstrates resource definitions and references -->
<pagx width="400" height="400">
<Layer>
<Rectangle left="40" top="40" width="320" height="320" roundness="32"/>
<Fill color="@oceanGradient"/>
<DropShadowStyle offsetY="12" blurX="40" blurY="40" color="#06B6D450"/>
</Layer>
<!-- Shape using solid color resource reference -->
<Layer>
<Ellipse left="140" top="140" width="120" height="120"/>
<Fill color="@coral"/>
</Layer>
<Resources>
<SolidColor id="coral" color="#F43F5E"/>
<LinearGradient id="oceanGradient">
<ColorStop color="#06B6D4" offset="0"/>
<ColorStop color="#3B82F6" offset="1"/>
</LinearGradient>
</Resources>
</pagx>
3.3.1 图片(Image)
图片资源定义可在文档中引用的位图数据。
<Image source="photo.png"/>
<Image source="data:image/png;base64,..."/>
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
source |
string | (必填) | 文件路径或数据 URI |
支持格式:PNG、JPEG、WebP、GIF
3.3.2 路径数据(PathData)
PathData 定义可复用的路径数据,供 Path 元素和 TextPath 修改器引用。
<PathData id="curvePath" data="M 0 0 C 50 0 50 100 100 100"/>
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
data |
string | (必填) | SVG 路径数据 |
3.3.3 颜色源(Color Source)
颜色源定义可用于填充和描边的颜色,支持两种使用方式:
- 共享定义:在
<Resources>中预定义,通过@id引用。适用于被多处引用的颜色源。 - 内联定义:直接嵌套在
<Fill>或<Stroke>元素内部。适用于仅使用一次的颜色源,更简洁。
纯色(SolidColor)
<SolidColor color="#FF0000"/>
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
color |
Color | (必填) | 颜色值 |
线性渐变(LinearGradient)
线性渐变沿起点到终点的方向插值。
<?xml version="1.0" encoding="UTF-8"?>
<!-- Demonstrates linear gradient -->
<pagx width="400" height="400">
<Layer>
<Rectangle left="40" top="40" width="320" height="320" roundness="32"/>
<Fill>
<LinearGradient endPoint="1,1">
<ColorStop color="#6366F1" offset="0"/>
<ColorStop color="#EC4899" offset="1"/>
</LinearGradient>
</Fill>
<DropShadowStyle offsetY="12" blurX="40" blurY="40" color="#8B5CF650"/>
</Layer>
</pagx>
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
startPoint |
Point | 0,0 |
起点 |
endPoint |
Point | 1,0 |
终点 |
matrix |
Matrix | 单位矩阵 | 在所选坐标空间中叠加的变换矩阵,详见颜色源坐标系统 |
fitsToGeometry |
boolean | true | 渐变的坐标空间。true 时为几何元素自身包围盒的归一化 0-1 坐标系;false 时为父级容器(Layer 或 Group)的坐标系。详见颜色源坐标系统 |
计算:对于点 P,其颜色由 P 在起点-终点连线上的投影位置决定。
径向渐变(RadialGradient)
径向渐变从中心向外辐射。
<?xml version="1.0" encoding="UTF-8"?>
<!-- Demonstrates radial gradient -->
<pagx width="400" height="400">
<Layer>
<Ellipse left="40" top="40" width="320" height="320"/>
<Fill>
<RadialGradient center="0.3,0.3" radius="0.8">
<ColorStop color="#FFF" offset="0"/>
<ColorStop color="#06B6D4" offset="0.3"/>
<ColorStop color="#3B82F6" offset="0.7"/>
<ColorStop color="#1E293B" offset="1"/>
</RadialGradient>
</Fill>
<DropShadowStyle offsetY="8" blurX="32" blurY="32" color="#06B6D450"/>
</Layer>
</pagx>
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
center |
Point | 0.5,0.5 |
中心点 |
radius |
float | 0.5 |
渐变半径 |
matrix |
Matrix | 单位矩阵 | 在所选坐标空间中叠加的变换矩阵,详见颜色源坐标系统 |
fitsToGeometry |
boolean | true | 渐变的坐标空间。true 时为几何元素自身包围盒的归一化 0-1 坐标系;false 时为父级容器(Layer 或 Group)的坐标系。详见颜色源坐标系统 |
计算:对于点 P,其颜色由 distance(P, center) / radius 决定。
锥形渐变(ConicGradient)
锥形渐变(也称扫描渐变)沿圆周方向插值。
<?xml version="1.0" encoding="UTF-8"?>
<!-- Demonstrates conic (sweep) gradient -->
<pagx width="400" height="400">
<Layer>
<Ellipse left="40" top="40" width="320" height="320"/>
<Fill>
<ConicGradient>
<ColorStop color="#F43F5E" offset="0"/>
<ColorStop color="#F59E0B" offset="0.2"/>
<ColorStop color="#10B981" offset="0.4"/>
<ColorStop color="#06B6D4" offset="0.6"/>
<ColorStop color="#8B5CF6" offset="0.8"/>
<ColorStop color="#F43F5E" offset="1"/>
</ConicGradient>
</Fill>
<DropShadowStyle offsetY="8" blurX="32" blurY="32" color="#8B5CF650"/>
</Layer>
</pagx>
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
center |
Point | 0.5,0.5 |
中心点 |
startAngle |
float | 0 | 起始角度 |
endAngle |
float | 360 | 结束角度 |
matrix |
Matrix | 单位矩阵 | 在所选坐标空间中叠加的变换矩阵,详见颜色源坐标系统 |
fitsToGeometry |
boolean | true | 渐变的坐标空间。true 时为几何元素自身包围盒的归一化 0-1 坐标系;false 时为父级容器(Layer 或 Group)的坐标系。详见颜色源坐标系统 |
计算:对于点 P,其颜色由 atan2(P.y - center.y, P.x - center.x) 在 [startAngle, endAngle] 范围内的比例决定。
角度约定:遵循全局坐标系约定(见 §3.1):0° 指向右侧(X 轴正方向),角度沿顺时针递增。这与 CSS conic-gradient(0° 指向顶部)不同。常用参考角度:
| 角度 | 方向 |
|---|---|
| 0° | 右 (3 点钟) |
| 90° | 下 (6 点钟) |
| 180° | 左 (9 点钟) |
| 270° | 上 (12 点钟) |
菱形渐变(DiamondGradient)
菱形渐变从中心向四角辐射。
<?xml version="1.0" encoding="UTF-8"?>
<!-- Demonstrates diamond gradient -->
<pagx width="400" height="400">
<Layer>
<Rectangle left="40" top="40" width="320" height="320" roundness="24"/>
<Fill>
<DiamondGradient>
<ColorStop color="#FBBF24" offset="0"/>
<ColorStop color="#F59E0B" offset="0.4"/>
<ColorStop color="#D97706" offset="0.7"/>
<ColorStop color="#1E293B" offset="1"/>
</DiamondGradient>
</Fill>
<DropShadowStyle offsetY="12" blurX="40" blurY="40" color="#F59E0B50"/>
</Layer>
</pagx>
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
center |
Point | 0.5,0.5 |
中心点 |
radius |
float | 0.5 |
渐变半径 |
matrix |
Matrix | 单位矩阵 | 在所选坐标空间中叠加的变换矩阵,详见颜色源坐标系统 |
fitsToGeometry |
boolean | true | 渐变的坐标空间。true 时为几何元素自身包围盒的归一化 0-1 坐标系;false 时为父级容器(Layer 或 Group)的坐标系。详见颜色源坐标系统 |
计算:对于点 P,其颜色由切比雪夫距离 max(|P.x - center.x|, |P.y - center.y|) / radius 决定。
渐变色标(ColorStop)
<ColorStop offset="0.5" color="#FF0000"/>
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
offset |
float | (必填) | 位置 0.0~1.0 |
color |
Color | (必填) | 色标颜色 |
渐变通用规则:
- 色标插值:相邻色标之间使用线性插值
- 色标边界:
offset < 0的色标被视为offset = 0offset > 1的色标被视为offset = 1- 如果没有
offset = 0的色标,使用第一个色标的颜色填充 - 如果没有
offset = 1的色标,使用最后一个色标的颜色填充
图片填充(ImagePattern)
图片图案使用图片作为颜色源。
<ImagePattern image="@img1" tileModeX="repeat" tileModeY="repeat"/>
<ImagePattern image="avatar.png" tileModeX="repeat" tileModeY="repeat"/>
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
image |
string | (必填) | 图片来源:@id 资源引用、文件路径或 data URI |
tileModeX |
TileMode | decal | X 方向平铺模式 |
tileModeY |
TileMode | decal | Y 方向平铺模式 |
filterMode |
FilterMode | linear | 纹理过滤模式 |
mipmapMode |
MipmapMode | linear | Mipmap 模式 |
matrix |
Matrix | 单位矩阵 | 作用于图片本地坐标系的变换矩阵,详见颜色源坐标系统 |
scaleMode |
ScaleMode | letterBox | 将变换后的图片适配到每个几何元素包围盒时使用的缩放规则。为 none 时图片直接放置在父级容器(Layer 或 Group)的坐标系中、不做适配,详见颜色源坐标系统 |
TileMode(平铺模式):clamp(钳制)、repeat(重复)、mirror(镜像)、decal(贴花)
FilterMode(纹理过滤模式):nearest(最近邻)、linear(双线性插值)
MipmapMode(Mipmap 模式):none(禁用)、nearest(最近级别)、linear(线性插值)
ScaleMode(缩放模式):none(不适配)、stretch(拉伸)、letterBox(等比缩放留白)、zoom(等比缩放裁剪)
完整示例:演示不同平铺模式的图片填充
<?xml version="1.0" encoding="UTF-8"?>
<!-- Demonstrates the four ImagePattern scaleMode values. The target rectangles are
non-square (160x120) so the per-geometry fitting modes produce visibly different
results from the square logo image. -->
<pagx width="400" height="400">
<!-- LetterBox (default): preserve aspect ratio, fit fully inside the bounds.
Empty bands appear on the long axis and are transparent (tileMode default = decal). -->
<Layer name="LetterBox">
<Rectangle left="25" top="45" width="160" height="120" roundness="16"/>
<Fill>
<ImagePattern image="@logo"/>
</Fill>
<Stroke color="#FFF" width="2"/>
</Layer>
<!-- Zoom: preserve aspect ratio, cover the bounds. The image is centered and
cropped along the longer axis. -->
<Layer name="Zoom">
<Rectangle left="215" top="45" width="160" height="120" roundness="16"/>
<Fill>
<ImagePattern image="@logo" scaleMode="zoom"/>
</Fill>
<Stroke color="#FFF" width="2"/>
</Layer>
<!-- Stretch: ignore aspect ratio and fill the bounds; the square logo is squashed
horizontally to match the 4:3 rectangle. -->
<Layer name="Stretch">
<Rectangle left="25" top="235" width="160" height="120" roundness="16"/>
<Fill>
<ImagePattern image="@logo" scaleMode="stretch"/>
</Fill>
<Stroke color="#FFF" width="2"/>
</Layer>
<!-- None + repeat: disable per-geometry fitting. The image is placed in the parent
Layer's coordinate space (origin at the Layer's (0,0)) and tiled by tileMode.
The rectangle simply acts as a window onto that tiled fill, so the tiling grid
is aligned to the Layer instead of the rectangle. -->
<Layer name="RepeatTiled">
<Rectangle left="215" top="235" width="160" height="120" roundness="16"/>
<Fill>
<ImagePattern image="@logo" scaleMode="none" tileModeX="repeat" tileModeY="repeat"
matrix="0.16,0,0,0.16,0,0"/>
</Fill>
<Stroke color="#FFF" width="2"/>
</Layer>
<Resources>
<Image id="logo" source="pag_logo.png"/>
</Resources>
</pagx>
颜色源坐标系统
除纯色外,所有颜色源(渐变、图片填充)都通过自己的属性,在两种坐标空间之间二选一:
- 渐变:由
fitsToGeometry控制(默认true)。 - ImagePattern:由
scaleMode控制(默认letterBox);任何非none的取值都启用按几何元素适配,scaleMode="none"切换到绝对坐标。
相对坐标(默认 — 按几何元素适配):
渐变参数(startPoint、endPoint、center、radius 等)位于 (0, 0)-(1, 1) 的归一化空间,映射到每个几何元素自身的包围盒;ImagePattern 图片按 scaleMode 适配每个几何元素的包围盒。填充随几何尺寸自动伸缩,多个不同尺寸的几何元素共用同一颜色源时,每个几何元素都获得自己适配后的填充。
绝对坐标(可选 — fitsToGeometry="false" / scaleMode="none"):
参数位于父级容器(Layer 或 Group)的坐标系中,原点在 (0, 0)。同一容器下的多个几何元素共用一份连续填充;改几何尺寸不会再缩放填充,但对父级容器施加变换会让两者一起移动。
matrix 属性在上述坐标之上叠加:
- 渐变:在所选坐标空间下叠加,对所有渐变参数统一生效。
- ImagePattern:作用于图片本地坐标系(原始图片 rect,原点在左上),先对图片做变换,然后再按
scaleMode放进几何元素。
示例:在 Layer 内的 300×300 矩形上以绝对坐标绘制线性渐变:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Demonstrates fitsToGeometry="false": gradient parameters are in the parent container's
(Layer or Group) coordinate space (origin at the parent Layer's (0,0)). -->
<pagx width="400" height="400">
<Layer>
<Rectangle left="50" top="50" width="300" height="300" roundness="24"/>
<Fill>
<LinearGradient endPoint="300,300" fitsToGeometry="false">
<ColorStop color="#EC4899" offset="0"/>
<ColorStop color="#8B5CF6" offset="0.5"/>
<ColorStop color="#6366F1" offset="1"/>
</LinearGradient>
</Fill>
<DropShadowStyle offsetY="12" blurX="40" blurY="40" color="#EC489950"/>
</Layer>
</pagx>
fitsToGeometry="false"+ 给父 Layer 设scale(2, 2):矩形变为 600×600,渐变随之放大。fitsToGeometry="false"+ 直接把 Rectangle 的width/height改为 600×600:矩形放大,但渐变仍锚定在 Layer,只覆盖左上四分之一。- 默认
fitsToGeometry="true":渐变始终按矩形当前包围盒重新适配。
3.3.4 合成(Composition)
合成用于内容复用(类似 After Effects 的 Pre-comp)。
<?xml version="1.0" encoding="UTF-8"?>
<!-- Demonstrates composition (reusable component) -->
<pagx width="400" height="400">
<Layer width="400" height="400" layout="vertical" padding="60" alignment="center" arrangement="spaceBetween">
<Layer composition="@buttonComp"/>
<Layer composition="@buttonComp"/>
<Layer composition="@buttonComp"/>
</Layer>
<Resources>
<Composition id="buttonComp" width="200" height="60">
<Layer name="button" width="200" height="60">
<Rectangle width="100%" height="100%" roundness="30"/>
<Fill>
<LinearGradient>
<ColorStop color="#6366F1" offset="0"/>
<ColorStop color="#8B5CF6" offset="1"/>
</LinearGradient>
</Fill>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#6366F180"/>
</Layer>
<Layer name="label" width="200" height="60">
<Text centerX="0" centerY="0" text="Button" fontFamily="Arial" fontStyle="Bold" fontSize="22"/>
<Fill color="#FFF"/>
</Layer>
</Composition>
</Resources>
</pagx>
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
width |
float | (必填) | 合成宽度 |
height |
float | (必填) | 合成高度 |
3.3.5 字体(Font)
Font 定义嵌入字体资源,包含子集化的字形数据(矢量轮廓或位图)。PAGX 文件通过嵌入字形数据实现完全自包含,确保跨平台渲染一致性。
<!-- 嵌入矢量字体 -->
<Font id="myFont" unitsPerEm="1000">
<Glyph advance="600" path="M 50 0 L 300 700 L 550 0 Z"/>
<Glyph advance="550" path="M 100 0 L 100 700 L 400 700 C 550 700 550 400 400 400 Z"/>
</Font>
<!-- 嵌入位图字体(Emoji) -->
<Font id="emojiFont" unitsPerEm="136">
<Glyph advance="136" image="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAUA..."/>
<Glyph advance="136" image="emoji/heart.png" offset="0,-5"/>
</Font>
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
unitsPerEm |
int | 1000 | 字体设计空间单位。渲染时按 fontSize / unitsPerEm 缩放 |
一致性约束:同一 Font 内的所有 Glyph 必须使用相同类型(全部 path 或全部 image),不允许混用。
GlyphID 规则:
- GlyphID 从 1 开始:Glyph 列表的索引 + 1 = GlyphID
- GlyphID 0 保留:表示缺失字形,不渲染
字形(Glyph)
Glyph 定义单个字形的渲染数据。path 和 image 二选一必填,不能同时指定。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
advance |
float | (必填) | 水平步进宽度,设计空间坐标(unitsPerEm 单位) |
path |
string | - | SVG 路径数据(矢量轮廓) |
image |
string | - | 图片数据(base64 数据 URI)或外部文件路径 |
offset |
Point | 0,0 | 字形偏移量,设计空间坐标(通常用于位图字形) |
字形类型:
- 矢量字形:指定
path属性,使用 SVG 路径语法描述轮廓 - 位图字形:指定
image属性,用于 Emoji 等彩色字形,可通过offset调整位置
坐标系说明:字形路径、偏移和步进均使用设计空间坐标。渲染时根据 GlyphRun 的 fontSize 和 Font 的 unitsPerEm 计算缩放比例:scale = fontSize / unitsPerEm。
3.4 文档层级结构
PAGX 文档采用层级结构组织内容:
<pagx> ← 根元素(画布尺寸)
├── <Layer>* ← 内容与子图层(递归)
│ ├── VectorElements ← 几何、修改器、绘制器、Group、TextBox
│ ├── LayerStyles / Filters ← 视觉效果
│ └── <Layer>* ← 子图层
└── <Resources> ← 可选的可复用定义
├── Image, PathData, Font ← 资源
├── 颜色源 ← 渐变、SolidColor、ImagePattern
└── <Composition> ← 可复用的图层子树
详细的节点分类和包含规则见附录 A。
4. 自动布局(Auto Layout)
自动布局让元素声明布局意图,由引擎自动计算坐标和尺寸。布局分两层:容器布局控制 Layer 之间的排列,约束定位控制元素相对于容器的定位。两者可以单独使用,也可以组合使用。所有布局计算在解析阶段一次性完成,之后布局属性即被丢弃,运行时只保留计算结果。
4.1 布局尺寸(Layout Size)
width/height 属性声明容器的布局尺寸,是两种布局机制的共同基础——容器布局用它确定子 Layer 的可用空间,约束定位用它作为定位的参考系。Layer 和 Group 都可以拥有布局尺寸。
对于子 Layer,容器布局和约束定位是互斥的——参与容器布局的子 Layer 由容器控制尺寸,不参与的由约束控制:
容器布局中(子 Layer 参与 layout="horizontal"/"vertical"):
- 主轴:有显式
width/height→ 固定(忽略 flex);无显式尺寸且flex=0(默认)→ 内容测量;无显式尺寸且flex>0→ 按 flex 权重按比例分配剩余空间 - 交叉轴:
alignment="stretch"为未设交叉轴尺寸的子元素拉伸填满(已设交叉轴尺寸的保持不变)
约束定位中(无容器布局或子 Layer 设 includeInLayout="false"):
对边约束:对边同设约束(
left+right或top+bottom)从父容器推导尺寸(如width = 父.width - left - right),始终覆盖显式尺寸显式
width/height(包括百分比值):直接设置width/height属性。百分比值(如50%)从父容器内缩 padding 后的布局尺寸推导(如width = (父.width - 父.padding.left - 父.padding.right) × 0.5)。如果父容器对应轴仍在解析中(内容测量型父容器的第一轮),百分比值此时无法求解,子元素在该轴上回退到自身的首选尺寸。内容测量:当以上都未设置时,引擎自动从内容边界计算尺寸——容器会被内容撑大。
对 Group 和 Layer,测量值从本地原点 (0,0) 到所有内部元素内容边界(含约束贡献的边距)的右下角坐标,确保测量结果不受内部元素排放位置的影响。对 Text,测量值为行盒边界(字形推进宽度之和 × 字体指标行高)。
这种内容驱动尺寸的机制支持自适应布局模式,例如自动宽度按钮(一个带
layout="horizontal"+padding的 Layer 包裹 Text 元素,Layer 自动根据文本内容加内边距计算尺寸)。
这些来源确保容器在布局计算时几乎总有尺寸可用。显式设置 width/height 只在需要特定设计尺寸(不等于内容自然尺寸)时才必要。
布局尺寸本身不产生直接的渲染效果,仅向下传递尺寸信息供子元素的约束定位使用。布局尺寸和约束计算在元素的本地坐标系中完成。变换(matrix/matrix3D)在布局计算结果之上叠加——不参与布局计算,也不影响兄弟节点或父节点的位置。
4.2 容器布局(Container Layout)
将父 Layer 的 layout 属性设为 horizontal 或 vertical 后,所有子 Layer 沿指定轴自动排列。语义上是 CSS Flexbox 的子集。
<!-- 三个等宽卡片水平排列 -->
<Layer width="920" height="200" layout="horizontal" gap="14" padding="20">
<Layer flex="1"><!-- 卡片 A --></Layer>
<Layer flex="1"><!-- 卡片 B --></Layer>
<Layer flex="1"><!-- 卡片 C --></Layer>
</Layer>
三个子 Layer 均未设 width 且 flex="1",等分可用宽度:(920 - 40 - 28) / 3 = 284。
属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
layout |
LayoutMode | none | 子图层容器布局模式 |
gap |
float | 0 | 相邻子 Layer 之间的间距 |
flex |
float | 0 | 主轴弹性权重。子元素无显式主轴尺寸时:flex=0(默认)使用内容测量尺寸;flex>0 按权重按比例分配剩余空间。设置了显式 width/height 时忽略此属性 |
padding |
float 或 "t,r,b,l" | 0 | 内缩布局内容区域和所有内容(VectorElements 和子 Layers)的约束参考系。支持单值(四边均匀)、两值(垂直,水平)、四值(上,右,下,左),与 CSS shorthand 一致 |
alignment |
Alignment | stretch | 交叉轴对齐方式 |
arrangement |
Arrangement | start | 主轴排布方式 |
includeInLayout |
bool | true | 是否参与父容器的布局排列,设为 false 时脱离布局流 |
LayoutMode:
| 值 | 说明 |
|---|---|
none |
无容器布局,子图层使用约束属性(left/top)定位(默认值) |
horizontal |
主轴水平方向,交叉轴垂直方向 |
vertical |
主轴垂直方向,交叉轴水平方向 |
Alignment(交叉轴对齐):
| 值 | 说明 |
|---|---|
start |
交叉轴起始端对齐 |
center |
交叉轴居中对齐 |
end |
交叉轴末尾端对齐 |
stretch |
拉伸填满交叉轴可用空间,即容器交叉轴尺寸减去两侧 padding(仅对未设交叉轴尺寸的子 Layer 生效) |
Arrangement(主轴排布):
| 值 | 说明 |
|---|---|
start |
主轴起始端排布 |
center |
主轴居中排布 |
end |
主轴末尾端排布 |
spaceBetween |
子元素之间均匀分布,首尾贴边 |
spaceEvenly |
所有间距完全相等,包括首尾两端 |
spaceAround |
每个子元素两侧等间距,首尾间距为元素间距的一半 |
当 arrangement 为 spaceBetween、spaceEvenly 或 spaceAround 时,gap 仍会减少弹性子元素的可分配空间,但实际子元素间距完全由 arrangement 公式决定——gap 被替换而非叠加。同时设置两者可能产生不直观的结果,建议只使用其中之一。
子元素尺寸
子 Layer 的主轴尺寸有三种状态:
- 固定尺寸:主轴方向设了
width/height,不随父容器变化。flex属性被忽略。 - 内容测量尺寸:主轴方向未设显式
width/height且flex=0(默认)。尺寸由子元素自身内容 bounds 决定。 - 弹性尺寸:主轴方向未设显式
width/height且flex>0。当父容器有确定的主轴尺寸时,剩余空间(减去固定和内容测量子元素及间距后)按flex权重在弹性子元素之间按比例分配。当父容器主轴尺寸由内容测量决定时,弹性子元素退化为内容测量尺寸,父容器收缩到恰好容纳所有子元素。
交叉轴尺寸:有显式 width/height 则使用;当父容器 alignment="stretch" 时,未设交叉轴尺寸的子 Layer 拉伸填满交叉轴可用空间(容器交叉轴尺寸减去两侧 padding);否则由内容 bounds 决定。
内容区域为 width × height 减去各边 padding。
背景填充与 Padding
由于 padding 会内缩所有内容的约束参考系,在有 padding 的 Layer 中使用 width="100%" height="100%" 的背景 Rectangle 只会填满内缩后的区域。如果背景需要铺满整个 Layer,应使用双层结构:外层放置背景,内层承载 padding:
<Layer width="300" height="200">
<Rectangle width="100%" height="100%" roundness="12"/>
<Fill color="#FFF"/>
<Layer width="100%" height="100%" layout="horizontal" gap="8" padding="16">
<!-- 内容 -->
</Layer>
</Layer>
内层容器的选择:需要容器布局(layout、gap、alignment、arrangement)时使用 Layer;仅需 padding 内缩 VectorElements 时使用更轻量的 Group。
对内容测量的容器(未显式设置 width/height),padding 会加到测量值上,例如一个包裹 100×50 Rectangle 且 padding="20" 的 Group 测量尺寸为 140×90。
gap、alignment 和 arrangement 仍然需要 layout 才能生效。
visible="false" 的子 Layer 不会渲染,但如果 includeInLayout 为 true(默认值),仍然参与布局计算。若需将不可见的子 Layer 排除出布局流,需显式设置 includeInLayout="false"。
脱离布局流
设了容器布局(layout="horizontal" 或 "vertical")的 Layer,默认所有子 Layer 的位置由布局引擎决定。设置 includeInLayout="false" 的子 Layer 脱离布局流——不参与排列、不占用空间、不影响容器测量,但仍然可见,可使用约束属性(left/top)定位。适用于在布局容器内放置装饰层、背景层或角标等不应参与排列的元素。脱离布局流的子 Layer 始终可以使用约束属性(left/right/top/bottom/centerX/centerY),不受父容器 layout 模式的限制。
<Layer width="400" height="300" layout="vertical" gap="12">
<Layer><!-- 参与布局 --></Layer>
<Layer><!-- 参与布局 --></Layer>
<!-- 角标:脱离布局流,使用 left/top 约束定位 -->
<Layer left="370" top="10" includeInLayout="false">
<Ellipse width="24" height="24"/>
<Fill color="#EF4444"/>
</Layer>
</Layer>
示例
<?xml version="1.0" encoding="UTF-8"?>
<!-- Demonstrates horizontal and vertical container layouts -->
<pagx width="400" height="400">
<!-- Horizontal layout boundary -->
<Layer left="20" top="20" width="360" height="170">
<Layer composition="@boundary"/>
</Layer>
<!-- Horizontal layout: three equal-width cards -->
<Layer left="20" top="20" width="360" height="170" layout="horizontal" gap="16" padding="20">
<Layer flex="1">
<Rectangle width="96" height="130" roundness="12"/>
<Fill color="#6366F1"/>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#6366F150"/>
</Layer>
<Layer flex="1">
<Rectangle width="96" height="130" roundness="12"/>
<Fill color="#F43F5E"/>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#F43F5E50"/>
</Layer>
<Layer flex="1">
<Rectangle width="96" height="130" roundness="12"/>
<Fill color="#06B6D4"/>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#06B6D450"/>
</Layer>
</Layer>
<!-- Vertical layout boundary -->
<Layer left="20" top="210" width="360" height="170">
<Layer composition="@boundary"/>
</Layer>
<!-- Vertical layout: three equal-height bars -->
<Layer left="20" top="210" width="360" height="170" layout="vertical" gap="12" padding="20">
<Layer flex="1">
<Rectangle top="0.5" width="320" height="35" roundness="12"/>
<Fill color="#10B981"/>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#10B98150"/>
</Layer>
<Layer flex="1">
<Rectangle top="0.5" width="320" height="35" roundness="12"/>
<Fill color="#F59E0B"/>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#F59E0B50"/>
</Layer>
<Layer flex="1">
<Rectangle top="0.5" width="320" height="35" roundness="12"/>
<Fill color="#3B82F6"/>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#3B82F650"/>
</Layer>
</Layer>
<Resources>
<Composition id="boundary" width="360" height="170">
<Layer width="100%" height="100%">
<Rectangle width="100%" height="100%" roundness="8"/>
<Stroke color="#CBD5E1" width="1" dashes="4,3"/>
</Layer>
</Composition>
</Resources>
</pagx>
<?xml version="1.0" encoding="UTF-8"?>
<!-- Demonstrates includeInLayout: badge overlay leaves the layout flow -->
<pagx width="400" height="400">
<!-- Vertical card list with a notification badge that leaves the layout flow -->
<Layer left="20" top="20" width="360" height="360" layout="vertical" gap="16" padding="20">
<!-- Card 1 -->
<Layer flex="1">
<Rectangle width="100%" height="100%" roundness="16"/>
<Fill color="#6366F1"/>
<Group centerX="0" centerY="0">
<Text text="Design Review" fontFamily="Arial" fontStyle="Bold" fontSize="16"/>
<Fill color="#FFF"/>
</Group>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#6366F150"/>
</Layer>
<!-- Card 2 -->
<Layer flex="1">
<Rectangle width="100%" height="100%" roundness="16"/>
<Fill color="#10B981"/>
<Group centerX="0" centerY="0">
<Text text="Sprint Planning" fontFamily="Arial" fontStyle="Bold" fontSize="16"/>
<Fill color="#FFF"/>
</Group>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#10B98150"/>
</Layer>
<!-- Card 3 -->
<Layer flex="1">
<Rectangle width="100%" height="100%" roundness="16"/>
<Fill color="#F59E0B"/>
<Group centerX="0" centerY="0">
<Text text="Bug Report #42" fontFamily="Arial" fontStyle="Bold" fontSize="16"/>
<Fill color="#FFF"/>
</Group>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#F59E0B50"/>
</Layer>
<!-- Notification badge: a Layer leaves the layout flow -->
<Layer right="-16" top="-16" width="32" height="32" includeInLayout="false">
<Ellipse width="100%" height="100%"/>
<Fill color="#EF4444"/>
<Group centerX="0" centerY="0">
<Text text="3" fontFamily="Arial" fontStyle="Bold" fontSize="16"/>
<Fill color="#FFF"/>
</Group>
<DropShadowStyle offsetY="4" blurX="12" blurY="12" color="#EF444460"/>
</Layer>
</Layer>
</pagx>
4.3 约束定位(Constraint Positioning)
约束定位让元素声明与所属容器的位置关系,引擎自动计算坐标。约束定位是所有节点的基础能力,不是一种布局模式。支持约束属性的元素包括:
- 图层内容节点:几何元素(Rectangle、Ellipse、Polystar、Path)、Text、TextBox、Group 和 TextPath——约束属性始终生效
- 子 Layer:当父 Layer 无容器布局(默认),或子 Layer 设了
includeInLayout="false"时
<!-- Rectangle 铺满容器并留出 10px 边距 -->
<Layer width="400" height="200">
<Rectangle left="10" right="10" top="10" bottom="10"/>
<Fill color="#3B82F6"/>
</Layer>
属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
left |
float | - | 左边缘到容器左边缘的距离 |
right |
float | - | 右边缘到容器右边缘的距离 |
top |
float | - | 上边缘到容器上边缘的距离 |
bottom |
float | - | 下边缘到容器下边缘的距离 |
centerX |
float | - | 相对容器水平中心的偏移(0 = 水平居中) |
centerY |
float | - | 相对容器垂直中心的偏移(0 = 垂直居中) |
width |
Dimension | - | 布局宽度;支持像素(如 100)或相对容器的百分比(如 50%) |
height |
Dimension | - | 布局高度;支持像素(如 100)或相对容器的百分比(如 50%) |
组合规则:每个轴上只能使用以下组合之一:单边定位(left、right、centerX 任选其一),或对边约束(left + right)。垂直轴同理(top / bottom / centerY,或 top + bottom)。如果同一轴上设置了多种组合,引擎按以下优先级解决冲突:centerX > left+right > left > right(垂直轴同理:centerY > top+bottom > top > bottom)。低优先级的约束被静默忽略。
生效条件:约束属性参照元素的直接父容器(Layer 或 Group)的布局尺寸,逐层向下传递——每层容器先确定自身尺寸,其子元素的约束再以该尺寸为参考系。当父容器有 padding 时,参考系按 padding 量内缩。由于引擎会自动测量容器尺寸(见 §4.1),约束通常都能生效。不同约束对容器尺寸的依赖程度不同:left/top 单独使用时,定位公式不涉及容器尺寸(如 tx = left - bounds.x),在任何情况下都能正确工作;right/bottom/centerX/centerY 以及对边约束需要引用容器尺寸来计算位置。
Content Bounds:约束中的「边缘」指元素的 content bounds 边缘。不同元素类型的起点不同:
- 框对齐节点(Rectangle、Ellipse、Polystar、TextBox、Group、Layer):content bounds 为本地坐标系中 [0, width] × [0, height] 的逻辑框。对 Polystar,框为 [0, outerRadius×2] × [0, outerRadius×2]。
left="0"将框的左边缘对齐到容器左边缘。 - 像素对齐节点(Path、Text、TextPath):content bounds 为实际渲染像素边界。
left="0"将内容平移使渲染像素紧贴容器边缘。对 Text,content bounds 为行盒边界(字形推进宽度之和 × 字体指标行高),提供不依赖于具体字形形状的稳定测量。
两种方式确保了 left="0" 对所有元素的语义一致:「内容紧贴容器边缘」。对框对齐节点指逻辑框,对像素对齐节点指渲染像素。
position 属性
所有图层内容节点拥有 position 属性,表示元素锚点在父坐标系中的绝对坐标。约束定位的计算结果写入 position。各元素的锚点位置不同:
| 元素 | 锚点位置 | 说明 |
|---|---|---|
| Rectangle、Ellipse | 几何中心 | 默认为 (size.width/2, size.height/2),使左上角对齐原点 |
| Polystar | 几何中心 | 默认为 (-bounds.x, -bounds.y),使像素左上角对齐原点 |
| Path、TextPath | 坐标系原点 | position="0,0" 时路径数据坐标直接作为绘制坐标 |
| Text | 由 textAnchor 决定 |
start:基线起点;center:水平中点;end:末尾 |
| Group、TextBox | 左上角 | position="0,0" 时内容从原点开始 |
单边与居中约束
单边约束将元素的对应边缘定位到距容器边缘指定距离处;居中约束将元素中心定位到容器中心加偏移量处。都不改变元素尺寸。
left=L: tx = L - B.left
right=R: tx = (W - R) - B.right
centerX=C: tx = (W/2 + C) - B.centerX
其中 B 为元素的 content bounds,W/H 为容器布局尺寸。垂直轴(top/bottom/centerY)计算方式相同。centerX="0" 水平居中,centerX="20" 居中后偏右 20px。
<?xml version="1.0" encoding="UTF-8"?>
<!-- Demonstrates all single-edge and center constraint combinations.
A 3x3 grid of colored squares, each using a different constraint pair:
Row 1: left+top, centerX+top, right+top
Row 2: left+centerY, centerX+centerY, right+centerY
Row 3: left+bottom, centerX+bottom, right+bottom
Two child Layer overlays show Layer-level constraint positioning. -->
<pagx width="400" height="400">
<Layer left="20" top="20" width="360" height="360">
<!-- Dashed container border -->
<Rectangle width="100%" height="100%" roundness="8"/>
<Stroke color="#CBD5E1" width="1" dashes="4,3"/>
<!-- Row 1 -->
<Group width="100%" height="100%">
<Rectangle left="20" top="20" width="72" height="72" roundness="10"/>
<Fill color="#6366F1"/>
</Group>
<Group width="100%" height="100%">
<Rectangle top="20" centerX="0" width="72" height="72" roundness="10"/>
<Fill color="#818CF8"/>
</Group>
<Group width="100%" height="100%">
<Rectangle right="20" top="20" width="72" height="72" roundness="10"/>
<Fill color="#F43F5E"/>
</Group>
<!-- Row 2 -->
<Group width="100%" height="100%">
<Rectangle left="20" centerY="0" width="72" height="72" roundness="10"/>
<Fill color="#8B5CF6"/>
</Group>
<Group width="100%" height="100%">
<Rectangle centerX="0" centerY="0" width="72" height="72" roundness="10"/>
<Fill color="#10B981"/>
</Group>
<Group width="100%" height="100%">
<Rectangle right="20" centerY="0" width="72" height="72" roundness="10"/>
<Fill color="#EC4899"/>
</Group>
<!-- Row 3 -->
<Group width="100%" height="100%">
<Rectangle left="20" bottom="20" width="72" height="72" roundness="10"/>
<Fill color="#F59E0B"/>
</Group>
<Group width="100%" height="100%">
<Rectangle bottom="20" centerX="0" width="72" height="72" roundness="10"/>
<Fill color="#F97316"/>
</Group>
<Group width="100%" height="100%">
<Rectangle right="20" bottom="20" width="72" height="72" roundness="10"/>
<Fill color="#06B6D4"/>
</Group>
<!-- Child Layer: left+right derives width, centered vertically -->
<Layer left="20" right="20" centerY="-62" height="16">
<Rectangle width="100%" height="100%" roundness="3"/>
<Fill color="#6366F118"/>
<Stroke color="#6366F1" width="1" dashes="4,3"/>
</Layer>
<!-- Child Layer: centerX + centerY with offset -->
<Layer centerX="0" centerY="62" width="120" height="16">
<Rectangle width="100%" height="100%" roundness="3"/>
<Fill color="#F43F5E18"/>
<Stroke color="#F43F5E" width="1" dashes="4,3"/>
</Layer>
</Layer>
</pagx>
对边约束与百分比尺寸
对边约束与百分比尺寸都是从父容器推导目标尺寸:
- 对边约束(
left+right或top+bottom):target = 容器尺寸 - L - R - 百分比尺寸(
width="N%"/height="N%"):target = 容器内缩尺寸 × N / 100,其中「容器内缩尺寸」为父容器该轴布局尺寸减去padding
不同节点类型对推导出的目标尺寸响应方式不同:
| 元素 | 行为 | 说明 |
|---|---|---|
| Rectangle、Ellipse | 拉伸形状 | 修改 size 填满目标区域,改变渲染形状 |
| TextBox | 拉伸排版区域 | 修改 width 和 height 填满目标区域,改变文字排版范围 |
| Group、Layer | 推导布局尺寸 | 将布局宽/高设为目标尺寸,内部子元素按新尺寸重新布局,不影响渲染。子 Layer 的情况下,该推导覆盖显式 width/height |
| Polystar、Path、Text、TextPath | 等比缩放适配 | 单轴设置时紧贴该轴边界等比缩放;双轴设置时取两轴中较小的缩放因子(fit 模式),在较长轴方向居中。Polystar 使用其外框尺寸(outerRadius×2 × outerRadius×2)参与缩放计算 |
同一轴上同时设置百分比尺寸与对边约束时,对边约束优先(见 §4.1 第 1 步)。仅在单轴上设置百分比尺寸时,另一轴回退到首选尺寸,除非另有约束指定其尺寸。
拉伸(Rectangle、Ellipse、TextBox):
left=L, right=R: 新 width = W - L - R
Rectangle/Ellipse: position.x = L + width/2(锚点在几何中心)
TextBox: position.x = L(锚点在左上角)
垂直轴同理。
推导布局尺寸(Group):
left=L, right=R: position.x = L, width = W - L - R
垂直轴同理。
等比缩放适配(Polystar、Path、Text、TextPath):
1. 计算缩放因子(B 为缩放前的 Content Bounds):
仅水平对边约束 (left=L, right=R):
scale = (W - L - R) / B.width
元素宽度恰好铺满目标区域,高度等比跟随缩放。
仅垂直对边约束 (top=T, bottom=B_):
scale = (H - T - B_) / B.height
元素高度恰好铺满目标区域,宽度等比跟随缩放。
双轴对边约束 (left=L, right=R, top=T, bottom=B_):
scale = min((W - L - R) / B.width, (H - T - B_) / B.height)
fit 模式:取较小的缩放因子,保证两个轴都不超出目标区域。
2. 缩放元素: Path 改写路径数据,Polystar 改写参数值,Text 改写 fontSize
3. 定位(B' 为缩放后的 Content Bounds):
有 left+right:
tx = L + (W - L - R - B'.width) / 2 - B'.left
单轴时 B'.width 恰好等于 W-L-R,居中偏移为零;
双轴 fit 时若水平轴为较长轴,则产生居中偏移。
有 top+bottom:
ty = T + (H - T - B_ - B'.height) / 2 - B'.top
同理。
无对边约束的轴按常规单边或居中约束定位。
拉伸:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Demonstrates opposite-edge constraints: stretchable elements fill target areas -->
<pagx width="400" height="400">
<Layer left="20" top="20" width="360" height="360">
<!-- Dashed container border -->
<Rectangle width="100%" height="100%" roundness="8"/>
<Stroke color="#CBD5E1" width="1" dashes="4,3"/>
<!-- Rectangle stretches to fill with 20px margins -->
<Layer width="100%" height="100%">
<Rectangle left="20" right="20" top="20" bottom="20" roundness="20"/>
<Fill color="#6366F1"/>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#6366F150"/>
</Layer>
<!-- Ellipse stretches horizontally and vertically with larger margins -->
<Layer width="100%" height="100%">
<Ellipse left="40" right="40" top="120" bottom="120"/>
<Fill color="#F43F5E"/>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#F43F5E50"/>
</Layer>
<!-- Small centered white rectangle as accent -->
<Layer width="100%" height="100%">
<Rectangle centerX="0" centerY="0" width="60" height="60" roundness="10"/>
<Fill color="#FFF"/>
<DropShadowStyle offsetY="4" blurX="16" blurY="16" color="#00000020"/>
</Layer>
</Layer>
</pagx>
TextBox 与 Group:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Demonstrates TextBox stretch and Group constraint-derived layout -->
<pagx width="400" height="400">
<!-- Upper card: TextBox stretches typesetting area -->
<Layer left="20" top="20" width="360" height="170">
<Rectangle width="100%" height="100%" roundness="16"/>
<Fill color="#6366F1"/>
<TextBox left="24" right="24" top="24" bottom="24" textAlign="center" paragraphAlign="middle">
<Text text="TextBox stretches its typesetting area using constraint attributes" fontFamily="Arial" fontStyle="Bold" fontSize="18"/>
<Fill color="#FFF"/>
</TextBox>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#6366F150"/>
</Layer>
<!-- Lower card: content directly in Layer -->
<Layer left="20" top="210" width="360" height="170">
<Rectangle width="100%" height="100%" roundness="16"/>
<Fill color="#10B981"/>
<TextBox centerX="0" centerY="0">
<Text text="Group Layout" fontFamily="Arial" fontStyle="Bold" fontSize="24"/>
<Fill color="#FFF"/>
</TextBox>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#00000050"/>
</Layer>
</pagx>
等比缩放适配:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Demonstrates Polystar centered over stretched Rectangle using constraints -->
<pagx width="400" height="400">
<Layer left="20" top="20" width="360" height="360">
<!-- Dashed container border -->
<Rectangle width="100%" height="100%" roundness="8"/>
<Stroke color="#CBD5E1" width="1" dashes="4,3"/>
<!-- Rectangle stretches to fill with margins -->
<Layer width="100%" height="100%">
<Rectangle left="20" right="20" top="20" bottom="20" roundness="20"/>
<Fill color="#6366F1"/>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#6366F150"/>
</Layer>
<!-- Polystar centered using centerX and centerY constraints -->
<Layer width="100%" height="100%">
<Polystar centerX="0" centerY="0" innerRadius="36"/>
<Fill color="#FBBF24"/>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#F59E0B60"/>
</Layer>
</Layer>
</pagx>
子 Layer 约束定位
子 Layer 的约束属性覆盖其 x/y。生效条件:
- 父 Layer 无容器布局:
layout为none(默认值),或 - 子 Layer 脱离布局流:
includeInLayout="false"
<Layer width="400" height="300">
<!-- 对边约束推导宽度:width = 400 - 20 - 20 = 360 -->
<Layer left="20" right="20" top="50">
<!-- ... -->
</Layer>
<!-- 居中定位 -->
<Layer centerX="0" bottom="20" width="100" height="40">
<!-- ... -->
</Layer>
</Layer>
4.4 与动画的关系
所有布局相关属性(width、height、layout、gap、padding、alignment、arrangement,以及约束属性 left、right、top、bottom、centerX、centerY)在运行时不存在。布局引擎在首次渲染前一次性计算,将结果写入各元素的绝对坐标(position、size),随后布局属性即被丢弃。
因此,动画无法修改布局属性,而是基于布局计算后的结果叠加视觉变化。例如 transform、alpha 等渲染属性可以设置关键帧动画,它们作用于布局结果之上,不会触发重新布局。
布局引擎在计算完成后,将所有 Layer 的位置(x、y)四舍五入到最近的整数像素,内容测量的尺寸向上取整到整数像素,避免亚像素渲染带来的模糊和约束边界处的内容裁剪。
5. 图层系统(Layer System)
图层(Layer)是 PAGX 内容组织的基本单元。每个 Layer 承担两种角色:作为容器,它持有 VectorElement(几何元素、修改器、绘制器)和子 Layer,构成层级结构;作为效果载体,样式、滤镜、混合模式和遮罩均在 Layer 级别生效。有关布局机制(尺寸、定位和排列),见 §4。
5.1 核心概念
本节介绍图层渲染流程的核心概念。图层内容、图层轮廓、图层背景这三个概念贯穿整个渲染流程,是理解图层样式、滤镜和遮罩如何计算效果的基础。
图层渲染流程
图层绑定的绘制器(Fill、Stroke 等)通过 placement 属性分为下层内容和上层内容,默认为下层内容。单个图层渲染时按以下顺序处理:
- 图层样式(下方):渲染位于内容下方的图层样式(如投影阴影)
- 下层内容:渲染
placement="background"的 Fill 和 Stroke - 子图层:按文档顺序递归渲染所有子图层
- 图层样式(上方):渲染位于内容上方的图层样式(如内阴影)
- 上层内容:渲染
placement="foreground"的 Fill 和 Stroke(绘制在子图层之上) - 图层滤镜:将前面步骤的整体输出作为滤镜链的输入,依次应用所有滤镜
图层内容(Layer Content)
图层内容是指图层的下层内容、子图层和上层内容的完整渲染结果(渲染流程中的步骤 2、3 和 5),不包含图层样式和图层滤镜。
图层样式基于图层内容计算效果。例如,当填充位于下层内容、描边位于上层内容时,描边会绘制在子图层之上,但投影阴影仍然基于包含填充、子图层和描边的完整图层内容计算。
图层轮廓(Layer Contour)
图层轮廓是基于图层内容生成的一个二值(不透明或完全透明)遮罩。与普通图层内容相比,图层轮廓有以下区别:
- 包含 alpha=0 的填充:填充透明度完全为 0 的几何形状也会加入轮廓计算
- 纯色填充和渐变填充:原始颜色替换为不透明白色
- 图片填充:完全透明的像素保留透明;其余像素均转为完全不透明
注意:没有绘制器的几何元素(独立的 Rectangle、Ellipse 等)不参与轮廓计算。
图层背景(Layer Background)
图层背景是指当前图层下方所有已渲染内容的合成结果,包括:
- 当前图层下方的所有兄弟图层及其子树的渲染结果
- 当前图层已绘制的下方图层样式(不包括 BackgroundBlurStyle 本身)
图层背景主要用于:
- 图层样式:部分图层样式需要背景作为其中一个输入源
- 混合模式:部分混合模式需要背景信息才能正确渲染
背景透传:通过 passThroughBackground 属性控制是否允许图层背景透传给子图层。当设置为 false 时,子图层的背景依赖样式将无法获取到正确的图层背景。以下情况会自动禁用背景透传:
- 图层使用了非
normal的混合模式 - 图层应用了滤镜
- 图层使用了 3D 变换或投影变换
5.2 图层(Layer)
<Layer> 是内容和子图层的基本容器。
<?xml version="1.0" encoding="UTF-8"?>
<!-- Demonstrates layer properties and nesting -->
<pagx width="400" height="400">
<!-- Parent layer with nested child -->
<Layer name="Parent" left="50" top="50">
<Rectangle left="20" top="20" width="260" height="260" roundness="32"/>
<Fill>
<LinearGradient>
<ColorStop color="#F43F5E" offset="0"/>
<ColorStop color="#EC4899" offset="1"/>
</LinearGradient>
</Fill>
<Layer name="Child">
<Ellipse left="90" top="90" width="120" height="120"/>
<Fill color="#FFF"/>
<DropShadowStyle offsetY="4" blurX="16" blurY="16" color="#00000030"/>
</Layer>
<DropShadowStyle offsetY="16" blurX="40" blurY="40" color="#F43F5E60"/>
</Layer>
</pagx>
子元素
Layer 的子元素按类型自动归类为四个集合:
| 子元素类型 | 归类 | 说明 |
|---|---|---|
| VectorElement | contents | 几何元素、修改器、绘制器(参与累积处理) |
| LayerStyle | styles | DropShadowStyle、InnerShadowStyle、BackgroundBlurStyle |
| LayerFilter | filters | BlurFilter、DropShadowFilter 等滤镜 |
| Layer | children | 嵌套子图层 |
建议顺序:虽然子元素顺序不影响解析结果,但建议按 VectorElement → LayerStyle → LayerFilter → 子Layer 的顺序书写,以提高可读性。
图层属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
name |
string | "" | 显示名称 |
visible |
bool | true | 是否可见 |
alpha |
float | 1 | 透明度 0~1 |
blendMode |
BlendMode | normal | 混合模式 |
x |
float | 0 | X 位置(推荐使用约束属性 left) |
y |
float | 0 | Y 位置(推荐使用约束属性 top) |
matrix |
Matrix | 单位矩阵 | 2D 变换 "a,b,c,d,tx,ty" |
matrix3D |
Matrix | - | 3D 变换(16 个值,列优先) |
preserve3D |
bool | false | 保持 3D 变换 |
antiAlias |
bool | true | 边缘抗锯齿 |
groupOpacity |
bool | true | 组透明度 |
passThroughBackground |
bool | true | 是否允许背景透传给子图层 |
scrollRect |
Rect | - | 滚动裁剪区域 "x,y,w,h" |
clipToBounds |
bool | false | 将内容裁剪到图层边界(见 §5.5.2) |
mask |
idref | - | 遮罩图层引用 "@id" |
maskType |
MaskType | alpha | 遮罩类型 |
composition |
idref | - | 合成引用 "@id" |
布局和约束属性:width、height、layout、gap、padding、alignment、arrangement、flex、includeInLayout、left、right、top、bottom、centerX、centerY —— 定义、默认值和用法见 §4。
groupOpacity:当值为 true(默认)时,所有图层内容先合成到离屏缓冲区,再将 alpha 整体应用到缓冲区,使整个图层呈现均匀的透明效果。当值为 false 时,图层的 alpha 独立应用到每个子元素,重叠的半透明子元素在交叉处可能显得更深。
preserve3D:当值为 false(默认)时,具有 3D 变换的子图层在合成前会被压平到父级的 2D 平面。当值为 true 时,子图层保留其 3D 位置,在共享的 3D 空间中渲染,实现基于深度的交叉和正确的兄弟层 Z 排序。类似于 CSS 的 transform-style: preserve-3d。
变换属性优先级:x/y、matrix、matrix3D 三者存在覆盖关系:
- 仅设置
x/y:使用x/y作为平移 - 设置
matrix:matrix覆盖x/y的值 - 设置
matrix3D:matrix3D覆盖matrix和x/y的值
MaskType(遮罩类型):
| 值 | 说明 |
|---|---|
alpha |
Alpha 遮罩:使用遮罩的 alpha 通道 |
luminance |
亮度遮罩:使用遮罩的亮度值 |
contour |
轮廓遮罩:使用遮罩的轮廓进行裁剪 |
BlendMode:见 2.9 节混合模式完整表格。
5.3 图层样式(Layer Styles)
图层样式在图层内容的上方或下方添加视觉效果,不会替换原有内容。
图层样式的输入源:
所有图层样式都基于图层内容计算效果。计算时,图层内容会被转换为对应的不透明版本:使用正常的填充方式渲染几何形状,然后将所有半透明像素转换为完全不透明(完全透明的像素保留)。这意味着半透明填充产生的阴影效果与完全不透明填充相同。
部分图层样式还会额外使用图层轮廓或图层背景作为输入(详见各样式说明)。图层轮廓和图层背景的定义参见 5.1 节。
<?xml version="1.0" encoding="UTF-8"?>
<!-- Demonstrates layer styles: DropShadow and InnerShadow -->
<pagx width="400" height="400">
<Layer left="70" top="70">
<Rectangle width="260" height="260" roundness="40"/>
<Fill>
<LinearGradient endPoint="1,1">
<ColorStop color="#06B6D4" offset="0"/>
<ColorStop color="#0891B2" offset="1"/>
</LinearGradient>
</Fill>
<DropShadowStyle offsetY="16" blurX="48" blurY="48" color="#06B6D480"/>
<InnerShadowStyle offsetX="8" offsetY="8" blurX="24" blurY="24" color="#00000040"/>
</Layer>
</pagx>
所有 LayerStyle 共有属性:
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
blendMode |
BlendMode | normal | 混合模式(见 2.9 节) |
excludeChildEffects |
bool | false | 是否排除子图层效果 |
excludeChildEffects:当值为 false(默认)时,图层样式基于完整的图层内容计算,包含子图层的渲染结果。当值为 true 时,子图层的图层样式和图层滤镜不参与该样式的计算,但子图层的基础渲染结果仍然包含在内。
5.3.1 投影阴影(DropShadowStyle)
在图层下方绘制投影阴影。基于不透明图层内容计算阴影形状。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
offsetX |
float | 0 | X 偏移 |
offsetY |
float | 0 | Y 偏移 |
blurX |
float | 0 | X 模糊半径 |
blurY |
float | 0 | Y 模糊半径 |
color |
Color | #000000 | 阴影颜色 |
showBehindLayer |
bool | true | 图层后面是否显示阴影 |
渲染步骤:
- 获取不透明图层内容并偏移
(offsetX, offsetY) - 对偏移后的内容应用高斯模糊
(blurX, blurY) - 使用
color的颜色填充阴影区域 - 如果
showBehindLayer="false",使用图层轮廓作为擦除遮罩挖空被遮挡部分
showBehindLayer:
true:阴影完整显示,包括被图层内容遮挡的部分false:阴影被图层内容遮挡的部分会被挖空(使用图层轮廓作为擦除遮罩)
5.3.2 背景模糊(BackgroundBlurStyle)
在图层下方对图层背景应用模糊效果。基于图层背景计算效果,使用不透明图层内容作为遮罩裁剪。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
blurX |
float | 0 | X 模糊半径 |
blurY |
float | 0 | Y 模糊半径 |
tileMode |
TileMode | mirror | 平铺模式 |
渲染步骤:
- 获取图层边界下方的图层背景
- 对图层背景应用高斯模糊
(blurX, blurY) - 使用不透明图层内容作为遮罩裁剪模糊结果
5.3.3 内阴影(InnerShadowStyle)
在图层上方绘制内阴影,效果呈现在图层内容之内。基于不透明图层内容计算阴影范围。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
offsetX |
float | 0 | X 偏移 |
offsetY |
float | 0 | Y 偏移 |
blurX |
float | 0 | X 模糊半径 |
blurY |
float | 0 | Y 模糊半径 |
color |
Color | #000000 | 阴影颜色 |
渲染步骤:
- 获取不透明图层内容并偏移
(offsetX, offsetY) - 对偏移后内容的反向(内容外部区域)应用高斯模糊
(blurX, blurY) - 使用
color的颜色填充阴影区域 - 与不透明图层内容求交集,仅保留内容内部的阴影
5.4 图层滤镜(Layer Filters)
图层滤镜是图层渲染的最后一个环节,所有之前按顺序渲染的结果(包含图层样式)累积起来作为滤镜的输入。滤镜按文档顺序链式应用,每个滤镜的输出作为下一个滤镜的输入。
与图层样式(5.3 节)的关键区别:图层样式在图层内容的上方或下方独立渲染视觉效果,而滤镜修改图层的整体渲染输出。图层样式先于滤镜应用。
<?xml version="1.0" encoding="UTF-8"?>
<!-- Demonstrates layer filters: Blur and DropShadow -->
<pagx width="400" height="400">
<Layer left="70" top="70">
<Rectangle width="260" height="260" roundness="40"/>
<Fill>
<LinearGradient endPoint="1,1">
<ColorStop color="#8B5CF6" offset="0"/>
<ColorStop color="#6366F1" offset="1"/>
</LinearGradient>
</Fill>
<BlurFilter blurX="4" blurY="4"/>
<DropShadowFilter offsetX="0" offsetY="16" blurX="48" blurY="48" color="#8B5CF680"/>
</Layer>
</pagx>
5.4.1 模糊滤镜(BlurFilter)
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
blurX |
float | 0 | X 模糊半径 |
blurY |
float | 0 | Y 模糊半径 |
tileMode |
TileMode | decal | 平铺模式 |
5.4.2 投影阴影滤镜(DropShadowFilter)
基于滤镜输入生成阴影效果。与 DropShadowStyle 的核心区别:滤镜基于原始渲染内容投影,支持半透明度;而样式基于不透明图层内容投影。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
offsetX |
float | 0 | X 偏移 |
offsetY |
float | 0 | Y 偏移 |
blurX |
float | 0 | X 模糊半径 |
blurY |
float | 0 | Y 模糊半径 |
color |
Color | #000000 | 阴影颜色 |
shadowOnly |
bool | false | 仅显示阴影 |
渲染步骤:
- 将滤镜输入偏移
(offsetX, offsetY) - 提取 alpha 通道并应用高斯模糊
(blurX, blurY) - 使用
color的颜色填充阴影区域 - 将阴影与滤镜输入合成(
shadowOnly=false)或仅输出阴影(shadowOnly=true)
5.4.3 内阴影滤镜(InnerShadowFilter)
在滤镜输入的内部绘制阴影。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
offsetX |
float | 0 | X 偏移 |
offsetY |
float | 0 | Y 偏移 |
blurX |
float | 0 | X 模糊半径 |
blurY |
float | 0 | Y 模糊半径 |
color |
Color | #000000 | 阴影颜色 |
shadowOnly |
bool | false | 仅显示阴影 |
渲染步骤:
- 创建滤镜输入 alpha 通道的反向遮罩
- 偏移并应用高斯模糊
- 与滤镜输入的 alpha 通道求交集
- 将结果与滤镜输入合成(
shadowOnly=false)或仅输出阴影(shadowOnly=true)
5.4.4 混合滤镜(BlendFilter)
将指定颜色以指定混合模式叠加到图层上。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
color |
Color | (必填) | 混合颜色 |
blendMode |
BlendMode | normal | 混合模式(见 2.9 节) |
5.4.5 颜色矩阵滤镜(ColorMatrixFilter)
使用 4×5 颜色矩阵变换颜色。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
matrix |
Matrix | 单位矩阵 | 4x5 颜色矩阵(20 个逗号分隔的浮点数) |
矩阵格式(20 个值,行优先):
| R' | | m[0] m[1] m[2] m[3] m[4] | | R |
| G' | | m[5] m[6] m[7] m[8] m[9] | | G |
| B' | = | m[10] m[11] m[12] m[13] m[14] | × | B |
| A' | | m[15] m[16] m[17] m[18] m[19] | | A |
| 1 |
5.5 裁剪与遮罩(Clipping and Masking)
5.5.1 scrollRect(滚动裁剪)
scrollRect 属性定义图层的可视区域,超出该区域的内容会被裁剪。
<?xml version="1.0" encoding="UTF-8"?>
<!-- Demonstrates scrollRect clipping -->
<pagx width="400" height="400">
<Layer left="100" top="100" scrollRect="100,100,200,200">
<Ellipse left="40" top="40" width="320" height="320"/>
<Fill>
<RadialGradient>
<ColorStop color="#F43F5E" offset="0"/>
<ColorStop color="#EC4899" offset="0.6"/>
<ColorStop color="#8B5CF6" offset="1"/>
</RadialGradient>
</Fill>
</Layer>
<!-- Visible clip region indicator (dashed border) -->
<Layer>
<Rectangle left="100" top="100" width="200" height="200" roundness="16"/>
<Stroke color="#94A3B840" width="2" dashes="4,4"/>
</Layer>
</pagx>
5.5.2 clipToBounds
当 clipToBounds="true" 时,布局引擎在布局阶段将 scrollRect="0,0,width,height" 写入该图层,将内容裁剪到图层自身的边界(width × height)。此功能与自动布局兼容——裁剪区域在布局引擎解析出图层尺寸之后确定。如果图层同时设置了显式 scrollRect,则 scrollRect 优先,clipToBounds 被忽略。
<?xml version="1.0" encoding="UTF-8"?>
<!-- Demonstrates clipToBounds: clips overflowing content to layer bounds -->
<pagx width="400" height="400">
<Layer left="100" top="100" width="200" height="200" clipToBounds="true">
<Ellipse left="-20" right="-20" top="-20" bottom="-20"/>
<Fill>
<RadialGradient>
<ColorStop color="#F43F5E" offset="0"/>
<ColorStop color="#EC4899" offset="0.6"/>
<ColorStop color="#8B5CF6" offset="1"/>
</RadialGradient>
</Fill>
</Layer>
</pagx>
5.5.3 遮罩(Masking)
通过 mask 属性引用另一个图层作为遮罩。
<?xml version="1.0" encoding="UTF-8"?>
<!-- Demonstrates alpha masking -->
<pagx width="400" height="400">
<!-- Mask shape (invisible but used for masking) -->
<Layer id="maskShape" visible="false">
<Polystar left="50" top="50" type="star" pointCount="5" outerRadius="150" innerRadius="70"/>
<Fill color="#FFF"/>
</Layer>
<Layer mask="@maskShape">
<Rectangle left="20" top="20" width="360" height="360"/>
<Fill>
<LinearGradient endPoint="1,1">
<ColorStop color="#6366F1" offset="0"/>
<ColorStop color="#8B5CF6" offset="0.33"/>
<ColorStop color="#EC4899" offset="0.66"/>
<ColorStop color="#F43F5E" offset="1"/>
</LinearGradient>
</Fill>
</Layer>
</pagx>
遮罩规则:
- 遮罩图层自身不渲染(
visible属性被忽略) - 遮罩图层的变换不影响被遮罩图层
6. 矢量元素系统(VectorElement System)
矢量元素系统定义了 Layer 内的矢量内容如何被处理和渲染。
6.1 处理模型(Processing Model)
VectorElement 系统采用累积-渲染的处理模型:几何元素在渲染上下文中累积,修改器对累积的几何进行变换,绘制器触发最终渲染。
6.1.1 术语定义
| 术语 | 包含元素 | 说明 |
|---|---|---|
| 几何元素 | Rectangle、Ellipse、Polystar、Path、Text | 可渲染的几何(形状和文本),在上下文中累积为几何列表 |
| 修改器 | TrimPath、RoundCorner、MergePath、TextModifier、TextPath、TextBox、Repeater | 对累积的几何进行变换 |
| 绘制器 | Fill、Stroke | 对累积的几何进行填充或描边渲染 |
| 容器 | Group | 创建独立作用域并应用矩阵变换,处理完成后合并 |
6.1.2 几何元素的内部结构
几何元素在上下文中累积时,内部结构有所不同:
| 元素类型 | 内部结构 | 说明 |
|---|---|---|
| 形状元素(Rectangle、Ellipse、Polystar、Path) | 单个 Path | 每个形状元素产生一个路径 |
| 文本元素(Text) | 字形列表 | 一个 Text 经过塑形后产生多个字形 |
6.1.3 处理与渲染顺序
VectorElement 按文档顺序依次处理,文档中靠前的元素先处理。默认情况下,先处理的绘制器先渲染(位于下方)。
由于 Fill 和 Stroke 可通过 placement 属性指定渲染到图层的背景或前景,因此最终渲染顺序可能与文档顺序不完全一致。但在默认情况下(所有内容均为背景),渲染顺序与文档顺序一致。
6.1.4 统一处理流程
几何元素 修改器 绘制器
┌──────────┐ ┌──────────┐ ┌──────────┐
│Rectangle │ │ TrimPath │ │ Fill │
│ Ellipse │ │RoundCorn │ │ Stroke │
│ Polystar │ │MergePath │ └────┬─────┘
│ Path │ │TextModif │ │
│ Text │ │ TextPath │ │
└────┬─────┘ │TextBox │ │
│ │ Repeater │ │
│ └────┬─────┘ │
│ │ │
│ 累积几何 │ 变换几何 │ 渲染
▼ ▼ ▼
┌─────────────────────────────────────────────────────────┐
│ 几何列表 [Path1, Path2, 字形列表1, 字形列表2...] │
└─────────────────────────────────────────────────────────┘
渲染上下文累积的是一个几何列表,其中:
- 每个形状元素贡献一个 Path
- 每个 Text 贡献一个字形列表(包含多个字形)
6.1.5 修改器的作用范围
不同修改器对几何列表中的元素有不同的作用范围:
| 修改器类型 | 作用对象 | 说明 |
|---|---|---|
| 形状修改器(TrimPath、RoundCorner、MergePath) | 仅 Path | 对文本触发强制转换 |
| 文本修改器(TextModifier、TextPath、TextBox) | 仅字形列表 | 对 Path 无效 |
| 复制器(Repeater) | Path + 字形列表 | 同时作用于所有几何 |
6.2 几何元素(Geometry Elements)
几何元素提供可渲染的几何(形状和文本)。所有几何元素以及 TextPath、TextBox 和 Group 都支持约束属性用于容器内定位——完整属性列表、定义和行为见 §4.3。各元素属性表中不再重复列出约束属性。
6.2.1 矩形(Rectangle)
矩形从中心点定义,支持统一圆角。
<Rectangle width="200" height="150" roundness="10" reversed="false"/>
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
position |
Point | (bounding box 中心) | 中心点坐标,可动画。设置约束属性时由约束系统自动计算。未设置时默认为 (size.width/2, size.height/2),使左上角对齐原点。静态布局推荐使用约束属性 |
size |
Size | 0,0 | 尺寸 "width,height",可动画。静态布局建议使用 width/height 属性 |
roundness |
float | 0 | 圆角半径 |
reversed |
bool | false | 反转路径方向 |
矩形支持所有约束属性(见 §4.3)。
计算规则:
rect.left = position.x - size.width / 2
rect.top = position.y - size.height / 2
rect.right = position.x + size.width / 2
rect.bottom = position.y + size.height / 2
圆角处理:
roundness值自动限制为min(roundness, size.width/2, size.height/2)- 当
roundness >= min(size.width, size.height) / 2时,短边方向呈半圆形
示例:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Demonstrates Rectangle shape with various roundness values -->
<pagx width="400" height="400">
<Layer width="400" height="400" layout="vertical">
<!-- Row 1 -->
<Layer flex="1" layout="horizontal">
<!-- Sharp rectangle -->
<Layer flex="1">
<Rectangle centerX="0" centerY="0" width="140" height="140"/>
<Fill>
<LinearGradient>
<ColorStop color="#F43F5E" offset="0"/>
<ColorStop color="#E11D48" offset="1"/>
</LinearGradient>
</Fill>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#F43F5E50"/>
</Layer>
<!-- Medium rounded rectangle -->
<Layer flex="1">
<Rectangle centerX="0" centerY="0" width="140" height="140" roundness="24"/>
<Fill>
<LinearGradient>
<ColorStop color="#8B5CF6" offset="0"/>
<ColorStop color="#7C3AED" offset="1"/>
</LinearGradient>
</Fill>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#8B5CF650"/>
</Layer>
</Layer>
<Layer flex="1" layout="horizontal">
<!-- Fully rounded rectangle (pill shape) -->
<Layer flex="1">
<Rectangle centerX="0" centerY="0" width="140" height="140" roundness="70"/>
<Fill>
<LinearGradient>
<ColorStop color="#06B6D4" offset="0"/>
<ColorStop color="#0891B2" offset="1"/>
</LinearGradient>
</Fill>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#06B6D450"/>
</Layer>
<!-- Wide rectangle -->
<Layer flex="1">
<Rectangle centerX="0" centerY="0" width="140" height="100" roundness="16"/>
<Fill>
<LinearGradient>
<ColorStop color="#10B981" offset="0"/>
<ColorStop color="#059669" offset="1"/>
</LinearGradient>
</Fill>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#10B98150"/>
</Layer>
</Layer>
</Layer>
</pagx>
路径起点:矩形路径从右上角开始,顺时针方向绘制(reversed="false" 时)。
6.2.2 椭圆(Ellipse)
椭圆从中心点定义。
<Ellipse width="100" height="60" reversed="false"/>
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
position |
Point | (bounding box 中心) | 中心点坐标,可动画。设置约束属性时由约束系统自动计算。未设置时默认为 (size.width/2, size.height/2),使左上角对齐原点。静态布局推荐使用约束属性 |
size |
Size | 0,0 | 尺寸 "width,height",可动画。静态布局建议使用 width/height 属性 |
reversed |
bool | false | 反转路径方向 |
椭圆支持所有约束属性(见 §4.3)。
计算规则:
boundingRect.left = position.x - size.width / 2
boundingRect.top = position.y - size.height / 2
boundingRect.right = position.x + size.width / 2
boundingRect.bottom = position.y + size.height / 2
示例:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Demonstrates Ellipse shape -->
<pagx width="400" height="400">
<Layer width="400" height="400" layout="vertical">
<!-- Row 1 -->
<Layer flex="1" layout="horizontal">
<!-- Perfect circle -->
<Layer flex="1">
<Ellipse centerX="0" centerY="0" width="140" height="140"/>
<Fill>
<RadialGradient>
<ColorStop color="#FBBF24" offset="0"/>
<ColorStop color="#F59E0B" offset="1"/>
</RadialGradient>
</Fill>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#F59E0B60"/>
</Layer>
<!-- Horizontal ellipse -->
<Layer flex="1">
<Ellipse centerX="0" centerY="0" width="160" height="100"/>
<Fill>
<LinearGradient>
<ColorStop color="#EC4899" offset="0"/>
<ColorStop color="#DB2777" offset="1"/>
</LinearGradient>
</Fill>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#EC489950"/>
</Layer>
</Layer>
<Layer flex="1" layout="horizontal">
<!-- Vertical ellipse -->
<Layer flex="1">
<Ellipse centerX="0" centerY="0" width="100" height="160"/>
<Fill>
<LinearGradient>
<ColorStop color="#06B6D4" offset="0"/>
<ColorStop color="#0891B2" offset="1"/>
</LinearGradient>
</Fill>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#06B6D450"/>
</Layer>
<!-- Large ellipse -->
<Layer flex="1">
<Ellipse centerX="0" centerY="0" width="150" height="150"/>
<Fill>
<RadialGradient>
<ColorStop color="#A78BFA" offset="0"/>
<ColorStop color="#8B5CF6" offset="1"/>
</RadialGradient>
</Fill>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#8B5CF650"/>
</Layer>
</Layer>
</Layer>
</pagx>
路径起点:椭圆路径从右侧中点(3 点钟方向)开始。
6.2.3 多边形/星形(Polystar)
支持正多边形和星形两种模式。
<Polystar type="star" pointCount="5" outerRadius="100" innerRadius="50" rotation="0" outerRoundness="0" innerRoundness="0" reversed="false"/>
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
position |
Point | (-bounds.x, -bounds.y) | 中心点坐标,设置约束属性时由约束系统自动计算。未设置时默认为像素边界框原点的负值,使像素左上角对齐原点。推荐使用约束属性(left/top)进行定位 |
type |
PolystarType | star | 类型(见下方) |
pointCount |
float | 5 | 顶点数(支持小数) |
outerRadius |
float | 100 | 外半径 |
innerRadius |
float | 50 | 内半径(仅星形) |
rotation |
float | 0 | 旋转角度 |
outerRoundness |
float | 0 | 外角圆度 0~1 |
innerRoundness |
float | 0 | 内角圆度 0~1 |
reversed |
bool | false | 反转路径方向 |
PolystarType(类型):
| 值 | 说明 |
|---|---|
polygon |
正多边形:只使用外半径 |
star |
星形:使用外半径和内半径交替 |
多边形模式 (type="polygon"):
- 只使用
outerRadius和outerRoundness innerRadius和innerRoundness被忽略
星形模式 (type="star"):
- 外顶点位于
outerRadius处 - 内顶点位于
innerRadius处 - 顶点交替连接形成星形
顶点计算(第 i 个外顶点):
angle = rotation + (i / pointCount) * 360°
x = position.x + outerRadius * cos(angle)
y = position.y + outerRadius * sin(angle)
小数点数:
pointCount支持小数值(如5.5)- 小数部分表示最后一个顶点的"完成度",产生不完整的最后一个角
pointCount <= 0时不生成任何路径
圆度处理:
outerRoundness和innerRoundness取值范围 0~1- 0 表示尖角,1 表示完全圆滑
- 圆度通过在顶点处添加贝塞尔控制点实现
示例:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Demonstrates Polystar shape (star and polygon) -->
<pagx width="400" height="400">
<Layer width="400" height="400" layout="vertical">
<!-- Row 1: stars -->
<Layer flex="1" layout="horizontal">
<!-- 5-pointed star -->
<Layer flex="1">
<Polystar centerX="0" centerY="0" type="star" pointCount="5" outerRadius="60" innerRadius="28"/>
<Fill>
<LinearGradient>
<ColorStop color="#FBBF24" offset="0"/>
<ColorStop color="#F59E0B" offset="1"/>
</LinearGradient>
</Fill>
</Layer>
<!-- 6-pointed star -->
<Layer flex="1">
<Polystar centerX="0" centerY="0" type="star" pointCount="6" outerRadius="60" innerRadius="32"/>
<Fill>
<LinearGradient>
<ColorStop color="#F43F5E" offset="0"/>
<ColorStop color="#E11D48" offset="1"/>
</LinearGradient>
</Fill>
</Layer>
</Layer>
<!-- Row 2: polygons -->
<Layer flex="1" layout="horizontal">
<!-- Hexagon -->
<Layer flex="1">
<Polystar centerX="0" centerY="0" type="polygon" pointCount="6" outerRadius="64"/>
<Fill>
<LinearGradient>
<ColorStop color="#06B6D4" offset="0"/>
<ColorStop color="#0891B2" offset="1"/>
</LinearGradient>
</Fill>
</Layer>
<!-- Pentagon -->
<Layer flex="1">
<Polystar centerX="0" centerY="0" type="polygon" pointCount="5" outerRadius="64"/>
<Fill>
<LinearGradient>
<ColorStop color="#8B5CF6" offset="0"/>
<ColorStop color="#7C3AED" offset="1"/>
</LinearGradient>
</Fill>
</Layer>
</Layer>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#00000050"/>
</Layer>
</pagx>
6.2.4 路径(Path)
使用 SVG 路径语法定义任意形状,支持内联数据或引用 Resources 中定义的 PathData。
<!-- 内联路径数据 -->
<Path data="M 0 0 L 100 0 L 100 100 Z" reversed="false"/>
<!-- 引用 PathData 资源 -->
<Path data="@curvePath" reversed="false"/>
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
data |
string/idref | (必填) | SVG 路径数据或 PathData 资源引用 "@id" |
position |
Point | 0,0 | 路径坐标系原点的偏移。推荐使用约束属性(left/top)进行定位 |
reversed |
bool | false | 反转路径方向 |
示例:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Demonstrates Path shape with custom bezier curves -->
<pagx width="400" height="400">
<!-- Heart shape (top-left quadrant: 0-200 x 0-200, center at 100,100) -->
<Layer>
<Path data="M 100,80 C 100,60 120,50 140,50 C 160,50 170,60 170,80 C 170,100 140,140 100,170 C 60,140 30,100 30,80 C 30,60 40,50 60,50 C 80,50 100,60 100,80 Z"/>
<Fill>
<LinearGradient>
<ColorStop color="#F43F5E" offset="0"/>
<ColorStop color="#EC4899" offset="1"/>
</LinearGradient>
</Fill>
<DropShadowStyle offsetY="4" blurX="16" blurY="16" color="#F43F5E60"/>
</Layer>
<!-- Lightning bolt (top-right quadrant: 200-400 x 0-200, center at 300,100) -->
<Layer>
<Path data="M 310,45 L 275,110 L 305,110 L 270,195 L 340,100 L 310,100 L 340,45 Z"/>
<Fill>
<LinearGradient>
<ColorStop color="#FBBF24" offset="0"/>
<ColorStop color="#F59E0B" offset="1"/>
</LinearGradient>
</Fill>
<DropShadowStyle offsetY="4" blurX="12" blurY="12" color="#F59E0B60"/>
</Layer>
<!-- Arrow (bottom-left quadrant: 0-200 x 200-400, center at 100,300) -->
<Layer>
<Path data="M 30,290 L 130,290 L 130,260 L 180,300 L 130,340 L 130,310 L 30,310 Z"/>
<Fill color="#06B6D4"/>
<DropShadowStyle offsetY="4" blurX="12" blurY="12" color="#06B6D460"/>
</Layer>
<!-- Star (bottom-right quadrant: 200-400 x 200-400, center at 300,300) -->
<Layer>
<Path data="M 300,220 L 318,270 L 370,270 L 328,300 L 343,350 L 300,322 L 257,350 L 272,300 L 230,270 L 282,270 Z"/>
<Fill color="#8B5CF6"/>
<DropShadowStyle offsetY="4" blurX="12" blurY="12" color="#8B5CF660"/>
</Layer>
</pagx>
6.2.5 文本(Text)
Text 是一种几何元素,向几何列表贡献字形(而非路径)。与形状元素产生单一 Path 不同,Text 经过塑形后会产生字形列表(多个字形)并累积到渲染上下文的几何列表中,供后续修改器变换或绘制器渲染。
<Text text="Hello World" left="100" top="200" fontFamily="Arial" fontStyle="Regular" fauxBold="true" fauxItalic="false" fontSize="24" letterSpacing="0" textAnchor="start"/>
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
text |
string | "" | 文本内容 |
position |
Point | 0,0 | 文本起点位置(y 为基线),设置约束属性时由约束系统自动计算。推荐使用约束属性(left/top)进行定位 |
fontFamily |
string | "" | 字体族(空字符串表示系统默认字体) |
fontStyle |
string | "" | 字体变体(Regular, Bold, Italic, Bold Italic 等)。空字符串表示该字体的默认变体 |
fontSize |
float | 12 | 字号 |
letterSpacing |
float | 0 | 字间距 |
fauxBold |
bool | false | 仿粗体效果 |
fauxItalic |
bool | false | 仿斜体效果 |
baseline |
TextBaseline | lineBox | 垂直定位基线模式。lineBox:position.y 是 linebox 顶部(基于字体指标行高);alphabetic:position.y 是字母基线 |
textAnchor |
TextAnchor | start | 文本锚点对齐——控制文本相对原点的位置(见下方)。有 TextBox 排版时忽略 |
子元素:CDATA 文本、GlyphRun*
文本内容:通常使用 text 属性指定文本内容。当文本包含 XML 特殊字符(<、>、& 等)或需要保留多行格式时,可使用 CDATA 子节点替代 text 属性。Text 不允许直接包含纯文本子节点,必须用 CDATA 包裹。
<!-- 简单文本:使用 text 属性 -->
<Text text="Hello World" fontFamily="Arial" fontSize="24"/>
<!-- 包含特殊字符:使用 CDATA -->
<Text fontFamily="Arial" fontSize="24"><![CDATA[A < B & C > D]]></Text>
<!-- 多行文本:使用 CDATA 保留格式 -->
<Text fontFamily="Arial" fontSize="24">
<![CDATA[Line 1
Line 2
Line 3]]>
</Text>
渲染模式:Text 支持预排版和运行时排版两种模式。预排版通过 GlyphRun 子节点提供预计算的字形和位置,使用嵌入字体渲染,确保跨平台完全一致。运行时排版在运行时进行塑形和排版,因各平台字体和排版特性差异,可能存在细微不一致。如需精确还原设计工具的排版效果,建议使用预排版。
TextAnchor(文本锚点对齐):
控制文本相对其原点的定位方式。
| 值 | 说明 |
|---|---|
start |
原点位于文本起始位置,不做偏移 |
center |
原点位于文本中心位置,文本偏移半个宽度使其居中于原点 |
end |
原点位于文本结束位置,文本偏移整个宽度使其终点对齐原点 |
TextBaseline(基线模式):
控制 position.y 在垂直定位中的解释方式。
| 值 | 说明 |
|---|---|
lineBox |
position.y 是 linebox 顶部(基于字体指标行高)(默认值) |
alphabetic |
position.y 是字母基线。文本直接在基线位置渲染 |
运行时排版渲染流程:
- 根据
fontFamily和fontStyle查找系统字体,不可用时按运行时配置的回退列表选择替代字体 - 使用
text属性(或 CDATA 子节点)进行塑形,换行符触发换行(默认行高取自字体指标:ascent + descent + leading,可通过 TextBox 自定义) - 应用
fontSize、letterSpacing等排版参数 - 构造字形列表累积到渲染上下文
运行时排版示例:
<?xml version="1.0" encoding="UTF-8"?>
<pagx width="400" height="400">
<Layer left="0" right="0" top="40" layout="vertical" gap="35" alignment="center">
<Layer>
<Text text="PAGX" fontFamily="Arial" fontStyle="Bold" fontSize="84"/>
<Fill>
<LinearGradient>
<ColorStop color="#6366F1" offset="0"/>
<ColorStop color="#8B5CF6" offset="0.5"/>
<ColorStop color="#EC4899" offset="1"/>
</LinearGradient>
</Fill>
</Layer>
<Layer>
<Text text="Text Shape" fontFamily="Arial" fontSize="36"/>
<Fill color="#334155"/>
</Layer>
<Layer>
<Text text="Beautiful Typography" fontFamily="Arial" fontSize="24"/>
<Fill color="#475569"/>
</Layer>
</Layer>
</pagx>
预排版数据(GlyphRun)
GlyphRun 定义一组字形的预排版数据,每个 GlyphRun 独立引用一个字体资源。
坐标系:GlyphRun 的位置属性(x、y、xOffsets、positions)使用排版坐标系 —— 对于 TextBox 内的 Text,坐标相对于 TextBox 原点;对于独立 Text,坐标相对于 Text 自身原点。渲染时,引擎会应用相应的逆变换将排版坐标转换回 Text 本地坐标。
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
font |
idref | (必填) | 引用 Font 资源 @id |
fontSize |
float | 12 | 渲染字号。实际缩放比例 = fontSize / font.unitsPerEm |
glyphs |
string | (必填) | GlyphID 序列,逗号分隔(0 表示缺失字形) |
x |
float | 0 | 总体 X 偏移 |
y |
float | 0 | 总体 Y 偏移 |
xOffsets |
string | - | 每字形 X 偏移,逗号分隔 |
positions |
string | - | 每字形 (x,y) 偏移,分号分隔 |
anchors |
string | - | 每字形锚点偏移 (x,y),分号分隔。锚点是缩放、旋转和斜切变换的中心点。默认锚点为 (advance×0.5, 0) |
scales |
string | - | 每字形缩放 (sx,sy),分号分隔。缩放围绕锚点进行。默认 1,1 |
rotations |
string | - | 每字形旋转角度(度),逗号分隔。旋转围绕锚点进行。默认 0 |
skews |
string | - | 每字形斜切角度(度),逗号分隔。斜切围绕锚点进行。默认 0 |
bounds |
string | - | 行框边界(x,y,w,h),在字体嵌入时计算。用于原始字体指标不可用时的布局测量 |
所有属性均为可选,可任意组合使用。当属性数组长度小于字形数量时,缺失的值使用默认值。
位置计算:
finalX[i] = x + xOffsets[i] + positions[i].x
finalY[i] = y + positions[i].y
- 未指定
xOffsets时,xOffsets[i]视为 0 - 未指定
positions时,positions[i]视为 (0, 0) - 不指定
xOffsets和positions时:首个字形位于 (x, y),后续字形依次累加 advance
变换应用顺序:
当字形有 scale、rotation 或 skew 变换时,按以下顺序应用(与 TextModifier 一致):
- 平移到锚点(
translate(-anchor)) - 缩放(
scale) - 斜切(
skew,沿垂直轴方向) - 旋转(
rotation) - 平移回锚点(
translate(anchor)) - 平移到位置(
translate(position))
锚点:
- 每个字形的默认锚点位于
(advance × 0.5, 0),即字形水平中心的基线位置 anchors属性记录的是相对于默认锚点的偏移,最终锚点 = 默认锚点 + anchors[i]
预排版示例:
<?xml version="1.0" encoding="UTF-8"?>
<pagx width="400" height="400">
<Layer>
<Text text="GlyphRun" fontFamily="Arial" fontStyle="Bold" fontSize="72">
<GlyphRun font="@font1" fontSize="72" glyphs="14,15,16,17,18,19,20,21" x="34" y="145" bounds="0,0,335,85"/>
</Text>
<Fill>
<LinearGradient>
<ColorStop color="#6366F1" offset="0"/>
<ColorStop color="#EC4899" offset="1"/>
</LinearGradient>
</Fill>
<Group>
<Text text="Embedded Font" fontFamily="Arial" fontSize="36">
<GlyphRun font="@font1" fontSize="36" glyphs="6,7,8,9,10,10,9,10,22,11,12,21,13" y="230" xOffsets="66,87,120,142,162,184,206,226,248,256,276,298,320" bounds="0,0,273,42"/>
</Text>
<Fill color="#334155"/>
</Group>
<Group>
<Text text="Pre-shaped Glyphs" fontFamily="Arial" fontSize="24">
<GlyphRun font="@font1" fontSize="24" glyphs="1,2,9,3,4,18,5,17,9,10,22,14,15,16,17,18,4" y="310" xOffsets="94.5,109.5,118.5,131.5,139.5,150.5,165.5,179.5,194.5,207.5,222.5,227.5,244.5,251.5,264.5,279.5,294.5" bounds="0,0,221,28"/>
</Text>
<Fill color="#475569"/>
</Group>
</Layer>
<Resources>
<Font id="font1">
<Glyph advance="625" path="M100 0L193 0L193 -299L314 -299C475 -299 584 -372 584 -531C584 -693 474 -750 310 -750L100 -750L100 0ZM193 -375L193 -674L298 -674C426 -674 492 -639 492 -531C492 -423 431 -375 301 -375L193 -375Z"/>
<Glyph advance="375" path="M92 0L183 0L183 -337C219 -427 275 -458 320 -458C342 -458 355 -456 372 -450L390 -542C372 -538 355 -542 331 -542C271 -542 215 -497 178 -429L174 -429L167 -542L92 -542L92 0Z"/>
<Glyph advance="333.333" path="M46 -250L301 -250L301 -320L46 -320L46 -250Z"/>
<Glyph advance="458.333" path="M234 0C362 0 432 -72 432 -155C432 -251 344 -281 266 -309C204 -330 148 -348 148 -396C148 -436 181 -469 250 -469C298 -469 336 -451 372 -424L417 -480C375 -513 316 -542 249 -542C131 -542 61 -473 61 -393C61 -307 144 -272 219 -246C280 -225 344 -202 344 -150C344 -106 309 -71 237 -71C172 -71 122 -95 76 -130L31 -74C83 -32 157 0 234 0Z"/>
<Glyph advance="583.333" path="M217 0C284 0 344 -35 396 -77L400 -77L408 0L482 0L482 -323C482 -453 426 -542 295 -542C208 -542 131 -503 81 -470L117 -410C160 -438 217 -465 280 -465C368 -465 392 -400 392 -333C161 -307 58 -251 58 -136C58 -56 126 0 217 0ZM243 -73C189 -73 146 -96 146 -154C146 -219 209 -261 392 -282L392 -140C339 -96 296 -73 243 -73Z"/>
<Glyph advance="583.333" path="M100 0L534 0L534 -79L193 -79L193 -338L471 -338L471 -417L193 -417L193 -643L523 -643L523 -722L100 -722L100 0Z"/>
<Glyph advance="916.667" path="M92 0L183 0L183 -392C233 -448 279 -475 320 -475C389 -475 421 -432 421 -331L421 0L512 0L512 -392C563 -448 607 -475 649 -475C718 -475 750 -432 750 -331L750 0L841 0L841 -343C841 -481 788 -556 676 -556C610 -556 553 -512 497 -451C475 -515 430 -556 347 -556C282 -556 225 -514 178 -462L175 -462L167 -556L92 -556L92 0Z"/>
<Glyph advance="611.111" path="M331 0C456 0 567 -106 567 -286C567 -447 491 -556 350 -556C290 -556 229 -521 180 -478L183 -576L183 -794L92 -794L92 0L165 0L173 -69L177 -69C224 -26 281 0 331 0ZM316 -76C280 -76 231 -90 183 -131L183 -406C235 -453 283 -478 329 -478C432 -478 472 -400 472 -284C472 -154 406 -76 316 -76Z"/>
<Glyph advance="555.556" path="M311 0C385 0 443 -25 491 -56L458 -113C418 -88 375 -73 322 -73C219 -73 148 -142 142 -250L508 -250C510 -263 512 -282 512 -302C512 -456 434 -556 296 -556C170 -556 51 -446 51 -271C51 -102 167 0 311 0ZM141 -316C152 -421 220 -482 297 -482C382 -482 432 -424 432 -316L141 -316Z"/>
<Glyph advance="611.111" path="M277 0C342 0 399 -35 442 -77L445 -77L453 0L527 0L527 -794L437 -794L437 -586L441 -491C393 -531 352 -556 288 -556C164 -556 53 -445 53 -270C53 -102 141 0 277 0ZM297 -76C201 -76 147 -151 147 -278C147 -397 216 -478 304 -478C349 -478 390 -463 437 -423L437 -148C391 -100 347 -76 297 -76Z"/>
<Glyph advance="555.556" path="M100 0L193 0L193 -333L473 -333L473 -411L193 -411L193 -643L523 -643L523 -722L100 -722L100 0Z"/>
<Glyph advance="611.111" path="M303 0C436 0 555 -103 555 -276C555 -451 436 -556 303 -556C170 -556 51 -451 51 -276C51 -103 170 0 303 0ZM303 -76C209 -76 146 -156 146 -276C146 -397 209 -479 303 -479C397 -479 461 -397 461 -276C461 -156 397 -76 303 -76Z"/>
<Glyph advance="388.889" path="M263 0C296 0 332 -10 363 -20L345 -88C327 -81 302 -74 283 -74C220 -74 199 -112 199 -179L199 -481L346 -481L346 -556L199 -556L199 -708L123 -708L112 -556L27 -550L27 -481L108 -481L108 -181C108 -73 147 0 263 0Z"/>
<Glyph advance="694.444" path="M388 14C487 14 568 -22 615 -71L615 -382L375 -382L375 -306L530 -306L530 -111C501 -83 450 -67 398 -67C240 -67 153 -185 153 -372C153 -555 249 -669 396 -669C471 -669 518 -638 555 -599L605 -659C563 -704 496 -750 394 -750C200 -750 58 -606 58 -368C58 -128 196 14 388 14Z"/>
<Glyph advance="291.667" path="M188 14C213 14 228 11 241 6L228 -64C218 -62 214 -62 209 -62C195 -62 183 -73 183 -101L183 -795L92 -795L92 -107C92 -30 120 14 188 14Z"/>
<Glyph advance="527.778" path="M101 236C209 236 266 154 303 47L508 -542L419 -542L322 -239C307 -191 291 -136 276 -87L271 -87C254 -136 235 -192 219 -239L108 -542L13 -542L231 3L219 44C197 111 158 161 96 161C82 161 66 156 55 152L37 225C54 232 75 236 101 236Z"/>
<Glyph advance="625" path="M92 222L183 222L183 45L181 -49C230 -9 282 14 331 14C456 14 567 -93 567 -279C567 -446 491 -556 351 -556C288 -556 227 -520 178 -479L175 -479L167 -542L92 -542L92 222ZM316 -62C280 -62 232 -77 183 -119L183 -403C236 -452 283 -479 329 -479C432 -479 472 -398 472 -278C472 -143 406 -62 316 -62Z"/>
<Glyph advance="611.111" path="M92 0L183 0L183 -393C238 -447 276 -475 332 -475C404 -475 435 -433 435 -331L435 0L526 0L526 -343C526 -481 474 -556 360 -556C286 -556 230 -515 180 -464L183 -576L183 -794L92 -794L92 0Z"/>
<Glyph advance="638.889" path="M100 0L193 0L193 -314L325 -314L503 0L608 0L420 -324C520 -348 586 -416 586 -529C586 -682 479 -736 330 -736L100 -736L100 0ZM193 -389L193 -660L316 -660C431 -660 494 -626 494 -529C494 -435 431 -389 316 -389L193 -389Z"/>
<Glyph advance="611.111" path="M250 14C325 14 379 -25 430 -84L433 -84L440 0L516 0L516 -542L425 -542L425 -157C373 -92 334 -65 278 -65C206 -65 176 -108 176 -209L176 -542L85 -542L85 -198C85 -60 136 14 250 14Z"/>
<Glyph advance="611.111" path="M92 0L183 0L183 -393C238 -447 276 -475 332 -475C404 -475 435 -433 435 -331L435 0L526 0L526 -343C526 -481 474 -556 360 -556C286 -556 230 -515 178 -464L175 -464L167 -542L92 -542L92 0Z"/>
<Glyph advance="208.333"/>
</Font>
</Resources>
</pagx>
6.3 绘制器(Painters)
绘制器(Fill、Stroke)对当前时刻累积的所有几何(Path 和字形列表)进行渲染。
6.3.1 填充(Fill)
填充使用指定的颜色源绘制几何的内部区域。
<?xml version="1.0" encoding="UTF-8"?>
<!-- Demonstrates different fill types -->
<pagx width="400" height="400">
<Layer width="400" height="400" layout="vertical">
<!-- Row 1: solid and gradient fills -->
<Layer flex="1" layout="horizontal">
<!-- Solid color fill -->
<Layer flex="1">
<Rectangle centerX="0" centerY="0" width="140" height="140" roundness="24"/>
<Fill color="#F43F5E"/>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#F43F5E50"/>
</Layer>
<!-- Linear gradient fill -->
<Layer flex="1">
<Rectangle centerX="0" centerY="0" width="140" height="140" roundness="24"/>
<Fill>
<LinearGradient>
<ColorStop color="#6366F1" offset="0"/>
<ColorStop color="#8B5CF6" offset="1"/>
</LinearGradient>
</Fill>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#8B5CF650"/>
</Layer>
</Layer>
<Layer flex="1">
<Ellipse centerX="0" centerY="0" width="320" height="140"/>
<Fill>
<RadialGradient center="160,70" radius="170" fitsToGeometry="false">
<ColorStop color="#FFF" offset="0"/>
<ColorStop color="#06B6D4" offset="0.5"/>
<ColorStop color="#0891B2" offset="1"/>
</RadialGradient>
</Fill>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#06B6D450"/>
</Layer>
</Layer>
</pagx>
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
color |
Color/idref | #000000 | 颜色值或颜色源引用,默认黑色 |
alpha |
float | 1 | 透明度 0~1 |
blendMode |
BlendMode | normal | 混合模式(见 2.9 节) |
fillRule |
FillRule | winding | 填充规则(见下方) |
placement |
LayerPlacement | background | 绘制位置(见 6.3.3 节) |
子元素:可内嵌一个颜色源(SolidColor、LinearGradient、RadialGradient、ConicGradient、DiamondGradient、ImagePattern)
FillRule(填充规则):
| 值 | 说明 |
|---|---|
winding |
非零环绕规则:根据路径方向计数,非零则填充 |
evenOdd |
奇偶规则:根据交叉次数,奇数则填充 |
文本填充:
- 文本以字形(glyph)为单位填充
- 支持通过 TextModifier 对单个字形应用颜色覆盖
- 颜色覆盖采用 alpha 混合:
finalColor = lerp(originalColor, overrideColor, overrideAlpha)
6.3.2 描边(Stroke)
描边沿几何边界绘制线条。
<?xml version="1.0" encoding="UTF-8"?>
<pagx width="400" height="400">
<Layer width="400" height="400" layout="vertical">
<!-- Row 1: stroke styles -->
<Layer flex="1" layout="horizontal">
<Layer flex="1">
<Rectangle centerX="0" centerY="0" width="130" height="130" roundness="20"/>
<Stroke color="#06B6D4" width="8" cap="round" join="round"/>
</Layer>
<Layer flex="1">
<Rectangle centerX="0" centerY="0" width="130" height="130" roundness="20"/>
<Stroke color="#8B5CF6" width="6" cap="round" dashes="12,8"/>
</Layer>
</Layer>
<!-- Row 2: gradient stroke on path -->
<Layer flex="1">
<Path centerX="0" data="M 0,120 Q 150,0 300,120"/>
<Stroke width="10" cap="round">
<LinearGradient>
<ColorStop color="#F43F5E" offset="0"/>
<ColorStop color="#EC4899" offset="0.5"/>
<ColorStop color="#8B5CF6" offset="1"/>
</LinearGradient>
</Stroke>
</Layer>
</Layer>
</pagx>
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
color |
Color/idref | #000000 | 颜色值或颜色源引用,默认黑色 |
width |
float | 1 | 描边宽度 |
alpha |
float | 1 | 透明度 0~1 |
blendMode |
BlendMode | normal | 混合模式(见 2.9 节) |
cap |
LineCap | butt | 线帽样式(见下方) |
join |
LineJoin | miter | 线连接样式(见下方) |
miterLimit |
float | 4 | 斜接限制 |
dashes |
string | - | 虚线模式 "d1,d2,..." |
dashOffset |
float | 0 | 虚线偏移 |
dashAdaptive |
bool | false | 等长虚线段缩放 |
align |
StrokeAlign | center | 描边对齐(见下方) |
placement |
LayerPlacement | background | 绘制位置(见 6.3.3 节) |
LineCap(线帽样式):
| 值 | 说明 |
|---|---|
butt |
平头:线条不超出端点 |
round |
圆头:以半圆形扩展端点 |
square |
方头:以方形扩展端点 |
LineJoin(线连接样式):
| 值 | 说明 |
|---|---|
miter |
斜接:延伸外边缘形成尖角 |
round |
圆角:以圆弧连接 |
bevel |
斜角:以三角形填充连接处 |
StrokeAlign(描边对齐):
| 值 | 说明 |
|---|---|
center |
描边居中于路径(默认) |
inside |
描边在闭合路径内侧 |
outside |
描边在闭合路径外侧 |
内侧/外侧描边通过以下方式实现:
- 以双倍宽度描边
- 与原始形状进行布尔运算(内侧用交集,外侧用差集)
虚线模式:
dashes:定义虚线段长度序列,如"5,3"表示 5px 实线 + 3px 空白dashOffset:虚线起始偏移量dashAdaptive:为 true 时,缩放虚线间隔使各虚线段保持等长
6.3.3 绘制位置(LayerPlacement)
Fill 和 Stroke 的 placement 属性控制相对于子图层的绘制顺序:
| 值 | 说明 |
|---|---|
background |
在子图层下方绘制(默认) |
foreground |
在子图层上方绘制 |
6.4 形状修改器(Shape Modifiers)
形状修改器对累积的 Path 进行原地变换,对字形列表则触发强制转换为 Path。
6.4.1 路径裁剪(TrimPath)
裁剪路径到指定的起止范围。
<TrimPath start="0" end="0.5" offset="0" type="separate"/>
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
start |
float | 0 | 起始位置 0~1 |
end |
float | 1 | 结束位置 0~1 |
offset |
float | 0 | 偏移量(度),360 度表示完整路径长度的一个周期。例如,180 度将裁剪范围偏移半个路径长度 |
type |
TrimType | separate | 裁剪类型(见下方) |
TrimType(裁剪类型):
| 值 | 说明 |
|---|---|
separate |
独立模式:每个形状独立裁剪,使用相同的 start/end 参数 |
continuous |
连续模式:所有形状视为一条连续路径,按总长度比例裁剪 |
边界情况:
start > end:对 start 和 end 值取镜像(start = 1 - start,end = 1 - end)并反转所有路径方向,然后执行正常裁剪。视觉效果为路径的互补段且方向相反- 支持环绕:当裁剪范围超出 [0,1] 时,自动环绕到路径另一端
- 路径总长度为 0 时,不执行任何操作
<?xml version="1.0" encoding="UTF-8"?>
<!-- Demonstrates TrimPath: separate vs continuous mode comparison -->
<pagx width="400" height="400">
<Layer width="100%" height="100%" layout="vertical" alignment="center" arrangement="spaceEvenly">
<!-- Row 1: Separate mode ellipses -->
<Layer>
<!-- Background rings -->
<Ellipse width="100" height="100"/>
<Ellipse left="160" width="100" height="100"/>
<Stroke color="#E2E8F0" width="8"/>
<!-- Trimmed: each ellipse trimmed independently to 0.2~0.9 -->
<Group>
<Ellipse width="100" height="100"/>
<Ellipse left="160" width="100" height="100"/>
<TrimPath start="0.2" end="0.9"/>
<Stroke width="10" cap="round">
<LinearGradient>
<ColorStop color="#06B6D4" offset="0"/>
<ColorStop color="#8B5CF6" offset="1"/>
</LinearGradient>
</Stroke>
</Group>
</Layer>
<!-- Row 2: Separate mode label -->
<Layer>
<Text text="Separate (0.2–0.9)" fontFamily="Arial" fontStyle="Bold" fontSize="18"/>
<Fill color="#475569"/>
</Layer>
<!-- Row 3: Continuous mode ellipses -->
<Layer>
<!-- Background rings -->
<Ellipse width="100" height="100"/>
<Ellipse left="160" width="100" height="100"/>
<Stroke color="#E2E8F0" width="8"/>
<!-- Trimmed: both ellipses treated as one path, 0.2~0.9 of total length -->
<Group>
<Ellipse width="100" height="100"/>
<Ellipse left="160" width="100" height="100"/>
<TrimPath start="0.2" end="0.9" type="continuous"/>
<Stroke width="10" cap="round">
<LinearGradient>
<ColorStop color="#F59E0B" offset="0"/>
<ColorStop color="#F43F5E" offset="1"/>
</LinearGradient>
</Stroke>
</Group>
</Layer>
<!-- Row 4: Continuous mode label -->
<Layer>
<Text text="Continuous (0.2–0.9)" fontFamily="Arial" fontStyle="Bold" fontSize="18"/>
<Fill color="#475569"/>
</Layer>
</Layer>
</pagx>
6.4.2 圆角(RoundCorner)
将路径的尖角转换为圆角。
<RoundCorner radius="10"/>
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
radius |
float | 10 | 圆角半径 |
处理规则:
- 只影响尖角(非平滑连接的顶点)
- 圆角半径自动限制为不超过相邻边长度的一半
radius <= 0时不执行任何操作
示例:
<?xml version="1.0" encoding="UTF-8"?>
<pagx width="400" height="400">
<!-- Original sharp rectangle (reference) -->
<Layer>
<Rectangle left="50" top="50" width="140" height="140"/>
<Stroke color="#94A3B840" width="2" dashes="4,4"/>
</Layer>
<Layer>
<Rectangle left="50" top="50" width="140" height="140"/>
<RoundCorner radius="30"/>
<Fill>
<LinearGradient>
<ColorStop color="#10B981" offset="0"/>
<ColorStop color="#059669" offset="1"/>
</LinearGradient>
</Fill>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#10B98160"/>
</Layer>
<!-- Original star (reference) -->
<Layer left="220" top="50">
<Polystar type="star" pointCount="5" outerRadius="70" innerRadius="32"/>
<Stroke color="#94A3B840" width="2" dashes="4,4"/>
</Layer>
<Layer left="220" top="50">
<Polystar type="star" pointCount="5" outerRadius="70" innerRadius="32"/>
<RoundCorner radius="12"/>
<Fill>
<LinearGradient>
<ColorStop color="#F59E0B" offset="0"/>
<ColorStop color="#D97706" offset="1"/>
</LinearGradient>
</Fill>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#F59E0B60"/>
</Layer>
<Layer left="120" top="220">
<Polystar type="polygon" pointCount="6" outerRadius="80"/>
<RoundCorner radius="20"/>
<Fill>
<LinearGradient>
<ColorStop color="#8B5CF6" offset="0"/>
<ColorStop color="#7C3AED" offset="1"/>
</LinearGradient>
</Fill>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#8B5CF660"/>
</Layer>
</pagx>
6.4.3 路径合并(MergePath)
将所有形状合并为单个形状。
<MergePath mode="append"/>
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
mode |
MergePathMode | append | 合并操作(见下方) |
MergePathMode(路径合并操作):
| 值 | 说明 |
|---|---|
append |
追加:简单合并所有路径,不进行布尔运算(默认) |
union |
并集:合并所有形状的覆盖区域 |
intersect |
交集:只保留所有形状的重叠区域 |
xor |
异或:保留非重叠区域 |
difference |
差集:从第一个形状中减去后续形状 |
重要行为:
- MergePath 会清空当前作用域中之前累积的所有 Fill 和 Stroke 效果,几何列表中仅保留合并后的路径
- 合并时应用各形状的当前变换矩阵
- 合并后的形状变换矩阵重置为单位矩阵
示例:
<?xml version="1.0" encoding="UTF-8"?>
<pagx width="400" height="400">
<Layer>
<Rectangle left="60" top="60" width="180" height="180" roundness="24"/>
<Ellipse left="160" top="160" width="180" height="180"/>
<MergePath mode="union"/>
<Fill>
<LinearGradient endPoint="1,1">
<ColorStop color="#6366F1" offset="0"/>
<ColorStop color="#EC4899" offset="1"/>
</LinearGradient>
</Fill>
<DropShadowStyle offsetY="12" blurX="40" blurY="40" color="#8B5CF660"/>
</Layer>
</pagx>
6.5 文本修改器(Text Modifiers)
文本修改器对文本中的独立字形进行变换。
6.5.1 文本修改器处理
遇到文本修改器时,上下文中累积的所有字形列表会汇总为一个统一的字形列表进行操作:
<TextBox left="100" top="50" width="200" height="100" textAlign="center">
<Text text="Hello " fontFamily="Arial" fontSize="24"/>
<Text text="World" fontFamily="Arial" fontSize="24"/>
<TextModifier position="0,-5"/>
<Fill color="#333333"/>
</TextBox>
6.5.2 文本转形状
当文本遇到形状修改器时,会强制转换为形状路径:
文本元素 形状修改器 后续修改器
┌──────────┐ ┌──────────┐
│ Text │ │ TrimPath │
└────┬─────┘ │RoundCorn │
│ │MergePath │
│ 累积字形列表 └────┬─────┘
▼ │
┌──────────────┐ │ 触发转换
│ 字形列表 │───────────┼──────────────────────┐
│ [H,e,l,l,o] │ │ │
└──────────────┘ ▼ ▼
┌──────────────┐ ┌──────────────────┐
│ 合并为单个 │ │ Emoji 被丢弃 │
│ Path │ │ (无法转为路径) │
└──────────────┘ └──────────────────┘
│
│ 后续文本修改器不再生效
▼
┌──────────────┐
│ TextModifier │ → 跳过(已是 Path)
└──────────────┘
转换规则:
- 触发条件:文本遇到 TrimPath、RoundCorner、MergePath 时触发转换
- 合并为单个 Path:一个 Text 的所有字形合并为一个 Path,而非每个字形产生一个独立 Path
- Emoji 丢失:Emoji 无法转换为路径轮廓,转换时被丢弃
- 不可逆转换:转换后成为纯 Path,后续的文本修改器对其无效
示例:
<Group>
<Text fontFamily="Arial" fontSize="24"><![CDATA[Hello 😀]]></Text>
<TrimPath start="0" end="0.5"/>
<TextModifier position="0,-10"/>
<Fill color="#333333"/>
</Group>
6.5.3 文本变换器(TextModifier)
对选定范围内的字形应用变换和样式覆盖。TextModifier 可包含多个 RangeSelector 子元素,用于定义不同的选择范围和影响因子。
<TextModifier anchor="0,0" position="0,0" rotation="0" scale="1,1" skew="0" skewAxis="0" alpha="1" fillColor="#FF0000" strokeColor="#000000" strokeWidth="1">
<RangeSelector start="0" end="0.5" shape="rampUp"/>
<RangeSelector start="0.5" end="1" shape="rampDown"/>
</TextModifier>
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
anchor |
Point | 0,0 | 锚点偏移,相对于字形默认锚点位置。每个字形的默认锚点位于 (advance × 0.5, 0),即字形水平中心的基线位置 |
position |
Point | 0,0 | 位置偏移 |
rotation |
float | 0 | 旋转 |
scale |
Point | 1,1 | 缩放 |
skew |
float | 0 | 倾斜角度(度),沿 skewAxis 方向应用 |
skewAxis |
float | 0 | 倾斜轴角度(度),定义倾斜的作用方向 |
alpha |
float | 1 | 透明度 |
fillColor |
Color | - | 填充颜色覆盖 |
strokeColor |
Color | - | 描边颜色覆盖 |
strokeWidth |
float | - | 描边宽度覆盖 |
选择器计算:
- 根据 RangeSelector 的
start、end、offset计算选择范围(支持任意小数值,超出 [0,1] 范围时自动环绕) - 根据
shape计算每个字形的原始影响值(0~1),然后乘以weight - 多个选择器按
mode组合,组合结果限制到 [-1, 1]
factor = clamp(combine(rawInfluence₁ × weight₁, rawInfluence₂ × weight₂, ...), -1, 1)
变换应用:
位置和旋转线性应用 factor。变换按以下顺序应用:
- 平移到锚点的负方向(
translate(-anchor × factor)) - 从单位矩阵插值缩放(
scale(1 + (scale - 1) × factor)) - 倾斜(
skew(skew × factor, skewAxis)) - 旋转(
rotate(rotation × factor)) - 平移回锚点(
translate(anchor × factor)) - 平移到位置(
translate(position × factor))
透明度使用 factor 的绝对值:
alphaFactor = 1 + (alpha - 1) × |factor|
finalAlpha = originalAlpha × max(0, alphaFactor)
颜色覆盖:
颜色覆盖使用 factor 的绝对值进行 alpha 混合:
blendFactor = overrideColor.alpha × |factor|
finalColor = blend(originalColor, overrideColor, blendFactor)
示例:
<?xml version="1.0" encoding="UTF-8"?>
<pagx width="400" height="400">
<Layer width="100%" height="100%" layout="vertical" alignment="center" arrangement="spaceEvenly">
<!-- Wave effect: triangle selector shifts characters vertically -->
<Layer>
<Text text="WAVE" fontFamily="Arial" fontStyle="Bold" fontSize="72"/>
<TextModifier position="0,-30">
<RangeSelector start="0" end="1" shape="triangle"/>
</TextModifier>
<Fill>
<LinearGradient startPoint="-200,0" endPoint="80,0" fitsToGeometry="false">
<ColorStop color="#06B6D4" offset="0"/>
<ColorStop color="#3B82F6" offset="1"/>
</LinearGradient>
</Fill>
</Layer>
<!-- Rotation effect: each character rotated with rampUp selector -->
<Layer>
<Text text="ROTATE" fontFamily="Arial" fontStyle="Bold" fontSize="56"/>
<TextModifier rotation="30">
<RangeSelector start="0" end="1" shape="rampUp"/>
</TextModifier>
<Fill>
<LinearGradient startPoint="-200,0" endPoint="80,0" fitsToGeometry="false">
<ColorStop color="#F59E0B" offset="0"/>
<ColorStop color="#F43F5E" offset="1"/>
</LinearGradient>
</Fill>
</Layer>
<!-- Color override effect: characters with gradient color override -->
<Layer>
<Text text="COLOR" fontFamily="Arial" fontStyle="Bold" fontSize="64"/>
<TextModifier fillColor="#EC4899">
<RangeSelector start="0" end="1" shape="triangle"/>
</TextModifier>
<Fill color="#8B5CF6"/>
</Layer>
</Layer>
</pagx>
6.5.4 范围选择器(RangeSelector)
范围选择器定义 TextModifier 影响的字形范围和影响程度。
<RangeSelector start="0" end="1" offset="0" unit="percentage" shape="square" easeIn="0" easeOut="0" mode="add" weight="1" randomOrder="false" randomSeed="0"/>
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
start |
float | 0 | 选择起始 |
end |
float | 1 | 选择结束 |
offset |
float | 0 | 选择偏移 |
unit |
SelectorUnit | percentage | 单位(见下方) |
shape |
SelectorShape | square | 形状(见下方) |
easeIn |
float | 0 | 缓入量 |
easeOut |
float | 0 | 缓出量 |
mode |
SelectorMode | add | 组合模式(见下方) |
weight |
float | 1 | 选择器权重 |
randomOrder |
bool | false | 随机顺序 |
randomSeed |
int | 0 | 随机种子 |
SelectorUnit(单位):
| 值 | 说明 |
|---|---|
index |
索引:按字形索引计算范围 |
percentage |
百分比:按字形总数的百分比计算范围 |
SelectorShape(形状):
| 值 | 说明 |
|---|---|
square |
矩形:范围内为 1,范围外为 0 |
rampUp |
上升斜坡:从 0 线性增加到 1 |
rampDown |
下降斜坡:从 1 线性减少到 0 |
triangle |
三角形:中心为 1,两端为 0 |
round |
圆形:正弦曲线过渡 |
smooth |
平滑:更平滑的过渡曲线 |
SelectorMode(组合模式):
| 值 | 说明 |
|---|---|
add |
相加:result = a + b |
subtract |
相减:result = b ≥ 0 ? a × (1 − b) : a × (−1 − b) |
intersect |
交集:result = a × b |
min |
最小:result = min(a, b) |
max |
最大:result = max(a, b) |
difference |
差值:`result = |
6.5.5 路径文本(TextPath)
将文本沿指定路径排列。路径可以通过引用 Resources 中定义的 PathData,也可以内联路径数据。TextPath 使用 基线(由 baselineOrigin 和 baselineAngle 定义的直线)作为文本的参考线:字形从基线上的位置映射到路径曲线上 的对应位置,保持相对间距和偏移。当 forceAlignment 启用时,忽略原始字形位置,将字形均匀分布以填满可用路径长度。
<?xml version="1.0" encoding="UTF-8"?>
<pagx width="400" height="400">
<Layer>
<Text text="PAGX" fontFamily="Arial" fontStyle="Bold" fontSize="56"/>
<TextPath path="@curve" forceAlignment="true"/>
<Fill>
<LinearGradient>
<ColorStop color="#06B6D4" offset="0"/>
<ColorStop color="#8B5CF6" offset="0.5"/>
<ColorStop color="#EC4899" offset="1"/>
</LinearGradient>
</Fill>
<Group>
<Path data="@curve"/>
<Stroke color="#64748B" width="1" dashes="6,4"/>
</Group>
<Group>
<Text text="TextPath" fontFamily="Arial" fontStyle="Bold" fontSize="28"/>
<TextPath path="M 80,320 Q 200,250 320,320" forceAlignment="true"/>
<Fill color="#475569"/>
</Group>
</Layer>
<Resources>
<PathData id="curve" data="M 60,230 Q 200,30 340,230"/>
</Resources>
</pagx>
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
path |
string/idref | (必填) | SVG 路径数据或 PathData 资源引用 "@id" |
baselineOrigin |
Point | 0,0 | 基线原点,文本参考线的起点坐标 |
baselineAngle |
float | 0 | 基线角度(度),0 为水平,90 为垂直 |
firstMargin |
float | 0 | 起始边距 |
lastMargin |
float | 0 | 结束边距 |
perpendicular |
bool | true | 垂直于路径 |
reversed |
bool | false | 反转方向 |
forceAlignment |
bool | false | 强制拉伸文本填满路径 |
基线:
baselineOrigin:基线的起点坐标,相对于 TextPath 的本地坐标空间baselineAngle:基线的角度(度数),0 表示水平基线(文本从左到右沿 X 轴),90 表示垂直基线(文本从上到下沿 Y 轴)- 字形沿基线的距离决定其在曲线上的位置,字形垂直于基线的偏移量保持为垂直于曲线的偏移量
边距:
firstMargin:起点边距(从路径起点向内偏移)lastMargin:终点边距(从路径终点向内偏移)
强制对齐:
- 当
forceAlignment="true"时,字形按其推进宽度依次排列,然后按比例调整间距以填满 firstMargin 和 lastMargin 之间的路径区域
字形定位:
- 计算字形中心在路径上的位置
- 获取该位置的路径切线方向
- 如果
perpendicular="true",旋转字形使其垂直于路径
闭合路径:对于闭合路径,超出范围的字形会环绕到路径另一端。
6.5.6 文本框(TextBox)
TextBox 是文本框排版器,对其内部的 Text 元素应用排版。它根据自身的排版尺寸(width/height)和对齐设置重新排版所有字形位置,排版结果通过反向变换补偿写入每个 Text 元素的 GlyphRun 数据,因此 Text 自身的 position 和父级 Group 变换在渲染管线中仍然有效。首行使用行框模型定位:行框近端贴齐文本区域近端边缘,基线位于近端下方 halfLeading + ascent 处,其中 halfLeading = (lineHeight - metricsHeight) / 2,metricsHeight = ascent + descent + leading。遵循 CSS Writing Modes 的惯例,lineHeight 是逻辑属性,始终作用于行框的块轴方向尺寸。竖排模式下,它控制的是列宽而非行高。列间距为 lineHeight(中心到中心的距离)。当 lineHeight 为 0(自动)时,列宽根据字体 metrics 计算(ascent + descent + leading),与横排自动行高的算法一致。列从右往左排列。
TextBox 是仅参与预排版的节点:它在渲染前的排版阶段被处理,不会在渲染树中实例化。如果内部所有 Text 元素都已包含嵌入的 GlyphRun 数据,则排版阶段会跳过 TextBox。但即使已填写嵌入的 GlyphRun 数据和字体,仍建议保留 TextBox 节点,因为设计工具导入时需要读取其排版属性(width、height、textAlign、paragraphAlign、lineHeight、wordWrap、overflow 等)用于编辑展示。
作为容器,TextBox 在独立的作用域中处理其内部的 Text 元素和文本修改器(TextModifier、TextPath 等)。TextBox 内的 Text 元素首先按 TextBox 的排版设置完成排版;TextBox 内的后续文本修改器在排版结果的基础上工作。TextBox 只影响 Text 元素的初始排版——它在文本修改器链开始之前确定字形位置。
<?xml version="1.0" encoding="UTF-8"?>
<!-- Demonstrates TextBox: word wrap, text alignment, vertical writing, paragraph alignment -->
<pagx width="400" height="400">
<Layer name="TextBoxGrid" width="400" height="400" layout="vertical" gap="12" padding="14">
<Layer height="180" layout="horizontal" gap="12">
<!-- Box 1 (top-left): horizontal justify + lineHeight -->
<Layer flex="1">
<Rectangle width="180" height="180" roundness="6"/>
<Stroke color="#CBD5E1" width="1" dashes="4,3"/>
<TextBox width="180" height="180" textAlign="justify" lineHeight="28">
<Text text="Justify aligns words evenly 分布在 each full line." fontFamily="Arial" fontSize="18"/>
<Fill>
<LinearGradient>
<ColorStop color="#6366F1" offset="0"/>
<ColorStop color="#8B5CF6" offset="1"/>
</LinearGradient>
</Fill>
</TextBox>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#6366F130"/>
</Layer>
<!-- Box 2 (top-right): horizontal textAlign center + paragraphAlign middle -->
<Layer flex="1">
<Rectangle width="180" height="180" roundness="6"/>
<Stroke color="#CBD5E1" width="1" dashes="4,3"/>
<TextBox width="180" height="180" textAlign="center" paragraphAlign="middle">
<Text text="Centered Horizontally & Vertically" fontFamily="Arial" fontStyle="Bold" fontSize="18"/>
<Fill>
<LinearGradient>
<ColorStop color="#F43F5E" offset="0"/>
<ColorStop color="#EC4899" offset="1"/>
</LinearGradient>
</Fill>
</TextBox>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#F43F5E30"/>
</Layer>
</Layer>
<Layer height="180" layout="horizontal" gap="12">
<!-- Box 3 (bottom-left): vertical writing -->
<Layer flex="1">
<Rectangle width="180" height="180" roundness="6"/>
<Stroke color="#CBD5E1" width="1" dashes="4,3"/>
<TextBox width="180" height="180" textAlign="justify" writingMode="vertical">
<Text text="春眠不觉 Dawn 处处闻啼鸟 Night 来风雨声花落知多少" fontFamily="Arial" fontStyle="Bold" fontSize="18"/>
<Fill>
<LinearGradient startPoint="1,0" endPoint="0,1">
<ColorStop color="#F59E0B" offset="0"/>
<ColorStop color="#F43F5E" offset="1"/>
</LinearGradient>
</Fill>
</TextBox>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#F59E0B30"/>
</Layer>
<!-- Box 4 (bottom-right): vertical textAlign center + paragraphAlign middle -->
<Layer flex="1">
<Rectangle width="180" height="180" roundness="6"/>
<Stroke color="#CBD5E1" width="1" dashes="4,3"/>
<TextBox width="180" height="180" textAlign="center" paragraphAlign="middle" writingMode="vertical">
<Text text="竖排文本 居中对齐" fontFamily="Arial" fontStyle="Bold" fontSize="18"/>
<Fill>
<LinearGradient>
<ColorStop color="#06B6D4" offset="0"/>
<ColorStop color="#10B981" offset="1"/>
</LinearGradient>
</Fill>
</TextBox>
<DropShadowStyle offsetY="8" blurX="24" blurY="24" color="#06B6D430"/>
</Layer>
</Layer>
</Layer>
</pagx>
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
textAlign |
TextAlign | start | 文本对齐——沿行内方向对齐文本(见下方) |
paragraphAlign |
ParagraphAlign | near | 段落对齐——沿块流方向对齐文本行/列(见下方) |
writingMode |
WritingMode | horizontal | 排版方向(见下方) |
lineHeight |
float | 0 | 行高(像素值)。0 表示自动(根据字体 metrics 计算:ascent + descent + leading)。遵循 CSS Writing Modes 的逻辑属性惯例,竖排模式下控制列宽 |
wordWrap |
bool | true | 是否启用自动换行,在盒子宽度边界(横排)或高度边界(竖排)处换行。当该维度为 NaN 时无效果 |
overflow |
Overflow | visible | 文本超出盒子高度(横排)或宽度(竖排)时的溢出行为。当该维度为 NaN 时无效果 |
TextBox 继承 Group 的所有属性(position、anchor、rotation、scale、skew、skewAxis、alpha、padding)和约束属性(见 §4.3)。TextBox 的 width/height 默认为 NaN(无边界,自动尺寸);NaN 表示该维度上无边界,可能导致 wordWrap 或 overflow 无效果。padding 属性内缩文本排版区域和非 Text 子元素的约束参考系。position 属性指定文本区域左上角在父坐标系中的坐标。推荐使用约束属性(left/top)进行定位——设置约束属性时,position 由约束系统自动计算。
TextAlign(文本对齐):
| 值 | 说明 |
|---|---|
start |
起始对齐 |
center |
居中对齐 |
end |
结束对齐 |
justify |
两端对齐(最后一行起始对齐) |
ParagraphAlign(段落对齐):
沿块流方向(block-flow direction)对齐文本行或列。使用方向中立的 Near/Far 而非 Top/Bottom,在横排和竖排模式下语义一致。横排模式下控制垂直定位,竖排模式下控制水平定位。
| 值 | 说明 |
|---|---|
near |
近端对齐(横排时为顶部,竖排时为右侧)。使用行框模型,首行行框近端贴齐文本区域近端边缘。基线位于近端下方 halfLeading + ascent 处,其中 halfLeading = (lineHeight - metricsHeight) / 2。 |
middle |
居中对齐。整体文本块尺寸(所有行高/列宽之和)在对应维度内居中。 |
far |
远端对齐(横排时为底部,竖排时为左侧)。末行行框远端对齐文本区域远端边缘。 |
WritingMode(排版方向):
| 值 | 说明 |
|---|---|
horizontal |
横排文本 |
vertical |
竖排文本(列从右到左排列,传统中日文竖排) |
Overflow(溢出行为):
| 值 | 说明 |
|---|---|
visible |
超出盒子边界的文本仍然渲染(默认) |
hidden |
超出盒子高度(横排)或宽度(竖排)的整行/整列被丢弃,不会显示被截断的半行/半列。当该维度为 NaN 时无效果 |
6.5.7 富文本
富文本通过 TextBox 内的多个 Text 元素组合,每个 Text 可以包裹在 Group 中拥有独立的 Fill/Stroke 样式。TextBox 提供统一排版。
<?xml version="1.0" encoding="UTF-8"?>
<!-- Demonstrates rich text with mixed styles -->
<pagx width="400" height="400">
<Layer width="100%" height="100%">
<TextBox width="100%" height="100%" textAlign="center" paragraphAlign="middle" lineHeight="48">
<Text text="Rich Text " fontFamily="Arial" fontStyle="Bold" fontSize="42"/>
<Fill>
<LinearGradient startPoint="0.25,0" endPoint="0.75,0">
<ColorStop color="#8B5CF6" offset="0"/>
<ColorStop color="#EC4899" offset="1"/>
</LinearGradient>
</Fill>
<Group>
<Text text="Supports " fontFamily="Arial" fontSize="20"/>
<Fill color="#475569"/>
</Group>
<Group>
<Text text="bold" fontFamily="Arial" fontStyle="Bold" fontSize="20"/>
<Fill color="#F43F5E"/>
</Group>
<Group>
<Text text=", " fontFamily="Arial" fontSize="20"/>
<Fill color="#475569"/>
</Group>
<Group>
<Text text="italic" fontFamily="Arial" fontStyle="Italic" fontSize="20"/>
<Fill color="#10B981"/>
</Group>
<Group>
<Text text=" and " fontFamily="Arial" fontSize="20"/>
<Fill color="#475569"/>
</Group>
<Group>
<Text text="colored" fontFamily="Arial" fontStyle="Bold" fontSize="20"/>
<Fill color="#06B6D4"/>
</Group>
<Group>
<Text text=" text. " fontFamily="Arial" fontSize="20"/>
<Text text="Mix " fontFamily="Arial" fontSize="16"/>
<Fill color="#475569"/>
</Group>
<Group>
<Text text="different" fontFamily="Arial" fontStyle="Bold" fontSize="24"/>
<Fill color="#F59E0B"/>
</Group>
<Group>
<Text text=" sizes " fontFamily="Arial" fontSize="16"/>
<Fill color="#475569"/>
</Group>
<Group>
<Text text="freely" fontFamily="Arial" fontStyle="Bold Italic" fontSize="24"/>
<Fill color="#8B5CF6"/>
</Group>
<Group>
<Text text=" within one line. " fontFamily="Arial" fontSize="16"/>
<Text text="Center " fontFamily="Arial" fontSize="20"/>
<Fill color="#475569"/>
</Group>
<Group>
<Text text="aligned" fontFamily="Arial" fontStyle="Bold" fontSize="20"/>
<Fill color="#EC4899"/>
</Group>
<Group>
<Text text=" rich text. " fontFamily="Arial" fontSize="20"/>
<Fill color="#475569"/>
</Group>
<Group>
<Text text="Gradient Fill" fontFamily="Arial" fontStyle="Bold" fontSize="28"/>
<Fill>
<LinearGradient>
<ColorStop color="#F43F5E" offset="0"/>
<ColorStop color="#F59E0B" offset="0.5"/>
<ColorStop color="#10B981" offset="1"/>
</LinearGradient>
</Fill>
</Group>
</TextBox>
</Layer>
</pagx>
说明:每个 Group 内的 Text + Fill/Stroke 定义一段样式独立的文本片段,TextBox 将所有片段作为子元素进行统一排版,实现自动换行和对齐。
6.6 复制器(Repeater)
复制累积的内容和已渲染的样式,对每个副本应用渐进变换。Repeater 对 Path 和字形列表同时生效,且不会触发文本转形状。
<Repeater copies="5" offset="1" order="belowOriginal" anchor="0,0" position="50,0" rotation="0" scale="1,1" startAlpha="1" endAlpha="0.2"/>
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
copies |
float | 3 | 副本数 |
offset |
float | 0 | 起始偏移 |
order |
RepeaterOrder | belowOriginal | 堆叠顺序(见下方) |
anchor |
Point | 0,0 | 锚点 |
position |
Point | 100,100 | 每个副本的位置偏移 |
rotation |
float | 0 | 每个副本的旋转 |
scale |
Point | 1,1 | 每个副本的缩放 |
startAlpha |
float | 1 | 首个副本透明度 |
endAlpha |
float | 1 | 末个副本透明度 |
变换计算(第 i 个副本,i 从 0 开始):
progress = i + offset
变换按以下顺序应用:
- 平移到锚点的负方向(
translate(-anchor)) - 指数缩放(
scale(scale^progress)) - 线性旋转(
rotate(rotation × progress)) - 线性位移(
translate(position × progress)) - 平移回锚点(
translate(anchor))
透明度插值:
maxCount = ceil(copies)
t = progress / maxCount
alpha = lerp(startAlpha, endAlpha, t)
// 最后一个副本的 alpha 还需乘以 copies 的小数部分(见下文)
RepeaterOrder(堆叠顺序):
| 值 | 说明 |
|---|---|
belowOriginal |
副本在原件下方。索引 0 在最上 |
aboveOriginal |
副本在原件上方。索引 N-1 在最上 |
小数副本数:
当 copies 为小数时(如 3.5),采用叠加半透明的方式实现部分副本效果:
- 几何复制:形状和文本几何按
ceil(copies)个复制(即 4 个),几何本身不做缩放或裁剪 - 透明度调整:最后一个副本的透明度乘以小数部分(如 0.5),产生半透明效果
- 视觉效果:通过透明度渐变模拟"部分存在"的副本
示例:copies="2.3" 时
- 复制 3 个完整的几何副本
- 第 1、2 个副本正常渲染
- 第 3 个副本透明度 × 0.3,呈现半透明效果
边界情况:
copies < 0:不执行任何操作copies = 0:清空所有累积的内容和已渲染的样式
Repeater 特性:
- 同时作用:复制所有累积的 Path 和字形列表
- 保留文本属性:字形列表复制后仍保留字形信息,后续文本修改器仍可作用
- 复制已渲染样式:同时复制已渲染的填充和描边
<?xml version="1.0" encoding="UTF-8"?>
<pagx width="400" height="400">
<Layer>
<Rectangle left="32" top="172" width="56" height="56" roundness="12"/>
<Fill>
<LinearGradient>
<ColorStop color="#6366F1" offset="0"/>
<ColorStop color="#8B5CF6" offset="1"/>
</LinearGradient>
</Fill>
<Repeater copies="5" position="70,0" endAlpha="0.15"/>
</Layer>
</pagx>
6.7 容器(Group)
Group 是带变换属性的矢量元素容器。
<?xml version="1.0" encoding="UTF-8"?>
<pagx width="400" height="400">
<Layer>
<Group left="200" top="200" anchor="110,110" rotation="12">
<Rectangle width="220" height="220" roundness="32"/>
<Fill>
<LinearGradient>
<ColorStop color="#F43F5E" offset="0"/>
<ColorStop color="#EC4899" offset="0.5"/>
<ColorStop color="#8B5CF6" offset="1"/>
</LinearGradient>
</Fill>
</Group>
<DropShadowStyle offsetY="12" blurX="40" blurY="40" color="#EC489960"/>
</Layer>
</pagx>
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
anchor |
Point | 0,0 | 锚点 "x,y" |
position |
Point | 0,0 | 位置 "x,y",设置约束属性时由约束系统自动计算。推荐使用约束属性(left/top)进行定位 |
rotation |
float | 0 | 旋转角度 |
scale |
Point | 1,1 | 缩放 "sx,sy" |
skew |
float | 0 | 倾斜量 |
skewAxis |
float | 0 | 倾斜轴角度 |
alpha |
float | 1 | 透明度 0~1 |
padding |
float 或 "t,r,b,l" | 0 | 内缩子元素的约束参考系。支持单值(四边均匀)、两值(垂直,水平)、四值(上,右,下,左) |
Group 支持所有约束属性(见 §4.3)。
变换顺序
变换按以下顺序应用:
- 平移到锚点的负方向(
translate(-anchor)) - 缩放(
scale) - 倾斜(
skew沿skewAxis方向) - 旋转(
rotation) - 平移到位置(
translate(position))
倾斜变换:
倾斜变换按以下顺序应用:
- 旋转到倾斜轴方向(
rotate(skewAxis)) - 沿 X 轴剪切(
shearX(tan(skew))) - 旋转回原方向(
rotate(-skewAxis))
作用域隔离
Group 创建独立的作用域,用于隔离几何累积和渲染:
- 组内的几何元素只在组内累积
- 组内的绘制器只渲染组内累积的几何
- 组内的修改器只影响组内累积的几何
- 组的变换矩阵应用到组内所有内容
- 组的
alpha属性应用到组内所有渲染内容
几何累积规则:
- 绘制器不清空几何:Fill 和 Stroke 渲染后,几何列表保持不变,后续绘制器仍可渲染相同的几何
- 子 Group 几何向上累积:子 Group 处理完成后,其几何会累积到父作用域,父级末尾的绘制器可以渲染所有子 Group 的几何
- 同级 Group 互不影响:每个 Group 创建独立的累积起点,不会看到后续兄弟 Group 的几何
- 隔离渲染范围:Group 内的绘制器只能渲染到当前位置已累积的几何,包括本组和已完成的子 Group
- Layer 是累积终止点:几何向上累积直到遇到 Layer 边界,不会跨 Layer 传递
示例 1 - 基本隔离:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Demonstrates Group isolation: alpha applied to group, not individual shapes -->
<pagx width="400" height="400">
<Layer>
<Group alpha="0.7">
<Rectangle left="40" top="100" width="200" height="200" roundness="24"/>
<Fill color="#F43F5E"/>
</Group>
<Ellipse left="160" top="100" width="200" height="200"/>
<Fill color="#06B6D4"/>
</Layer>
</pagx>
示例 2 - 子 Group 几何向上累积:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Demonstrates style propagation: Fill outside Groups applies to all shapes -->
<pagx width="400" height="400">
<Layer>
<Rectangle left="60" top="120" width="160" height="160" roundness="24"/>
<Fill color="#F43F5E"/>
<Group>
<Ellipse left="180" top="120" width="160" height="160"/>
<Fill color="#06B6D4"/>
</Group>
<!-- Semi-transparent overlay applied to both shapes -->
<Fill color="#8B5CF630"/>
</Layer>
</pagx>
多重填充与描边
由于绘制器不清空几何列表,同一几何可连续应用多个 Fill 和 Stroke。
示例 1 - 多重填充:
<?xml version="1.0" encoding="UTF-8"?>
<pagx width="400" height="400">
<Layer>
<Rectangle left="50" top="100" width="300" height="200" roundness="32"/>
<Fill>
<LinearGradient>
<ColorStop color="#F59E0B" offset="0"/>
<ColorStop color="#F43F5E" offset="1"/>
</LinearGradient>
</Fill>
<Fill color="#8B5CF640"/>
<DropShadowStyle offsetY="12" blurX="40" blurY="40" color="#F43F5E50"/>
</Layer>
</pagx>
示例 2 - 多重描边:
<?xml version="1.0" encoding="UTF-8"?>
<!-- Demonstrates multiple Stroke styles creating glow/outline effect -->
<pagx width="400" height="400">
<Layer>
<Path data="M 60,200 Q 200,60 340,200 Q 200,340 60,200"/>
<!-- Outer glow -->
<Stroke color="#8B5CF615" width="48" cap="round" join="round"/>
<!-- Middle glow -->
<Stroke color="#8B5CF640" width="28" cap="round" join="round"/>
<!-- Core stroke -->
<Stroke width="8" cap="round" join="round">
<LinearGradient>
<ColorStop color="#6366F1" offset="0"/>
<ColorStop color="#8B5CF6" offset="0.5"/>
<ColorStop color="#EC4899" offset="1"/>
</LinearGradient>
</Stroke>
</Layer>
</pagx>
渲染顺序:多个绘制器按文档顺序渲染,先出现的位于下方。
7. 导入指令(Import Directives)
导入指令用于将外部内容(目前支持 SVG)嵌入到 PAGX 文件中。它们不会被直接渲染 —— 必须通过
pagx resolve 解析为原生 PAGX 节点后,文件才能被渲染或验证。
7.1 内联 SVG(Inline SVG)
<Layer> 可以直接包含一个 <svg> 元素作为子节点。解析器只读取 <svg> 根节点,将其视为
不透明的外部内容 —— <svg> 的子节点不会被展开或作为 PAGX 节点验证。
<Layer id="shareIcon" centerX="0" centerY="0">
<svg viewBox="0 0 24 24">
<path d="M4 12v8a2 2 0 002 2h12a2 2 0 002-2v-8" fill="none"
stroke="#7F8C8D" stroke-width="1.5" stroke-linecap="round"/>
<polyline points="16,6 12,2 8,6" fill="none"
stroke="#7F8C8D" stroke-width="1.5" stroke-linecap="round"
stroke-linejoin="round"/>
<line x1="12" y1="2" x2="12" y2="15" fill="none"
stroke="#7F8C8D" stroke-width="1.5" stroke-linecap="round"/>
</svg>
</Layer>
7.2 外部导入(External Import)
Layer 的 import 属性引用要导入的外部文件。格式从文件扩展名推断。当扩展名不明确时,
可以使用 importFormat 属性显式指定格式。
属性
| 属性 | 类型 | 默认值 | 必需 | 说明 |
|---|---|---|---|---|
import |
string | — | 否 | 外部文件路径,相对于 PAGX 文件所在位置。 |
importFormat |
string | — | 否 | 强制指定输入格式(如 svg)。省略时从 import 文件扩展名推断。 |
<Layer id="logoIcon" centerX="0" centerY="0" import="assets/logo.svg"/>
<!-- 扩展名不明确时显式指定格式 -->
<Layer id="icon" centerX="0" centerY="0" import="assets/drawing.xml" importFormat="svg"/>
7.3 解析(Resolution)
pagx resolve 命令处理 PAGX 文件中所有导入指令:
- 对于每个包含内联
<svg>子节点或import属性的 Layer,读取内容(内联元素或外部文件) - 将 SVG 内容转换为原生 PAGX 节点(如 SVG 元素转为 Rectangle、Ellipse、Path、Fill、Stroke、 Group 节点)
- 替换
<svg>元素或移除import/importFormat属性,并将转换后的节点插入 Layer 的子节点 - 如果 Layer 已显式设置了
width和height,内容将等比缩放以适应这些尺寸(居中,保持宽高比)。 如果 Layer 未设置显式尺寸,则从源文件尺寸设置width和height(如 SVG 的viewBox或width/height属性) - 在 Layer 的子节点中插入注释以标注原始来源:
- 内联 SVG:
<!-- Resolved from: inline svg --> - 外部文件:
<!-- Resolved from: assets/logo.svg -->
- 内联 SVG:
解析完成后,文件仅包含原生 PAGX 节点 —— 不再有 <svg> 元素或 import 属性。
工具行为
处理 PAGX 文件的工具对未解析的导入指令的处理方式:
pagx verify:自动解析所有导入后再进行检查。如果解析失败,报告错误。pagx render:报告错误 ——unresolved import,拒绝渲染。
附录 A. 节点层级与包含关系(Node Hierarchy)
本附录描述节点的分类和嵌套规则。
A.1 节点分类
| 分类 | 节点 | 说明 |
|---|---|---|
| 结构节点 | pagx, Resources |
pagx:文档入口(子节点仅限:<Layer>、<Resources>)。Resources:存放可复用定义(Image、PathData、Composition、Font 等) |
| 内容容器 | Layer |
接纳 VectorElement、子 Layer、样式和滤镜。 |
| 元素容器 | Group, TextBox |
仅接纳 VectorElement。 |
| 资源类型 | Image, PathData, Composition, Font, Glyph |
存储在 <Resources> 中的可复用资源。 |
| 颜色源 | SolidColor, LinearGradient, RadialGradient, ConicGradient, DiamondGradient, ImagePattern, ColorStop |
绘制器使用的颜色定义。 |
| 图层样式 | DropShadowStyle, InnerShadowStyle, BackgroundBlurStyle |
应用于图层内容的视觉效果。 |
| 图层滤镜 | BlurFilter, DropShadowFilter, InnerShadowFilter, BlendFilter, ColorMatrixFilter |
应用于合成图层的后期处理效果。 |
| 几何元素 | Rectangle, Ellipse, Polystar, Path, Text, GlyphRun |
可绘制的几何(形状和文本)。必须在 Layer/Group 内。 |
| 修改器 | TrimPath, RoundCorner, MergePath, TextModifier, RangeSelector, TextPath, Repeater |
变换或组合几何图形和文本。 |
| 绘制器 | Fill, Stroke |
对几何图形应用颜色/渐变。必须在 Layer/Group 内。 |
| 导入指令 | (内联 <svg>、import 属性) |
导入指令。Layer 的内联 <svg> 子元素和 import/importFormat 属性通过 pagx resolve 解析为原生 PAGX 节点。 |
A.2 文档包含关系
根节点 <pagx> 仅接受 <Layer> 和 <Resources> 作为直接子节点。几何元素、绘制器等其他元素必须嵌套在 <Layer> 内。
pagx(必需属性:width、height)
├── Layer* ← 直接子节点仅能是 Layer
│ ├── VectorElement*(见 A.3)
│ ├── <svg>*(导入指令,见 §7)
│ ├── DropShadowStyle*
│ ├── InnerShadowStyle*
│ ├── BackgroundBlurStyle*
│ ├── BlurFilter*
│ ├── DropShadowFilter*
│ ├── InnerShadowFilter*
│ ├── BlendFilter*
│ ├── ColorMatrixFilter*
│ └── Layer*(子图层,递归)
│
└── Resources(可选,可复用定义)
├── Image
├── PathData
├── SolidColor
├── LinearGradient → ColorStop*
├── RadialGradient → ColorStop*
├── ConicGradient → ColorStop*
├── DiamondGradient → ColorStop*
├── ImagePattern
├── Font → Glyph*
└── Composition → Layer* ← Composition 的根节点子级也仅能是 Layer
关键规则:
<pagx>直接子节点:仅<Layer>和<Resources>- VectorElement(Rectangle、Ellipse、Path、Text 等)必须在
<Layer>或<Group>内 - 绘制器(Fill、Stroke)必须在
<Layer>或<Group>内 <Group>或几何元素作为<pagx>直接子节点会导致解析错误<Composition>的根节点子级遵循相同规则:仅允许<Layer>
A.3 VectorElement 包含关系
Layer 和 Group 可包含以下 VectorElement:
Layer / Group
├── Rectangle
├── Ellipse
├── Polystar
├── Path
├── Text → GlyphRun*(预排版模式)
├── Fill(可内嵌颜色源)
│ └── SolidColor / LinearGradient / RadialGradient / ConicGradient / DiamondGradient / ImagePattern
├── Stroke(可内嵌颜色源)
│ └── SolidColor / LinearGradient / RadialGradient / ConicGradient / DiamondGradient / ImagePattern
├── TrimPath
├── RoundCorner
├── MergePath
├── TextModifier → RangeSelector*
├── TextPath
├── TextBox
├── Repeater
└── Group*(递归)
此外,Layer(不含 Group)可包含 <svg> 作为导入指令(见 §7)。
附录 B. 枚举类型(Enumeration Types)
图层相关
| 枚举 | 值 |
|---|---|
| BlendMode | normal, multiply, screen, overlay, darken, lighten, colorDodge, colorBurn, hardLight, softLight, difference, exclusion, hue, saturation, color, luminosity, plusLighter, plusDarker |
| MaskType | alpha, luminance, contour |
| TileMode | clamp, repeat, mirror, decal |
| FilterMode | nearest, linear |
| MipmapMode | none, nearest, linear |
| ScaleMode | none, stretch, letterBox, zoom |
| LayoutMode | none, horizontal, vertical |
| Alignment | start, center, end, stretch |
| Arrangement | start, center, end, spaceBetween, spaceEvenly, spaceAround |
绘制器相关
| 枚举 | 值 |
|---|---|
| FillRule | winding, evenOdd |
| LineCap | butt, round, square |
| LineJoin | miter, round, bevel |
| StrokeAlign | center, inside, outside |
| LayerPlacement | background, foreground |
几何元素相关
| 枚举 | 值 |
|---|---|
| PolystarType | polygon, star |
修改器相关
| 枚举 | 值 |
|---|---|
| TrimType | separate, continuous |
| MergePathMode | append, union, intersect, xor, difference |
| SelectorUnit | index, percentage |
| SelectorShape | square, rampUp, rampDown, triangle, round, smooth |
| SelectorMode | add, subtract, intersect, min, max, difference |
| TextAlign | start, center, end, justify |
| TextAnchor | start, center, end |
| TextBaseline | lineBox, alphabetic |
| ParagraphAlign | near, middle, far |
| WritingMode | horizontal, vertical |
| RepeaterOrder | belowOriginal, aboveOriginal |
| Overflow | visible, hidden |
附录 C. 常见用法示例(Examples)
C.1 完整示例
以下示例涵盖 PAGX 的所有主要节点类型,展示完整的文档结构。
<?xml version="1.0" encoding="UTF-8"?>
<pagx width="800" height="520">
<Layer name="background" width="100%" height="100%">
<Rectangle width="100%" height="100%"/>
<Fill>
<LinearGradient>
<ColorStop color="#0F172A" offset="0"/>
<ColorStop color="#1E293B" offset="1"/>
</LinearGradient>
</Fill>
</Layer>
<Layer name="GlowTopLeft" blendMode="screen">
<Ellipse left="-80" top="-100" width="320" height="320"/>
<Fill>
<RadialGradient>
<ColorStop color="#8B5CF660" offset="0"/>
<ColorStop color="#8B5CF600" offset="1"/>
</RadialGradient>
</Fill>
<BlurFilter blurX="30" blurY="30"/>
</Layer>
<Layer name="GlowBottomRight" blendMode="screen">
<Ellipse left="520" top="260" width="400" height="400"/>
<Fill>
<RadialGradient>
<ColorStop color="#06B6D450" offset="0"/>
<ColorStop color="#06B6D400" offset="1"/>
</RadialGradient>
</Fill>
<BlurFilter blurX="40" blurY="40"/>
</Layer>
<Layer name="Title" width="800">
<Text centerX="0" text="PAGX" fontFamily="Arial" fontStyle="Bold" fontSize="56"/>
<Fill color="#06B6D4"/>
</Layer>
<Layer name="Subtitle">
<Text text="Portable Animated Graphics XML" fontFamily="Arial" fontSize="14"/>
<TextPath path="M 220,135 Q 400,65 580,135" forceAlignment="true"/>
<Fill color="#64748B"/>
</Layer>
<Layer name="Cards" left="110" top="155" width="580" height="80" layout="horizontal" gap="20">
<Layer name="Card1" width="100">
<Layer composition="@cardBg"/>
<Layer left="50" top="40">
<Rectangle left="-25" top="-17.5" width="50" height="35" roundness="8"/>
<Fill color="@coral"/>
<DropShadowStyle offsetY="4" blurX="12" blurY="12" color="#F43F5E80"/>
</Layer>
</Layer>
<Layer name="Card2" width="100">
<Layer composition="@cardBg"/>
<Layer left="50" top="40">
<Ellipse left="-25" top="-17.5" width="50" height="35"/>
<Fill color="@purple"/>
<InnerShadowStyle offsetX="2" offsetY="2" blurX="6" blurY="6" color="#00000080"/>
</Layer>
</Layer>
<Layer name="Card3" width="100">
<Layer composition="@cardBg"/>
<Layer left="50" top="40">
<Polystar left="-24" top="-24" type="star" pointCount="5" outerRadius="24" innerRadius="11"/>
<Fill color="@amber"/>
<DropShadowStyle offsetY="4" blurX="12" blurY="12" color="#F59E0B80"/>
</Layer>
</Layer>
<Layer name="Card4" width="100">
<Layer composition="@cardBg"/>
<Layer left="50" top="40">
<Polystar left="-26" top="-26" type="polygon" pointCount="6" outerRadius="26"/>
<Fill>
<ConicGradient>
<ColorStop color="#F43F5E" offset="0"/>
<ColorStop color="#8B5CF6" offset="0.25"/>
<ColorStop color="#06B6D4" offset="0.5"/>
<ColorStop color="#10B981" offset="0.75"/>
<ColorStop color="#F43F5E" offset="1"/>
</ConicGradient>
</Fill>
<DropShadowStyle offsetY="4" blurX="12" blurY="12" color="#14B8A680"/>
</Layer>
</Layer>
<Layer name="Card5" width="100">
<Layer composition="@cardBg"/>
<Layer left="50" top="40">
<Path data="M-20 -15L0 -25L20 -15L20 15L0 25L-20 15Z"/>
<Fill color="@orange"/>
<DropShadowStyle offsetY="4" blurX="12" blurY="12" color="#F9731680"/>
</Layer>
</Layer>
</Layer>
<Layer name="GetStartedButton" left="88" top="307">
<Rectangle width="120" height="36" roundness="18"/>
<Fill>
<LinearGradient>
<ColorStop color="#6366F1" offset="0"/>
<ColorStop color="#8B5CF6" offset="1"/>
</LinearGradient>
</Fill>
<DropShadowStyle offsetY="4" blurX="12" blurY="12" color="#6366F180"/>
<Group centerX="0" centerY="0">
<Text text="Get Started" fontFamily="Arial" fontStyle="Bold" fontSize="13"/>
<Fill color="#FFF"/>
</Group>
</Layer>
<Layer name="Modifiers" top="325">
<Path left="258" top="-20" data="@wavePath"/>
<TrimPath end="0.75"/>
<Stroke color="@cyan" width="3" cap="round"/>
<Group left="368">
<Rectangle left="-30" top="-20" width="60" height="40"/>
<RoundCorner radius="10"/>
<Fill color="@emerald"/>
</Group>
<Group left="478">
<Rectangle left="-27.5" top="-17.5" width="35" height="35"/>
<Ellipse left="-7.5" top="-17.5" width="35" height="35"/>
<MergePath mode="xor"/>
<Fill color="@purple"/>
</Group>
<Group left="588">
<Ellipse left="16" top="-6" width="12" height="12"/>
<Fill color="@cyan"/>
<Repeater copies="5" position="0,0" rotation="72"/>
</Group>
</Layer>
<Layer left="663" top="300">
<Rectangle width="50" height="50"/>
<Fill>
<DiamondGradient>
<ColorStop color="#14B8A6" offset="0"/>
<ColorStop color="#0F766E" offset="1"/>
</DiamondGradient>
</Fill>
</Layer>
<Layer id="circleMask" visible="false">
<Ellipse left="-22.5" top="-22.5" width="45" height="45"/>
<Fill color="#FFF"/>
</Layer>
<Layer name="MaskedLayer" left="688" top="325" mask="@circleMask">
<Rectangle left="-25" top="-25" width="50" height="50"/>
<Fill>
<RadialGradient>
<ColorStop color="#F59E0B" offset="0"/>
<ColorStop color="#EC4899" offset="1"/>
</RadialGradient>
</Fill>
</Layer>
<Layer left="130" top="430">
<Rectangle left="-40" top="-30" width="80" height="60" roundness="10"/>
<Fill color="@emerald"/>
<BlurFilter blurX="3" blurY="3"/>
</Layer>
<Layer left="260" top="430">
<Rectangle left="-40" top="-30" width="80" height="60" roundness="10"/>
<Fill color="@cyan"/>
<DropShadowFilter offsetX="4" offsetY="4" blurX="12" blurY="12" color="#00000080"/>
</Layer>
<Layer left="390" top="430">
<Ellipse left="-27.5" top="-27.5" width="55" height="55"/>
<Fill color="@purple"/>
<ColorMatrixFilter matrix="0.33,0.33,0.33,0,0,0.33,0.33,0.33,0,0,0.33,0.33,0.33,0,0,0,0,0,1,0"/>
</Layer>
<Layer left="520" top="430">
<Rectangle left="-40" top="-30" width="80" height="60" roundness="10"/>
<Fill color="@coral"/>
<BlendFilter color="#6366F160" blendMode="overlay"/>
</Layer>
<Layer left="670" top="430">
<Rectangle left="-45" top="-30" width="90" height="60" roundness="8"/>
<Fill>
<ImagePattern image="pag_logo.png"/>
</Fill>
<Stroke color="#FFFFFF20" width="1"/>
<DropShadowStyle offsetY="6" blurX="16" blurY="16" color="#00000060"/>
</Layer>
<Layer left="400" top="500">
<Text fontFamily="Arial" fontSize="18">
<GlyphRun font="@iconFont" glyphs="1,2,3" y="0" xOffsets="0,28,56" bounds="0,0,80,18"/>
</Text>
<Fill color="#64748B"/>
</Layer>
<Resources>
<PathData id="wavePath" data="M 0 20 Q 30 0 60 20 T 120 20 T 180 20"/>
<Font id="iconFont">
<Glyph advance="20" path="M 0 -8 L 8 8 L -8 8 Z"/>
<Glyph advance="20" path="M -8 -8 L 8 -8 L 8 8 L -8 8 Z"/>
<Glyph advance="24" path="M 0 -10 A 10 10 0 1 1 0 10 A 10 10 0 1 1 0 -10"/>
</Font>
<SolidColor id="coral" color="#F43F5E"/>
<SolidColor id="purple" color="#8B5CF6"/>
<SolidColor id="amber" color="#F59E0B"/>
<SolidColor id="orange" color="#F97316"/>
<SolidColor id="cyan" color="#06B6D4"/>
<SolidColor id="emerald" color="#10B981"/>
<Composition id="cardBg" width="100" height="80">
<Layer width="100" height="80">
<Rectangle width="100%" height="100%" roundness="12"/>
<Fill color="#1E293B"/>
<Stroke color="#334155" width="1"/>
</Layer>
</Composition>
</Resources>
</pagx>
C.2 应用图标
毛玻璃风格的图标网格 — 12 个多色图标位于深色背景上,配合柔和色场和背景模糊卡片。展示了基于 Path 的矢量图标构建、Composition 复用、BackgroundBlurStyle 和 DropShadowStyle。
<?xml version="1.0" encoding="UTF-8"?>
<pagx width="640" height="520">
<!-- Background: Gray 900 -->
<Layer name="Background" left="320" top="260">
<Rectangle left="-320" top="-260" width="640" height="520"/>
<Fill color="#111827"/>
</Layer>
<!-- Background: soft color fields -->
<Layer name="BgPattern">
<Layer left="60" top="60">
<Ellipse left="-160" top="-160" width="320" height="320"/>
<Fill color="#8B5CF6" alpha="0.2"/>
<BlurFilter blurX="50" blurY="50"/>
</Layer>
<Layer left="560" top="80">
<Ellipse left="-140" top="-140" width="280" height="280"/>
<Fill color="#06B6D4" alpha="0.18"/>
<BlurFilter blurX="50" blurY="50"/>
</Layer>
<Layer left="600" top="380">
<Ellipse left="-130" top="-130" width="260" height="260"/>
<Fill color="#3B82F6" alpha="0.16"/>
<BlurFilter blurX="50" blurY="50"/>
</Layer>
<Layer left="80" top="480">
<Ellipse left="-150" top="-150" width="300" height="300"/>
<Fill color="#EC4899" alpha="0.18"/>
<BlurFilter blurX="50" blurY="50"/>
</Layer>
<Layer left="320" top="260">
<Ellipse left="-100" top="-100" width="200" height="200"/>
<Fill color="#10B981" alpha="0.1"/>
<BlurFilter blurX="40" blurY="40"/>
</Layer>
</Layer>
<!-- ==================== App Icons ==================== -->
<!-- 3-row grid: vertical container with 3 horizontal rows -->
<Layer name="IconGrid" width="640" height="520" layout="vertical" padding="50" arrangement="spaceBetween">
<!-- Row 1 -->
<Layer layout="horizontal" arrangement="spaceBetween">
<Layer name="Home" layout="vertical" gap="10" alignment="center">
<Layer width="72" height="72">
<Layer composition="@card"/>
<Layer centerX="0" centerY="0">
<Path data="M19 0L0 19L5 19L5 36L14 36L14 25L24 25L24 36L33 36L33 19L38 19Z"/>
<Fill color="#F97316"/>
</Layer>
</Layer>
<Layer>
<Text text="Home" fontFamily="Arial" fontSize="12"/>
<Fill color="#6B7280"/>
</Layer>
</Layer>
<Layer name="Search" layout="vertical" gap="10" alignment="center">
<Layer width="72" height="72">
<Layer composition="@card"/>
<Layer centerX="0" centerY="0">
<Ellipse width="26" height="26"/>
<Stroke color="#3B82F6" width="3.5"/>
<Group>
<Path data="M23 23L33 33"/>
<Stroke color="#3B82F6" width="4" cap="round"/>
</Group>
</Layer>
</Layer>
<Layer>
<Text text="Search" fontFamily="Arial" fontSize="12"/>
<Fill color="#6B7280"/>
</Layer>
</Layer>
<Layer name="Bell" layout="vertical" gap="10" alignment="center">
<Layer width="72" height="72">
<Layer composition="@card"/>
<Layer centerX="0" centerY="0">
<Path data="M3 29C3 19 7 7 17 5C27 7 31 19 31 29L34 33L0 33Z"/>
<Fill color="#FBBF24"/>
<Group>
<Path data="M17 0L17 6"/>
<Stroke color="#FBBF24" width="2.5" cap="round"/>
</Group>
<Group>
<Ellipse left="13" top="32.5" width="8" height="5"/>
<Fill color="#FBBF24"/>
</Group>
</Layer>
</Layer>
<Layer>
<Text text="Bell" fontFamily="Arial" fontSize="12"/>
<Fill color="#6B7280"/>
</Layer>
</Layer>
<Layer name="Heart" layout="vertical" gap="10" alignment="center">
<Layer width="72" height="72">
<Layer composition="@card"/>
<Layer centerX="0" centerY="0">
<Path data="M19 13C16 3 0 0 0 13C0 24 12 32 19 39C26 32 38 24 38 13C38 0 22 3 19 13Z"/>
<Fill color="#EF4444"/>
</Layer>
</Layer>
<Layer>
<Text text="Heart" fontFamily="Arial" fontSize="12"/>
<Fill color="#6B7280"/>
</Layer>
</Layer>
</Layer>
<!-- Row 2 -->
<Layer layout="horizontal" arrangement="spaceBetween">
<Layer name="Chat" layout="vertical" gap="10" alignment="center">
<Layer width="72" height="72">
<Layer composition="@card"/>
<Layer centerX="0" centerY="0">
<Path data="M0 7C0 2 3 0 8 0L28 0C33 0 36 2 36 7L36 22C36 27 33 29 28 29L20 29L8 34L14 29L8 29C3 29 0 27 0 22Z"/>
<Fill color="#10B981"/>
<Group>
<Ellipse left="9" top="12" width="4" height="4"/>
<Ellipse left="16" top="12" width="4" height="4"/>
<Ellipse left="23" top="12" width="4" height="4"/>
<Fill color="#1E2433"/>
</Group>
</Layer>
</Layer>
<Layer>
<Text text="Chat" fontFamily="Arial" fontSize="12"/>
<Fill color="#6B7280"/>
</Layer>
</Layer>
<Layer name="Camera" layout="vertical" gap="10" alignment="center">
<Layer width="72" height="72">
<Layer composition="@card"/>
<Layer centerX="0" centerY="0">
<Rectangle top="5" width="38" height="26" roundness="5"/>
<Path data="M12 7L15 0L23 0L26 7"/>
<Fill color="#6366F1"/>
<Group>
<Ellipse left="10.5" top="9.5" width="17" height="17"/>
<Fill color="#1E2433"/>
</Group>
<Group>
<Ellipse left="14" top="13" width="10" height="10"/>
<Stroke color="#6366F1" width="2.5"/>
</Group>
</Layer>
</Layer>
<Layer>
<Text text="Camera" fontFamily="Arial" fontSize="12"/>
<Fill color="#6B7280"/>
</Layer>
</Layer>
<Layer name="Settings" layout="vertical" gap="10" alignment="center">
<Layer width="72" height="72">
<Layer composition="@card"/>
<Layer centerX="0" centerY="0">
<Path data="M17 0L22.6 0L23.3 6.2L26.9 7.7L31.8 3.8L35.8 7.8L31.9 12.7L33.4 16.3L39.6 17L39.6 22.6L33.4 23.3L31.9 26.9L35.8 31.8L31.8 35.8L26.9 31.9L23.3 33.4L22.6 39.6L17 39.6L16.3 33.4L12.7 31.9L7.8 35.8L3.8 31.8L7.7 26.9L6.2 23.3L0 22.6L0 17L6.2 16.3L7.7 12.7L3.8 7.8L7.8 3.8L12.7 7.7L16.3 6.2Z"/>
<Fill color="#8B5CF6"/>
<Group>
<Ellipse left="12.8" top="12.8" width="14" height="14"/>
<Fill color="#1E2433"/>
</Group>
<Group>
<Ellipse left="17.3" top="17.3" width="5" height="5"/>
<Fill color="#8B5CF6"/>
</Group>
</Layer>
</Layer>
<Layer>
<Text text="Settings" fontFamily="Arial" fontSize="12"/>
<Fill color="#6B7280"/>
</Layer>
</Layer>
<Layer name="User" layout="vertical" gap="10" alignment="center">
<Layer width="72" height="72">
<Layer composition="@card"/>
<Layer centerX="0" centerY="0">
<Ellipse width="46" height="46"/>
<Fill color="#06B6D4"/>
<Group>
<Ellipse left="17" top="6" width="12" height="12"/>
<Path data="M12 39C12 27 16 21 23 21C30 21 34 27 34 39"/>
<Fill color="#1E2433"/>
</Group>
</Layer>
</Layer>
<Layer>
<Text text="User" fontFamily="Arial" fontSize="12"/>
<Fill color="#6B7280"/>
</Layer>
</Layer>
</Layer>
<!-- Row 3 -->
<Layer layout="horizontal" arrangement="spaceBetween">
<Layer name="Calendar" layout="vertical" gap="10" alignment="center">
<Layer width="72" height="72">
<Layer composition="@card"/>
<Layer centerX="0" centerY="0">
<Rectangle top="4" width="38" height="38" roundness="6"/>
<Rectangle left="8.5" width="3" height="9" roundness="2"/>
<Rectangle left="26.5" width="3" height="9" roundness="2"/>
<Fill color="#EF4444"/>
<Group>
<Rectangle left="4" top="16" width="30" height="22" roundness="3"/>
<Fill color="#1E2433"/>
</Group>
<Group top="15" centerX="0">
<Text text="17" fontFamily="Arial" fontStyle="Bold" fontSize="16"/>
<Fill color="#EF4444"/>
</Group>
</Layer>
</Layer>
<Layer>
<Text text="Calendar" fontFamily="Arial" fontSize="12"/>
<Fill color="#6B7280"/>
</Layer>
</Layer>
<Layer name="Lock" layout="vertical" gap="10" alignment="center">
<Layer width="72" height="72">
<Layer composition="@card"/>
<Layer centerX="0" centerY="0">
<Path data="M5 16L5 9C5 0 25 0 25 9L25 16"/>
<Stroke color="#F97316" width="4" cap="round"/>
<Group>
<Rectangle top="17" width="30" height="22" roundness="6"/>
<Fill color="#F97316"/>
</Group>
<Group>
<Ellipse left="11.5" top="22.5" width="7" height="7"/>
<Rectangle left="13.5" top="29" width="3" height="6" roundness="1"/>
<Fill color="#1E2433"/>
</Group>
</Layer>
</Layer>
<Layer>
<Text text="Lock" fontFamily="Arial" fontSize="12"/>
<Fill color="#6B7280"/>
</Layer>
</Layer>
<Layer name="Music" layout="vertical" gap="10" alignment="center">
<Layer width="72" height="72">
<Layer composition="@card"/>
<Layer centerX="0" centerY="0">
<Path data="M6.5 30L6.5 7L27.5 2L27.5 25M6.5 12L27.5 7"/>
<Stroke color="#EC4899" width="3.5" cap="round" join="round"/>
<Group>
<Ellipse top="28.5" width="15" height="11"/>
<Ellipse left="20" top="23.5" width="15" height="11"/>
<Fill color="#EC4899"/>
</Group>
</Layer>
</Layer>
<Layer>
<Text text="Music" fontFamily="Arial" fontSize="12"/>
<Fill color="#6B7280"/>
</Layer>
</Layer>
<Layer name="Download" layout="vertical" gap="10" alignment="center">
<Layer width="72" height="72">
<Layer composition="@card"/>
<Layer centerX="0" centerY="0">
<Ellipse left="7" width="24" height="22"/>
<Ellipse top="9" width="16" height="16"/>
<Ellipse left="21" top="9" width="18" height="16"/>
<Ellipse left="2" top="14" width="34" height="10"/>
<Fill color="#3B82F6"/>
<Group>
<Path data="M19 25L19 32M16 30L19 34L22 30"/>
<Stroke color="#3B82F6" width="3" cap="round" join="round"/>
</Group>
</Layer>
</Layer>
<Layer>
<Text text="Download" fontFamily="Arial" fontSize="12"/>
<Fill color="#6B7280"/>
</Layer>
</Layer>
</Layer>
</Layer>
<!-- ==================== Resources ==================== -->
<Resources>
<Composition id="card" width="72" height="72">
<Layer width="72" height="72">
<Rectangle width="100%" height="100%" roundness="12"/>
<Fill color="#FFFFFF" alpha="0.06"/>
<Stroke color="#FFFFFF" width="0.5" alpha="0.25"/>
<BackgroundBlurStyle blurX="20" blurY="20"/>
<DropShadowStyle offsetY="4" blurX="20" blurY="20" color="#00000040"/>
</Layer>
</Composition>
</Resources>
</pagx>
C.3 星云学员
全屏 UI 面板,包含顶部导航栏、头像、进度条、操作按钮、货币组件和底部 Tab 栏。展示了典型应用界面的布局方式和组件组合。
<?xml version="1.0" encoding="UTF-8"?>
<pagx width="800" height="600">
<!-- ======== Decorations (not part of layout) ======== -->
<Layer name="Decorations" width="100%" height="100%" clipToBounds="true">
<!-- Background -->
<Rectangle width="100%" height="100%"/>
<Fill>
<LinearGradient endPoint="0,1">
<ColorStop color="#0B1026" offset="0"/>
<ColorStop color="#0F1C3D" offset="0.6"/>
<ColorStop color="#1B1140" offset="1"/>
</LinearGradient>
</Fill>
<Layer name="NebulaLeft">
<Ellipse left="-20" top="70" width="360" height="300"/>
<Fill>
<RadialGradient center="160,220" radius="220" fitsToGeometry="false">
<ColorStop color="#1D4ED880" offset="0"/>
<ColorStop color="#1D4ED800" offset="1"/>
</RadialGradient>
</Fill>
</Layer>
<Layer name="NebulaRight">
<Ellipse left="460" top="40" width="360" height="280"/>
<Fill>
<RadialGradient center="640,180" radius="220" fitsToGeometry="false">
<ColorStop color="#C026D380" offset="0"/>
<ColorStop color="#C026D300" offset="1"/>
</RadialGradient>
</Fill>
</Layer>
<Layer name="NebulaBottom" alpha="0.9">
<Ellipse left="100" top="395" width="600" height="220"/>
<Fill>
<RadialGradient center="400,505" radius="260" fitsToGeometry="false">
<ColorStop color="#22D3EE60" offset="0"/>
<ColorStop color="#22D3EE00" offset="1"/>
</RadialGradient>
</Fill>
</Layer>
<Layer name="Stars" alpha="0.9">
<Ellipse left="119" top="79" width="2" height="2"/>
<Ellipse left="178.5" top="108.5" width="3" height="3"/>
<Ellipse left="259" top="89" width="2" height="2"/>
<Ellipse left="339" top="69" width="2" height="2"/>
<Ellipse left="418.5" top="108.5" width="3" height="3"/>
<Ellipse left="519" top="79" width="2" height="2"/>
<Ellipse left="609" top="119" width="2" height="2"/>
<Ellipse left="699" top="89" width="2" height="2"/>
<Ellipse left="738.5" top="148.5" width="3" height="3"/>
<Ellipse left="89" top="159" width="2" height="2"/>
<Fill color="#FFFFFFCC"/>
<Group>
<Ellipse left="149" top="199" width="2" height="2"/>
<Ellipse left="239" top="169" width="2" height="2"/>
<Ellipse left="359" top="189" width="2" height="2"/>
<Ellipse left="468.5" top="158.5" width="3" height="3"/>
<Ellipse left="559" top="209" width="2" height="2"/>
<Ellipse left="659" top="179" width="2" height="2"/>
<Ellipse left="739" top="229" width="2" height="2"/>
<Ellipse left="209" top="239" width="2" height="2"/>
<Ellipse left="539" top="239" width="2" height="2"/>
<Fill color="#7DD3FC88"/>
</Group>
</Layer>
<Layer name="StarBursts">
<Layer>
<Polystar left="212" top="122" type="star" pointCount="4" outerRadius="8" innerRadius="3" rotation="45"/>
<Fill color="#FFFFFFEE"/>
<DropShadowStyle blurX="6" blurY="6" color="#7DD3FC80"/>
</Layer>
<Layer>
<Polystar left="613" top="203" type="star" pointCount="4" outerRadius="7" innerRadius="3" rotation="45"/>
<Fill color="#FFFFFFDD"/>
<DropShadowStyle blurX="6" blurY="6" color="#C084FC80"/>
</Layer>
<Layer>
<Polystar left="694" top="414" type="star" pointCount="4" outerRadius="6" innerRadius="2" rotation="45"/>
<Fill color="#FFFFFFCC"/>
<DropShadowStyle blurX="5" blurY="5" color="#22D3EE80"/>
</Layer>
</Layer>
<Layer name="HudRings" left="400" top="310" alpha="0.7">
<Ellipse left="-240" top="-240" width="480" height="480"/>
<Stroke color="#1E3A8A" width="2" dashes="10,12"/>
<Group>
<Ellipse left="-210" top="-210" width="420" height="420"/>
<TrimPath start="0.1" end="0.4" offset="40"/>
<Stroke width="4" cap="round">
<ConicGradient fitsToGeometry="false">
<ColorStop color="#38BDF8" offset="0"/>
<ColorStop color="#22D3EE" offset="0.5"/>
<ColorStop color="#7C3AED" offset="1"/>
</ConicGradient>
</Stroke>
</Group>
<Group>
<Ellipse left="-180" top="-180" width="360" height="360"/>
<TrimPath start="0.55" end="0.85" offset="-30"/>
<Stroke color="#38BDF8" width="3" alpha="0.8" cap="round"/>
</Group>
</Layer>
<Layer name="MainPanel">
<Rectangle left="60" top="130" width="680" height="360" roundness="28"/>
<Fill>
<LinearGradient>
<ColorStop color="#0F1C3DCC" offset="0"/>
<ColorStop color="#12244ACC" offset="0.5"/>
<ColorStop color="#0C1B33CC" offset="1"/>
</LinearGradient>
</Fill>
<Stroke width="1.5">
<LinearGradient>
<ColorStop color="#3B82F6AA" offset="0"/>
<ColorStop color="#22D3EE88" offset="0.5"/>
<ColorStop color="#A78BFAAA" offset="1"/>
</LinearGradient>
</Stroke>
<BackgroundBlurStyle blurX="24" blurY="24" tileMode="mirror"/>
<DropShadowStyle offsetY="12" blurX="20" blurY="20" color="#00000066"/>
</Layer>
<Layer name="TopBar">
<Rectangle left="20" top="20" width="760" height="90" roundness="22"/>
<Fill>
<LinearGradient>
<ColorStop color="#0C1B33EE" offset="0"/>
<ColorStop color="#10284CEE" offset="1"/>
</LinearGradient>
</Fill>
<Stroke width="1">
<LinearGradient>
<ColorStop color="#1D4ED8AA" offset="0"/>
<ColorStop color="#38BDF8AA" offset="1"/>
</LinearGradient>
</Stroke>
<DropShadowStyle offsetY="8" blurX="16" blurY="16" color="#00000055"/>
</Layer>
<Layer name="BottomBar">
<Rectangle left="20" top="510" width="760" height="70" roundness="20"/>
<Fill>
<LinearGradient>
<ColorStop color="#0B1A33CC" offset="0"/>
<ColorStop color="#10284ECC" offset="1"/>
</LinearGradient>
</Fill>
<Stroke color="#1D4ED8AA" width="1"/>
</Layer>
<Layer name="TechLines" alpha="0.6">
<Path data="M 80 130 L 160 130 L 200 170"/>
<Stroke color="#38BDF8" width="1"/>
<Group>
<Path data="M 720 130 L 640 130 L 600 170"/>
<Stroke color="#A78BFA" width="1"/>
</Group>
<Group>
<Path data="M 90 460 L 170 460 L 210 420"/>
<Stroke color="#22D3EE" width="1"/>
</Group>
<Group>
<Path data="M 710 460 L 630 460 L 590 420"/>
<Stroke color="#C084FC" width="1"/>
</Group>
</Layer>
<Layer name="HexDecor" alpha="0.6">
<Polystar left="106" top="186" type="polygon" pointCount="6" outerRadius="14" rotation="30"/>
<Stroke color="#1D4ED8" width="1"/>
<Group>
<Polystar left="668" top="228" type="polygon" pointCount="6" outerRadius="12" rotation="30"/>
<Stroke color="#38BDF8" width="1"/>
</Group>
<Group>
<Polystar left="170" top="410" type="polygon" pointCount="6" outerRadius="10" rotation="30"/>
<Stroke color="#22D3EE" width="1"/>
</Group>
<Group>
<Polystar left="628" top="408" type="polygon" pointCount="6" outerRadius="12" rotation="30"/>
<Stroke color="#A78BFA" width="1"/>
</Group>
</Layer>
</Layer>
<!-- ======== Content Container (vertical layout) ======== -->
<Layer name="ContentContainer" width="100%" height="100%" layout="vertical" alignment="center" arrangement="spaceEvenly">
<!-- Top Bar Area -->
<Layer name="TopBarArea" width="760" height="90">
<Layer name="Avatar">
<Polystar type="polygon" pointCount="6" outerRadius="36" rotation="30" position="50,45"/>
<Stroke width="2">
<LinearGradient endPoint="1,1">
<ColorStop color="#38BDF8" offset="0"/>
<ColorStop color="#A78BFA" offset="1"/>
</LinearGradient>
</Stroke>
<Group>
<Ellipse left="22" top="17" width="56" height="56"/>
<Fill color="#0B1225"/>
</Group>
<Group>
<Ellipse left="26" top="21" width="48" height="48"/>
<Fill>
<RadialGradient radius="0.625">
<ColorStop color="#38BDF8AA" offset="0"/>
<ColorStop color="#0B122500" offset="1"/>
</RadialGradient>
</Fill>
</Group>
<DropShadowStyle blurX="10" blurY="10" color="#1D4ED880"/>
</Layer>
<Layer name="PlayerName">
<TextBox left="100" top="9">
<Text text="NEBULA CADET" fontFamily="Arial" fontStyle="Bold" fontSize="16"/>
<Fill color="#E2E8F0"/>
</TextBox>
</Layer>
<Layer name="PlayerIdRow">
<TextBox left="100" top="32">
<Text text="#A17X9" fontFamily="Arial" fontSize="11"/>
<Fill color="#7DD3FC"/>
</TextBox>
<Layer name="LevelBadge">
<Rectangle left="151" top="33" width="48" height="14" roundness="7"/>
<Fill>
<LinearGradient>
<ColorStop color="#0EA5E9" offset="0"/>
<ColorStop color="#22D3EE" offset="1"/>
</LinearGradient>
</Fill>
<Stroke color="#7DD3FC" width="1"/>
<TextBox left="151" top="33" width="48" height="14" textAlign="center" paragraphAlign="middle">
<Text text="LV 27" fontFamily="Arial" fontStyle="Bold" fontSize="9"/>
<Fill color="#E0F2FE"/>
</TextBox>
</Layer>
</Layer>
<Layer name="ExpBar">
<Rectangle left="100" top="54" width="280" height="10" roundness="5"/>
<Fill color="#1E293B"/>
<Group>
<Rectangle left="100" top="56" width="190" height="6" roundness="3"/>
<Fill>
<LinearGradient>
<ColorStop color="#22D3EE" offset="0"/>
<ColorStop color="#3B82F6" offset="1"/>
</LinearGradient>
</Fill>
</Group>
</Layer>
<Layer name="EnergyBar">
<Rectangle left="100" top="70" width="280" height="10" roundness="5"/>
<Fill color="#172437"/>
<Group>
<Rectangle left="100" top="72" width="160" height="6" roundness="3"/>
<Fill>
<LinearGradient>
<ColorStop color="#34D399" offset="0"/>
<ColorStop color="#10B981" offset="1"/>
</LinearGradient>
</Fill>
</Group>
</Layer>
<Layer name="Chips" right="14" top="14" width="150" layout="vertical" gap="6">
<Layer name="CoinChip" height="28">
<Rectangle width="100%" height="100%" roundness="14"/>
<Fill color="@chipGradient"/>
<Stroke color="#334155" width="1"/>
<Group>
<Ellipse left="12" top="6" width="16" height="16"/>
<Fill>
<LinearGradient>
<ColorStop color="#FFE29A" offset="0"/>
<ColorStop color="#F59E0B" offset="1"/>
</LinearGradient>
</Fill>
</Group>
<Group>
<Ellipse left="16" top="10" width="8" height="8"/>
<Stroke color="#F8D477" width="1"/>
</Group>
<TextBox left="40" width="110" height="28" paragraphAlign="middle">
<Text text="12,450" fontFamily="Arial" fontStyle="Bold" fontSize="13"/>
<Fill color="#F8FAFC"/>
</TextBox>
</Layer>
<Layer name="DiamondChip" height="28">
<Rectangle width="100%" height="100%" roundness="14"/>
<Fill color="@chipGradient"/>
<Stroke color="#334155" width="1"/>
<Group left="12" top="6">
<Path data="@IconDiamond"/>
<Fill>
<LinearGradient>
<ColorStop color="#7DD3FC" offset="0"/>
<ColorStop color="#A78BFA" offset="1"/>
</LinearGradient>
</Fill>
</Group>
<TextBox left="40" width="110" height="28" paragraphAlign="middle">
<Text text="860" fontFamily="Arial" fontStyle="Bold" fontSize="13"/>
<Fill color="#F8FAFC"/>
</TextBox>
</Layer>
</Layer>
</Layer>
<!-- Main Panel Area -->
<Layer name="MainPanelArea" width="680" height="360">
<Layer name="MenuButtons" centerX="0" centerY="0" width="340" height="262" layout="vertical" gap="14">
<Layer name="StartButton" height="78">
<Rectangle width="340" height="78" roundness="28"/>
<Fill>
<LinearGradient>
<ColorStop color="#00FFC6" offset="0"/>
<ColorStop color="#00C2FF" offset="0.5"/>
<ColorStop color="#00FFA6" offset="1"/>
</LinearGradient>
</Fill>
<Stroke color="#5AFADF" width="2"/>
<Group>
<Rectangle left="15" top="12" width="310" height="34" roundness="18"/>
<Fill color="#FFFFFF22"/>
</Group>
<Group left="50" top="27">
<Path data="@IconPlay"/>
<Fill color="#ECFEFF"/>
</Group>
<Layer width="100%" height="100%">
<TextBox width="100%" height="100%" textAlign="center" paragraphAlign="middle">
<Text text="START" fontFamily="Arial" fontStyle="Bold" fontSize="24"/>
<Fill color="#F0FFFD"/>
</TextBox>
<DropShadowStyle offsetY="2" blurX="6" blurY="6" color="#0F172A80"/>
</Layer>
<DropShadowStyle offsetY="12" blurX="18" blurY="18" color="#00FFC066"/>
</Layer>
<Layer name="ShopButton" height="78">
<Rectangle width="340" height="78" roundness="28"/>
<Fill>
<LinearGradient>
<ColorStop color="#FFD166" offset="0"/>
<ColorStop color="#FF8C42" offset="1"/>
</LinearGradient>
</Fill>
<Stroke color="#FFD166" width="1.5"/>
<Group left="55" top="33">
<Path data="@IconCart"/>
<Fill color="#FFF7ED"/>
</Group>
<TextBox width="100%" height="100%" textAlign="center" paragraphAlign="middle">
<Text text="SHOP" fontFamily="Arial" fontStyle="Bold" fontSize="22"/>
<Fill color="#FFF7ED"/>
</TextBox>
<DropShadowStyle offsetY="10" blurX="16" blurY="16" color="#F59E0B66"/>
</Layer>
<Layer name="RankButton" height="78">
<Rectangle width="340" height="78" roundness="28"/>
<Fill>
<LinearGradient>
<ColorStop color="#7C3AED" offset="0"/>
<ColorStop color="#EC4899" offset="1"/>
</LinearGradient>
</Fill>
<Stroke color="#C084FC" width="1.5"/>
<Group left="52" top="33">
<Path data="@IconCrown"/>
<Fill color="#FDF2F8"/>
</Group>
<TextBox width="100%" height="100%" textAlign="center" paragraphAlign="middle">
<Text text="RANK" fontFamily="Arial" fontStyle="Bold" fontSize="22"/>
<Fill color="#FDF2F8"/>
</TextBox>
<DropShadowStyle offsetY="10" blurX="16" blurY="16" color="#C084FC66"/>
</Layer>
</Layer>
</Layer>
<!-- Bottom Bar Area -->
<Layer name="BottomBarArea" width="760" height="70">
<Layer name="BottomButtons" centerX="0" centerY="0" width="366" height="46" layout="horizontal" arrangement="spaceBetween">
<Layer name="BottomButtonSettings" width="46">
<Layer composition="@navButton"/>
<Layer left="11" top="11">
<Polystar left="2" top="2" type="star" pointCount="8" outerRadius="10" innerRadius="6"/>
<Stroke color="#93C5FD" width="2"/>
<Group>
<Ellipse left="9" top="9" width="6" height="6"/>
<Fill color="#93C5FD"/>
</Group>
</Layer>
</Layer>
<Layer name="BottomButtonHelp" width="46">
<Layer composition="@navButton"/>
<Layer left="17" top="15">
<Path data="@IconQuestion"/>
<Fill color="#93C5FD"/>
</Layer>
</Layer>
<Layer name="BottomButtonShare" width="46">
<Layer composition="@navButton"/>
<Layer left="15" top="15">
<Path data="@IconShare"/>
<Fill color="#93C5FD"/>
</Layer>
</Layer>
</Layer>
</Layer>
</Layer>
<Resources>
<LinearGradient id="chipGradient">
<ColorStop color="#0F1A33CC" offset="0"/>
<ColorStop color="#14244ACC" offset="1"/>
</LinearGradient>
<PathData id="IconPlay" data="M 0 0 L 24 12 L 0 24 Z"/>
<PathData id="IconCart" data="M 0 0 L 15 0 L 13 8 L 2 8 Z M 4 12 A 2 2 0 1 0 4 16 A 2 2 0 1 0 4 12 Z M 11 12 A 2 2 0 1 0 11 16 A 2 2 0 1 0 11 12 Z"/>
<PathData id="IconCrown" data="M 0 10 L 4 0 L 10 6 L 16 0 L 20 10 Z M 2 10 L 2 14 L 18 14 L 18 10 Z"/>
<PathData id="IconDiamond" data="M 8 0 L 16 4 L 16 12 L 8 16 L 0 12 L 0 4 Z"/>
<PathData id="IconQuestion" data="M 6 0 C 2 0 0 2 0 5 L 3 5 C 3 3.5 4 2.5 6 2.5 C 8 2.5 9 3.5 9 5 C 9 6.5 7.5 7 6 8 C 4.5 9 4 10 4 12 L 8 12 C 8 10.5 8.5 9.5 10 8.5 C 12 7.5 13 6 13 4 C 13 1 10 0 6 0 Z M 6 14 A 2 2 0 1 0 6 18 A 2 2 0 1 0 6 14 Z"/>
<PathData id="IconShare" data="M 10 0 L 18 8 L 10 16 L 10 11 L 0 11 L 0 5 L 10 5 Z"/>
<Composition id="navButton" width="46" height="46">
<Layer width="46" height="46">
<Ellipse width="100%" height="100%"/>
<Fill>
<LinearGradient>
<ColorStop color="#0D2038CC" offset="0"/>
<ColorStop color="#162B4DCC" offset="1"/>
</LinearGradient>
</Fill>
<Stroke color="#3B82F6" width="1"/>
</Layer>
</Composition>
</Resources>
</pagx>
C.4 游戏 HUD
科幻风格的游戏平视显示器,包含瞄准十字线、弧形生命值和能量仪表、雷达小地图、弹药计数器和任务目标栏。展示了 Repeater 驱动的刻度线、弧形 Stroke 上的 TrimPath、ConicGradient 扫描效果和多层 Mask 叠加。
<?xml version="1.0" encoding="UTF-8"?>
<pagx width="800" height="600">
<Layer id="triangleMask" visible="false">
<Polystar left="-40" type="polygon" pointCount="3" outerRadius="80" rotation="90"/>
<Fill color="#FFF"/>
</Layer>
<Layer name="Background">
<Rectangle width="800" height="600"/>
<Fill>
<RadialGradient center="400,300" radius="500" fitsToGeometry="false">
<ColorStop color="#001122" offset="0"/>
<ColorStop color="#000811" offset="0.7"/>
<ColorStop color="#000" offset="1"/>
</RadialGradient>
</Fill>
<Layer alpha="0.15">
<Rectangle top="-0.5" width="800" height="1"/>
<Repeater copies="35" position="0,17"/>
<Group left="400" top="300" rotation="60">
<Rectangle left="-600" top="-510.5" width="1200" height="1"/>
<Repeater copies="60" position="0,17"/>
</Group>
<Group left="400" top="300" rotation="-60">
<Rectangle left="-600" top="-510.5" width="1200" height="1"/>
<Repeater copies="60" position="0,17"/>
</Group>
<Fill color="#0066AA"/>
</Layer>
<Group>
<Rectangle width="800" height="600"/>
<Fill>
<RadialGradient center="400,300" radius="600" fitsToGeometry="false">
<ColorStop color="#00000000" offset="0.6"/>
<ColorStop color="#00000080" offset="1"/>
</RadialGradient>
</Fill>
</Group>
</Layer>
<Layer name="ReticleComplex" left="400" top="300">
<Rectangle left="-1" top="-227.5" width="2" height="15"/>
<Fill color="#00CCFF" alpha="0.6"/>
<Repeater copies="60" rotation="6"/>
<Group>
<Rectangle left="-0.5" top="-219" width="1" height="8"/>
<Fill color="#00CCFF" alpha="0.3"/>
<Repeater copies="60" offset="0.5" rotation="6"/>
</Group>
<Group>
<Ellipse left="-190" top="-190" width="380" height="380"/>
<Stroke color="#0FF" width="1" alpha="0.4" dashes="60,40"/>
</Group>
<Group>
<Ellipse left="-180" top="-180" width="360" height="360"/>
<Stroke color="#0088FF" width="2" alpha="0.5" dashes="10,80"/>
</Group>
<Layer>
<Ellipse left="-150" top="-150" width="300" height="300"/>
<Stroke color="#00CCFF" width="3"/>
<DropShadowStyle blurX="8" blurY="8" color="#00CCFF80"/>
</Layer>
<Layer>
<Polystar type="polygon" pointCount="3" outerRadius="80" rotation="90" position="0,0"/>
<Stroke color="#FFAA00" width="4"/>
<Fill>
<RadialGradient radius="80" fitsToGeometry="false">
<ColorStop color="#FFAA0040" offset="0"/>
<ColorStop color="#FFAA0010" offset="1"/>
</RadialGradient>
</Fill>
<DropShadowStyle blurX="25" blurY="25" color="#FFAA00"/>
</Layer>
<Layer mask="@triangleMask">
<Rectangle left="-50" top="-50" width="100" height="100"/>
<Fill>
<LinearGradient>
<ColorStop color="#FFAA0000" offset="0"/>
<ColorStop color="#FFAA0040" offset="0.5"/>
<ColorStop color="#FFAA0000" offset="1"/>
</LinearGradient>
</Fill>
</Layer>
<Layer>
<Ellipse left="-3" top="-3" width="6" height="6"/>
<Fill color="#FFF"/>
<Group>
<Path data="M -20 0 L 20 0 M 0 -20 L 0 20"/>
<Stroke color="#FFF" width="1" alpha="0.5"/>
</Group>
</Layer>
</Layer>
<Layer name="LeftSide" left="400" top="0" bottom="0">
<Layer left="-180" top="200">
<Path data="@healthArc"/>
<Stroke color="#002233" width="24" cap="butt"/>
</Layer>
<Layer left="-180" top="200">
<Path data="@healthArc"/>
<Stroke width="20" cap="butt" dashes="4,2">
<LinearGradient startPoint="0,200" endPoint="0,0" fitsToGeometry="false">
<ColorStop color="#F00" offset="0"/>
<ColorStop color="#FF0" offset="0.5"/>
<ColorStop color="#0F0" offset="1"/>
</LinearGradient>
</Stroke>
<TrimPath end="0.85"/>
<DropShadowStyle blurX="5" blurY="5" color="#FF000040"/>
</Layer>
<Layer name="HealthText" left="-440" centerY="0" width="200" layout="vertical" alignment="end">
<Layer>
<Text text="100%" fontFamily="Arial" fontStyle="Bold" fontSize="24"/>
<Fill color="#FFF"/>
</Layer>
<Layer>
<Text text="STRUCTURAL INTEGRITY" fontFamily="Arial" fontSize="10"/>
<Fill color="#0088FF"/>
</Layer>
</Layer>
</Layer>
<Layer name="SystemStats" left="50" top="48" layout="vertical">
<Layer>
<Text text="PWR: 88%" fontFamily="Arial" fontSize="12"/>
<Fill color="#00CCFF" alpha="0.7"/>
</Layer>
<Layer>
<Text text="RADAR: ACT" fontFamily="Arial" fontSize="12"/>
<Fill color="#00CCFF" alpha="0.7"/>
</Layer>
<Layer>
<Text text="SHIELD: OK" fontFamily="Arial" fontSize="12"/>
<Fill color="#00CCFF" alpha="0.7"/>
</Layer>
</Layer>
<Layer name="RightSide" left="400" top="0" bottom="0">
<Layer left="144" top="200">
<Path data="@energyArc"/>
<Stroke color="#002233" width="24" cap="butt"/>
</Layer>
<Layer left="144" top="200">
<Path data="@energyArc"/>
<Stroke width="20" cap="butt" dashes="10,2">
<LinearGradient startPoint="36,0" endPoint="36,200" fitsToGeometry="false">
<ColorStop color="#0FF" offset="0"/>
<ColorStop color="#0044FF" offset="1"/>
</LinearGradient>
</Stroke>
<TrimPath end="0.6"/>
<DropShadowStyle blurX="5" blurY="5" color="#00FFFF40"/>
</Layer>
<Layer name="EnergyText" left="240" centerY="0" layout="vertical">
<Layer>
<Text text="60%" fontFamily="Arial" fontStyle="Bold" fontSize="24"/>
<Fill color="#FFF"/>
</Layer>
<Layer>
<Text text="PLASMA RESERVES" fontFamily="Arial" fontSize="10"/>
<Fill color="#0088FF"/>
</Layer>
</Layer>
</Layer>
<Layer name="Bottom" left="200" top="480" width="400" height="60">
<Layer>
<Path data="M 0 40 L 20 0 L 380 0 L 400 40 L 380 60 L 20 60 Z"/>
<Fill color="#001122" alpha="0.95"/>
<Stroke color="#0066AA" width="2"/>
<DropShadowStyle blurX="15" blurY="15" color="#000"/>
</Layer>
<Layer name="WeaponInfo" centerX="0" centerY="0" layout="vertical" alignment="center">
<Layer>
<Text text="MK-IV RAILGUN" fontFamily="Arial" fontStyle="Bold" fontSize="20"/>
<Fill color="#FFF"/>
</Layer>
<Layer>
<Text text="[ BURST MODE ]" fontFamily="Arial" fontSize="12"/>
<Fill color="#FFAA00"/>
</Layer>
</Layer>
</Layer>
<Layer left="200" top="550">
<Rectangle left="180" top="-2" width="40" height="4"/>
<Fill color="#004488"/>
<Group>
<Rectangle left="165" top="-2" width="10" height="4"/>
<Rectangle left="225" top="-2" width="10" height="4"/>
<Fill color="#002244"/>
</Group>
</Layer>
<Layer name="Radar" left="100" top="500">
<Ellipse left="-70" top="-70" width="140" height="140"/>
<Fill color="#001122" alpha="0.9"/>
<Stroke color="#0066AA" width="2"/>
<Group>
<Ellipse left="-50" top="-50" width="100" height="100"/>
<Ellipse left="-25" top="-25" width="50" height="50"/>
<Path data="M -70 0 L 70 0 M 0 -70 L 0 70"/>
<Stroke color="#004488" width="1"/>
</Group>
<Layer>
<Ellipse left="-65" top="-65" width="130" height="130"/>
<Fill>
<ConicGradient endAngle="90" fitsToGeometry="false">
<ColorStop color="#00FF0000" offset="0"/>
<ColorStop color="#00FF0080" offset="1"/>
</ConicGradient>
</Fill>
</Layer>
<Layer>
<Ellipse left="27" top="-23" width="6" height="6"/>
<Ellipse left="-43" top="7" width="6" height="6"/>
<Fill color="#F00"/>
<DropShadowStyle blurX="4" blurY="4" color="#F00"/>
</Layer>
<Layer>
<Ellipse left="-2" top="-2" width="4" height="4"/>
<Fill color="#FFF"/>
</Layer>
</Layer>
<Layer name="Objectives" top="36" centerX="0">
<Text text="MISSION OBJECTIVES: [ ] INFILTRATE BASE [ ] HACK TERMINAL [x] SECURE PERIMETER" fontFamily="Arial" fontStyle="Bold" fontSize="12"/>
<Fill color="#FFF"/>
<DropShadowStyle blurX="2" blurY="2" color="#000"/>
</Layer>
<Layer name="StatusIcons" left="706" top="70">
<Rectangle left="-4" top="-4" width="8" height="8"/>
<Stroke color="#FFAA00" width="1"/>
<Repeater copies="3" position="15,0"/>
<DropShadowStyle blurX="5" blurY="5" color="#FFAA00"/>
</Layer>
<Layer name="AmmoDisplay" left="630" top="496">
<Layer>
<Path left="-2" data="@bullet"/>
<Fill color="#FFAA00"/>
<Stroke color="#000" width="1"/>
<Repeater copies="10" position="10,0"/>
</Layer>
<Layer top="15">
<Path left="-2" data="@bullet"/>
<Fill color="#FFAA00"/>
<Stroke color="#000" width="1"/>
<Repeater copies="5" position="10,0"/>
<Group>
<Path left="-2" data="@bullet"/>
<Fill color="#331100"/>
<Stroke color="#442200" width="1"/>
<Repeater copies="5" offset="5" position="10,0"/>
</Group>
</Layer>
</Layer>
<Layer name="Corners">
<Path data="M 40 100 L 40 40 L 100 40 M 760 100 L 760 40 L 700 40 M 40 500 L 40 560 L 100 560 M 760 500 L 760 560 L 700 560"/>
<Stroke color="#00CCFF" width="4"/>
</Layer>
<Resources>
<PathData id="bullet" data="M 2 0 L 6 0 L 8 4 L 6 12 L 2 12 L 0 4 Z"/>
<PathData id="healthArc" data="M 0 200 A 200 200 0 0 0 0 0"/>
<PathData id="energyArc" data="M 36 0 A 200 200 0 0 0 36 200"/>
</Resources>
</pagx>
C.5 PAGX 特性概览
信息图/演示幻灯片风格的 PAGX 能力介绍 — 中心标题搭配轨道环、五张特性卡片通过虚线连接、底部转换流程条。展示了 TextBox 多行排版、卡片式信息架构和装饰性连接线图形。
<?xml version="1.0" encoding="UTF-8"?>
<pagx width="1600" height="1200">
<Layer name="Background" width="100%" height="100%">
<Rectangle width="100%" height="100%"/>
<Fill>
<LinearGradient endPoint="0,1">
<ColorStop color="#0F172A" offset="0"/>
<ColorStop color="#0B1228" offset="0.4"/>
<ColorStop color="#0F172A" offset="1"/>
</LinearGradient>
</Fill>
</Layer>
<Layer name="GlowB" alpha="0.22">
<Ellipse left="1210" top="25" width="700" height="550"/>
<Fill color="#38BDF8"/>
<BlurFilter blurX="220" blurY="200"/>
</Layer>
<Layer name="GlowC" alpha="0.18">
<Ellipse left="100" top="875" width="500" height="350"/>
<Fill color="#10B981"/>
<BlurFilter blurX="180" blurY="130"/>
</Layer>
<Layer name="OrbitRing" left="800" top="530" alpha="0.18">
<Ellipse left="-380" top="-380" width="760" height="760"/>
<Stroke color="#334155" width="1"/>
</Layer>
<Layer name="Grid" alpha="0.05">
<Group left="56" top="56">
<Path data="M 0 4 L 8 4 M 4 0 L 4 8"/>
<Stroke color="#475569" width="1" cap="round"/>
<Repeater copies="13" position="120,0"/>
</Group>
<Repeater copies="10" position="0,120"/>
</Layer>
<Layer name="Core" centerX="0" centerY="-70" width="326" height="326">
<Layer width="100%" height="100%" alpha="0.2">
<Ellipse centerX="0" centerY="0" width="320" height="320"/>
<Fill color="#3B82F6"/>
<BlurFilter blurX="80" blurY="80"/>
</Layer>
<Layer width="100%" height="100%">
<Ellipse centerX="0" centerY="0" width="260" height="260"/>
<Stroke color="#475569" width="2"/>
</Layer>
<Layer width="100%" height="100%" alpha="0.7">
<Ellipse centerX="0" centerY="0" width="310" height="310"/>
<TrimPath end="0.77"/>
<Stroke width="2.5" cap="round">
<ConicGradient>
<ColorStop color="#06B6D400" offset="0"/>
<ColorStop color="#06B6D4" offset="0.77"/>
<ColorStop color="#06B6D400" offset="1"/>
</ConicGradient>
</Stroke>
</Layer>
<Layer width="100%" height="100%" alpha="0.4">
<Path centerX="0" centerY="0" data="M 0 -155 L 0 -163 M 155 0 L 163 0 M 0 155 L 0 163 M -155 0 L -163 0"/>
<Stroke color="#94A3B8" width="1.5" cap="round"/>
</Layer>
<Layer centerX="0" centerY="0" layout="vertical" gap="4" alignment="center">
<Layer>
<TextBox textAlign="center">
<Text text="PAGX" fontFamily="Arial" fontStyle="Bold" fontSize="52"/>
<Fill color="#FFF"/>
</TextBox>
<DropShadowStyle offsetY="4" blurX="20" blurY="20" color="#06B6D440"/>
</Layer>
<Layer>
<TextBox textAlign="center">
<Text text="Portable Animated Graphics XML" fontFamily="Arial" fontSize="14"/>
<Fill color="#94A3B8"/>
</TextBox>
</Layer>
</Layer>
<Text text="An XML-based markup language for describing animated vector graphics." fontFamily="Arial" fontSize="12"/>
<TextPath top="163" centerX="0" path="M -141 0 A 141 141 0 0 0 141 0" forceAlignment="true"/>
<Fill color="#64748B60"/>
</Layer>
<Layer name="ConnectorDots" alpha="0.7">
<Ellipse left="797" top="372" width="6" height="6"/>
<Ellipse left="951" top="508" width="6" height="6"/>
<Ellipse left="900" top="643" width="6" height="6"/>
<Ellipse left="694" top="643" width="6" height="6"/>
<Ellipse left="643" top="508" width="6" height="6"/>
<Fill color="#06B6D4"/>
</Layer>
<Layer name="Connectors" alpha="0.35">
<Path data="M 800 375 L 800 240 M 954 511 L 1119 466 M 903 646 L 989 790 M 697 646 L 611 790 M 646 511 L 481 466"/>
<Stroke width="2" dashes="8,14">
<LinearGradient startPoint="-120,0" endPoint="120,0" fitsToGeometry="false">
<ColorStop color="#06B6D4" offset="0"/>
<ColorStop color="#3B82F6" offset="1"/>
</LinearGradient>
</Stroke>
</Layer>
<Layer name="CardReadable" left="600" top="80" width="400" height="160">
<Rectangle width="100%" height="100%" roundness="20"/>
<Fill color="#1E293B80"/>
<Stroke color="#33415580" width="1"/>
<DropShadowStyle offsetY="4" blurX="24" blurY="24" color="#00000060"/>
<Layer width="100%" height="100%" alpha="0.08">
<Path left="20" data="@cardTopLine"/>
<Stroke width="1">
<LinearGradient>
<ColorStop color="#F43F5E00" offset="0"/>
<ColorStop color="#F43F5E" offset="0.5"/>
<ColorStop color="#F43F5E00" offset="1"/>
</LinearGradient>
</Stroke>
</Layer>
<Layer left="58" centerY="0">
<Path data="M 0 0 L 28 0 L 44 16 L 44 68 L 0 68 Z"/>
<Stroke color="#F43F5E" width="2" join="round"/>
<Fill color="#F43F5E10"/>
<Group>
<Path data="M 28 0 L 28 16 L 44 16"/>
<Stroke color="#F43F5E" width="1.5" join="round"/>
</Group>
<Group>
<Path data="M 12 28 L 6 36 L 12 44 M 32 28 L 38 36 L 32 44"/>
<Stroke color="#F43F5E" width="2" cap="round" join="round"/>
</Group>
<Group>
<Path data="M 25 24 L 19 48"/>
<Stroke color="#F43F5E" width="1.5" cap="round"/>
</Group>
</Layer>
<Layer left="150" top="34" layout="vertical">
<Layer>
<TextBox>
<Text text="Readable" fontFamily="Arial" fontStyle="Bold" fontSize="24"/>
<Fill color="#F43F5E"/>
</TextBox>
</Layer>
<Layer>
<TextBox width="220" lineHeight="22">
<Text text="Plain-text XML, easy to diff, debug, and AI-generate." fontFamily="Arial" fontSize="16"/>
<Fill color="#CBD5E1"/>
</TextBox>
</Layer>
</Layer>
</Layer>
<Layer name="CardComprehensive" left="1119" top="386" width="400" height="160">
<Rectangle width="100%" height="100%" roundness="20"/>
<Fill color="#1E293B80"/>
<Stroke color="#33415580" width="1"/>
<DropShadowStyle offsetY="4" blurX="24" blurY="24" color="#00000060"/>
<Layer width="100%" height="100%" alpha="0.08">
<Path left="20" data="@cardTopLine"/>
<Stroke width="1">
<LinearGradient>
<ColorStop color="#38BDF800" offset="0"/>
<ColorStop color="#38BDF8" offset="0.5"/>
<ColorStop color="#38BDF800" offset="1"/>
</LinearGradient>
</Stroke>
</Layer>
<Layer left="49" centerY="0">
<Rectangle width="52" height="32" roundness="6"/>
<Stroke color="#38BDF8" width="2"/>
<Fill color="#38BDF820"/>
<Group>
<Rectangle left="10" top="20" width="52" height="32" roundness="6"/>
<Stroke color="#38BDF8" width="2"/>
<Fill color="#38BDF815"/>
</Group>
<Group>
<Rectangle left="20" top="40" width="52" height="32" roundness="6"/>
<Stroke color="#38BDF8" width="2"/>
<Fill color="#38BDF810"/>
</Group>
</Layer>
<Layer left="150" top="34" layout="vertical">
<Layer>
<TextBox>
<Text text="Comprehensive" fontFamily="Arial" fontStyle="Bold" fontSize="24"/>
<Fill color="#38BDF8"/>
</TextBox>
</Layer>
<Layer>
<TextBox width="220" lineHeight="22">
<Text text="Covers vectors, images, text, effects, and masks." fontFamily="Arial" fontSize="16"/>
<Fill color="#CBD5E1"/>
</TextBox>
</Layer>
</Layer>
</Layer>
<Layer name="CardExpressive" left="81" top="386" width="400" height="160">
<Rectangle width="100%" height="100%" roundness="20"/>
<Fill color="#1E293B80"/>
<Stroke color="#33415580" width="1"/>
<DropShadowStyle offsetY="4" blurX="24" blurY="24" color="#00000060"/>
<Layer width="100%" height="100%" alpha="0.08">
<Path left="20" data="@cardTopLine"/>
<Stroke width="1">
<LinearGradient>
<ColorStop color="#A855F700" offset="0"/>
<ColorStop color="#A855F7" offset="0.5"/>
<ColorStop color="#A855F700" offset="1"/>
</LinearGradient>
</Stroke>
</Layer>
<Layer left="52" centerY="0">
<Rectangle top="1" width="32" height="72" roundness="5"/>
<Stroke color="#A855F7" width="2"/>
<Fill color="#A855F720"/>
<Group>
<Path data="M 4 21 L 28 21 M 4 37 L 22 37 M 4 53 L 28 53"/>
<Stroke color="#A855F7" width="1.5" cap="round"/>
</Group>
<Group>
<Path data="M 40 37 L 50 37 M 46 32 L 50 37 L 46 42"/>
<Stroke color="#A855F7" width="2" cap="round" join="round"/>
</Group>
<Group>
<Ellipse left="51" width="18" height="18"/>
<Stroke color="#A855F7" width="1.5"/>
</Group>
<Group>
<Path data="M 53 37 L 60 30 L 67 37 L 60 44 Z"/>
<Fill color="#A855F7"/>
</Group>
<Group>
<Path data="M 60 55 L 53 73 L 67 73 Z"/>
<Stroke color="#A855F7" width="1.5" join="round"/>
<Fill color="#A855F715"/>
</Group>
</Layer>
<Layer left="150" top="34" layout="vertical">
<Layer>
<TextBox>
<Text text="Expressive" fontFamily="Arial" fontStyle="Bold" fontSize="24"/>
<Fill color="#A855F7"/>
</TextBox>
</Layer>
<Layer>
<TextBox width="220" lineHeight="22">
<Text text="Compact structure for both static and animated content." fontFamily="Arial" fontSize="16"/>
<Fill color="#CBD5E1"/>
</TextBox>
</Layer>
</Layer>
</Layer>
<Layer name="CardInteroperable" left="299" top="790" width="400" height="160">
<Rectangle width="100%" height="100%" roundness="20"/>
<Fill color="#1E293B80"/>
<Stroke color="#33415580" width="1"/>
<DropShadowStyle offsetY="4" blurX="24" blurY="24" color="#00000060"/>
<Layer width="100%" height="100%" alpha="0.08">
<Path left="20" data="@cardTopLine"/>
<Stroke width="1">
<LinearGradient>
<ColorStop color="#10B98100" offset="0"/>
<ColorStop color="#10B981" offset="0.5"/>
<ColorStop color="#10B98100" offset="1"/>
</LinearGradient>
</Stroke>
</Layer>
<Layer left="42" centerY="0">
<Ellipse left="10" top="18" width="40" height="40"/>
<Ellipse left="46" top="18" width="40" height="40"/>
<Stroke color="#10B981" width="2"/>
<Group>
<Path data="M 46 38 L 50 38"/>
<Stroke color="#10B981" width="2" cap="round"/>
</Group>
<Group>
<Path data="M 13 13 L 0 0 M 83 63 L 96 76"/>
<Stroke color="#10B981" width="1" dashes="3,3"/>
</Group>
</Layer>
<Layer left="150" top="34" layout="vertical">
<Layer>
<TextBox>
<Text text="Interoperable" fontFamily="Arial" fontStyle="Bold" fontSize="24"/>
<Fill color="#10B981"/>
</TextBox>
</Layer>
<Layer>
<TextBox width="220" lineHeight="22">
<Text text="Bridges AE, Figma, SVG, PDF, and more seamlessly." fontFamily="Arial" fontSize="16"/>
<Fill color="#CBD5E1"/>
</TextBox>
</Layer>
</Layer>
</Layer>
<Layer name="CardDeployable" left="901" top="790" width="400" height="160">
<Rectangle width="100%" height="100%" roundness="20"/>
<Fill color="#1E293B80"/>
<Stroke color="#33415580" width="1"/>
<DropShadowStyle offsetY="4" blurX="24" blurY="24" color="#00000060"/>
<Layer width="100%" height="100%" alpha="0.08">
<Path left="20" data="@cardTopLine"/>
<Stroke width="1">
<LinearGradient>
<ColorStop color="#FBBF2400" offset="0"/>
<ColorStop color="#FBBF24" offset="0.5"/>
<ColorStop color="#FBBF2400" offset="1"/>
</LinearGradient>
</Stroke>
</Layer>
<Layer left="50" centerY="0">
<Path data="M 30 0 C 40 5 48 20 48 40 L 48 62 L 12 62 L 12 40 C 12 20 20 5 30 0 Z"/>
<Stroke color="#FBBF24" width="2" join="round"/>
<Fill color="#FBBF2420"/>
<Group>
<Path data="M 12 52 L 0 70 L 12 65 Z M 48 52 L 60 70 L 48 65 Z"/>
<Stroke color="#FBBF24" width="2" join="round"/>
<Fill color="#FBBF2415"/>
</Group>
<Group>
<Ellipse left="24" top="24" width="12" height="12"/>
<Stroke color="#FBBF24" width="2"/>
</Group>
<Group>
<Path data="M 20 62 L 25 78 L 30 70 L 35 78 L 40 62"/>
<Stroke color="#FB923C" width="2" cap="round" join="round"/>
</Group>
</Layer>
<Layer left="150" top="34" layout="vertical">
<Layer>
<TextBox>
<Text text="Deployable" fontFamily="Arial" fontStyle="Bold" fontSize="24"/>
<Fill color="#FBBF24"/>
</TextBox>
</Layer>
<Layer>
<TextBox width="220" lineHeight="22">
<Text text="One-click export to binary PAG with high performance." fontFamily="Arial" fontSize="16"/>
<Fill color="#CBD5E1"/>
</TextBox>
</Layer>
</Layer>
</Layer>
<Layer name="Pipeline" left="800" top="1070">
<Group alpha="0.18">
<Path data="M -400 -40 L 400 -40"/>
<Stroke width="1">
<LinearGradient>
<ColorStop color="#6366F100" offset="0"/>
<ColorStop color="#6366F1" offset="0.2"/>
<ColorStop color="#8B5CF6" offset="0.5"/>
<ColorStop color="#EC4899" offset="0.8"/>
<ColorStop color="#EC489900" offset="1"/>
</LinearGradient>
</Stroke>
</Group>
<Group left="-180">
<Rectangle left="-60" top="-20" width="120" height="40" roundness="20"/>
<Fill color="#6366F120"/>
<Stroke color="#6366F160" width="1"/>
<TextBox left="-60" top="-20" width="120" height="40" textAlign="center" paragraphAlign="middle">
<Text text=".pagx" fontFamily="Arial" fontStyle="Bold" fontSize="16"/>
<Fill color="#A78BFA"/>
</TextBox>
</Group>
<Group left="-95" top="-6">
<Path data="@arrowRight"/>
<Stroke color="#64748B" width="2" cap="round" join="round"/>
</Group>
<Group>
<Rectangle left="-50" top="-20" width="100" height="40" roundness="20"/>
<Fill color="#EC489920"/>
<Stroke color="#EC489960" width="1"/>
<TextBox left="-50" top="-20" width="100" height="40" textAlign="center" paragraphAlign="middle">
<Text text=".pag" fontFamily="Arial" fontStyle="Bold" fontSize="16"/>
<Fill color="#F472B6"/>
</TextBox>
</Group>
<Group left="75" top="-6">
<Path data="@arrowRight"/>
<Stroke color="#64748B" width="2" cap="round" join="round"/>
</Group>
<Group left="190">
<Rectangle left="-70" top="-20" width="140" height="40" roundness="20"/>
<Fill color="#34D39920"/>
<Stroke color="#34D39960" width="1"/>
<TextBox left="-70" top="-20" width="140" height="40" textAlign="center" paragraphAlign="middle">
<Text text="Production" fontFamily="Arial" fontStyle="Bold" fontSize="16"/>
<Fill color="#34D399"/>
</TextBox>
</Group>
</Layer>
<Layer name="BottomTagline" top="1122" centerX="0">
<TextBox textAlign="center">
<Text text="Design - Develop - Deploy" fontFamily="Arial" fontSize="16"/>
<Fill color="#64748B"/>
</TextBox>
</Layer>
<Layer name="Corners" alpha="0.2">
<Path data="M 50 110 L 50 50 L 110 50 M 1490 50 L 1550 50 L 1550 110 M 50 1090 L 50 1150 L 110 1150 M 1490 1150 L 1550 1150 L 1550 1090"/>
<Stroke color="#64748B" width="1" cap="round" join="round"/>
<Group>
<Ellipse left="48" top="48" width="4" height="4"/>
<Ellipse left="1548" top="48" width="4" height="4"/>
<Ellipse left="48" top="1148" width="4" height="4"/>
<Ellipse left="1548" top="1148" width="4" height="4"/>
<Fill color="#64748B"/>
</Group>
</Layer>
<Resources>
<PathData id="cardTopLine" data="M 0 0 L 360 0"/>
<PathData id="arrowRight" data="M 0 6 L 25 6 M 19 0 L 27 6 L 19 12"/>
</Resources>
</pagx>
C.6 太空探索者
一幅外星球探险插画,包含宇航员、奇异植物、外星生物和大气效果。展示了复杂场景合成、分层背景、手绘风格 Path 图形、通过超长 Path 数据程序化生成的草地纹理以及丰富的渐变光效。
<?xml version="1.0" encoding="UTF-8"?>
<pagx width="1200" height="800">
<Layer id="sky" left="600" top="400">
<Rectangle left="-600" top="-400" width="1200" height="800"/>
<Fill>
<LinearGradient endPoint="0,1">
<ColorStop color="#020810" offset="0"/>
<ColorStop color="#06122E" offset="0.15"/>
<ColorStop color="#0E1450" offset="0.35"/>
<ColorStop color="#1A1060" offset="0.5"/>
<ColorStop color="#2D1B69" offset="0.65"/>
<ColorStop color="#4A1942" offset="0.8"/>
<ColorStop color="#6B2040" offset="0.92"/>
<ColorStop color="#7A2545" offset="1"/>
</LinearGradient>
</Fill>
</Layer>
<Layer left="600" top="250" alpha="0.22" blendMode="screen">
<Ellipse left="-600" top="-100" width="1200" height="200"/>
<Fill>
<RadialGradient radius="600" fitsToGeometry="false">
<ColorStop color="#C4B5FD55" offset="0"/>
<ColorStop color="#A78BFA30" offset="0.3"/>
<ColorStop color="#7C3AED18" offset="0.6"/>
<ColorStop color="#7C3AED00" offset="1"/>
</RadialGradient>
</Fill>
<BlurFilter blurX="50" blurY="25"/>
</Layer>
<Layer left="150" top="200" alpha="0.5" blendMode="screen">
<Ellipse left="-275" top="-225" width="550" height="450"/>
<Fill>
<RadialGradient radius="275" fitsToGeometry="false">
<ColorStop color="#7C3AED90" offset="0"/>
<ColorStop color="#7C3AED45" offset="0.4"/>
<ColorStop color="#7C3AED18" offset="0.7"/>
<ColorStop color="#7C3AED00" offset="1"/>
</RadialGradient>
</Fill>
<BlurFilter blurX="45" blurY="45"/>
</Layer>
<Layer left="1000" top="120" alpha="0.45" blendMode="screen">
<Ellipse left="-240" top="-180" width="480" height="360"/>
<Fill>
<RadialGradient radius="240" fitsToGeometry="false">
<ColorStop color="#06B6D470" offset="0"/>
<ColorStop color="#06B6D430" offset="0.4"/>
<ColorStop color="#06B6D415" offset="0.7"/>
<ColorStop color="#06B6D400" offset="1"/>
</RadialGradient>
</Fill>
<BlurFilter blurX="40" blurY="40"/>
</Layer>
<Layer left="550" top="90" alpha="0.28" blendMode="screen">
<Ellipse left="-200" top="-110" width="400" height="220"/>
<Fill>
<RadialGradient radius="200" fitsToGeometry="false">
<ColorStop color="#EC489955" offset="0"/>
<ColorStop color="#EC489922" offset="0.5"/>
<ColorStop color="#EC489900" offset="1"/>
</RadialGradient>
</Fill>
<BlurFilter blurX="30" blurY="30"/>
</Layer>
<Layer left="80" top="55" composition="@comp1"/>
<Layer left="150" top="125" composition="@comp4"/>
<Layer left="260" top="42" composition="@comp5"/>
<Layer left="340" top="175" composition="@comp1"/>
<Layer left="420" top="68" composition="@comp6"/>
<Layer left="530" top="22">
<Ellipse left="-2" top="-2" width="4" height="4"/>
<Fill color="#FFFFFF"/>
</Layer>
<Layer left="610" top="150" composition="@comp4"/>
<Layer left="700" top="48" composition="@comp1"/>
<Layer left="780" top="108" composition="@comp5"/>
<Layer left="850" top="32" composition="@comp1"/>
<Layer left="940" top="88" composition="@comp6"/>
<Layer left="1020" top="58" composition="@comp1"/>
<Layer left="1100" top="138" composition="@comp4"/>
<Layer left="180" top="275" composition="@comp5"/>
<Layer left="480" top="235" composition="@comp7"/>
<Layer left="750" top="255" composition="@comp2"/>
<Layer left="1050" top="218" composition="@comp7"/>
<Layer left="320" top="315" composition="@comp4"/>
<Layer left="60" top="345" composition="@comp1"/>
<Layer left="45" top="160" composition="@comp5"/>
<Layer left="200" top="380" composition="@comp6"/>
<Layer left="370" top="260" composition="@comp1"/>
<Layer left="560" top="310" composition="@comp4"/>
<Layer left="650" top="200" composition="@comp5"/>
<Layer left="900" top="195" composition="@comp6"/>
<Layer left="1150" top="80" composition="@comp7"/>
<Layer left="440" top="150" composition="@comp4"/>
<Layer left="1080" top="350" composition="@comp5"/>
<Layer left="30" top="450" composition="@comp7"/>
<Layer left="680" top="350" composition="@comp2"/>
<Layer left="300" top="95" alpha="0.9">
<Ellipse left="-3" top="-3" width="6" height="6"/>
<Fill color="#FFFFFF"/>
<Group>
<Ellipse left="-11" top="-11" width="22" height="22"/>
<Fill>
<RadialGradient radius="11" fitsToGeometry="false">
<ColorStop color="#FFFFFF60" offset="0"/>
<ColorStop color="#FFFFFF00" offset="1"/>
</RadialGradient>
</Fill>
</Group>
<Group>
<Path data="M-14 0L14 0M0 -14L0 14"/>
<Stroke color="#FFFFFF50"/>
</Group>
</Layer>
<Layer left="820" top="65" alpha="0.85">
<Ellipse left="-2.5" top="-2.5" width="5" height="5"/>
<Fill color="#FFFFFF"/>
<Group>
<Ellipse left="-9" top="-9" width="18" height="18"/>
<Fill>
<RadialGradient radius="9" fitsToGeometry="false">
<ColorStop color="#FFFFFF50" offset="0"/>
<ColorStop color="#FFFFFF00" offset="1"/>
</RadialGradient>
</Fill>
</Group>
<Group>
<Path data="M-10 0L10 0M0 -10L0 10"/>
<Stroke color="#FFFFFF40"/>
</Group>
</Layer>
<Layer left="1100" top="295" alpha="0.8">
<Ellipse left="-2.5" top="-2.5" width="5" height="5"/>
<Fill color="#FFE4B5"/>
<Group>
<Ellipse left="-8" top="-8" width="16" height="16"/>
<Fill>
<RadialGradient radius="8" fitsToGeometry="false">
<ColorStop color="#FFE4B540" offset="0"/>
<ColorStop color="#FFE4B500" offset="1"/>
</RadialGradient>
</Fill>
</Group>
</Layer>
<Layer left="550" top="180" alpha="0.75">
<Ellipse left="-2" top="-2" width="4" height="4"/>
<Fill color="#E0E7FF"/>
<Group>
<Ellipse left="-7" top="-7" width="14" height="14"/>
<Fill>
<RadialGradient radius="7" fitsToGeometry="false">
<ColorStop color="#E0E7FF40" offset="0"/>
<ColorStop color="#E0E7FF00" offset="1"/>
</RadialGradient>
</Fill>
</Group>
<Group>
<Path data="M-8 0L8 0M0 -8L0 8"/>
<Stroke color="#E0E7FF30"/>
</Group>
</Layer>
<Layer left="1020" top="140">
<Ellipse left="-65" top="-65" width="130" height="130"/>
<Fill>
<RadialGradient center="-20,-20" radius="80" fitsToGeometry="false">
<ColorStop color="#F97316" offset="0"/>
<ColorStop color="#EA580C" offset="0.35"/>
<ColorStop color="#C2410C" offset="0.65"/>
<ColorStop color="#7C2D12" offset="1"/>
</RadialGradient>
</Fill>
<Group>
<Path data="M-55 -8C-30 -12 30 -4 55 -8"/>
<Stroke color="#FBBF2420" width="4"/>
</Group>
<Group>
<Path data="M-50 14C-15 10 20 16 50 14"/>
<Stroke color="#F9731620" width="5"/>
</Group>
<Group>
<Path data="M-45 32C-10 28 15 34 45 32"/>
<Stroke color="#FBBF2415" width="3"/>
</Group>
<Group rotation="-20">
<Ellipse left="-100" top="-21" width="200" height="42"/>
<Stroke color="#F9731660" width="5"/>
</Group>
<Group rotation="-20">
<Ellipse left="-85" top="-15" width="170" height="30"/>
<Stroke color="#FB923C40" width="3"/>
</Group>
<Group>
<Ellipse left="-75" top="-75" width="150" height="150"/>
<Fill>
<RadialGradient radius="75" fitsToGeometry="false">
<ColorStop color="#F9731600" offset="0.7"/>
<ColorStop color="#F9731635" offset="1"/>
</RadialGradient>
</Fill>
</Group>
</Layer>
<Layer left="420" top="105">
<Ellipse left="-14" top="-14" width="28" height="28"/>
<Fill>
<RadialGradient center="-4,-4" radius="16" fitsToGeometry="false">
<ColorStop color="#A78BFA" offset="0"/>
<ColorStop color="#7C3AED" offset="0.6"/>
<ColorStop color="#4C1D95" offset="1"/>
</RadialGradient>
</Fill>
<Group>
<Ellipse left="-18" top="-18" width="36" height="36"/>
<Fill>
<RadialGradient radius="18" fitsToGeometry="false">
<ColorStop color="#A78BFA00" offset="0.7"/>
<ColorStop color="#A78BFA25" offset="1"/>
</RadialGradient>
</Fill>
</Group>
</Layer>
<Layer id="planet" left="60" top="580">
<Ellipse left="-210" top="-210" width="420" height="420"/>
<Fill>
<RadialGradient center="-40,-60" radius="230" fitsToGeometry="false">
<ColorStop color="#3B82F6" offset="0"/>
<ColorStop color="#2563EB" offset="0.3"/>
<ColorStop color="#1D4ED8" offset="0.6"/>
<ColorStop color="#1E3A5F" offset="1"/>
</RadialGradient>
</Fill>
<Group>
<Path data="M-180 -40C-130 -46 -50 -34 0 -40C50 -46 130 -34 180 -40"/>
<Stroke color="#60A5FA25" width="10"/>
</Group>
<Group>
<Path data="M-170 25C-100 18 -30 32 40 25C100 18 170 32 190 25"/>
<Stroke color="#93C5FD20" width="8"/>
</Group>
<Group>
<Path data="M-160 75C-90 68 -10 82 50 75C110 68 175 82 190 75"/>
<Stroke color="#60A5FA18" width="12"/>
</Group>
<Group>
<Path data="M-150 -80C-80 -86 10 -74 80 -80"/>
<Stroke color="#93C5FD15" width="6"/>
</Group>
<Group>
<Ellipse left="-225" top="-225" width="450" height="450"/>
<Fill>
<RadialGradient radius="225" fitsToGeometry="false">
<ColorStop color="#3B82F600" offset="0.8"/>
<ColorStop color="#3B82F635" offset="1"/>
</RadialGradient>
</Fill>
</Group>
</Layer>
<Layer id="mountains" alpha="0.55">
<Path data="M0 560L50 515L100 535L180 475L230 498L300 452L360 485L420 442L480 465L540 422L580 450L650 412L700 438L760 408L820 432L880 402L940 428L1000 392L1050 418L1100 398L1150 422L1200 405L1200 560Z"/>
<Fill>
<LinearGradient startPoint="600,-85" endPoint="600,85" fitsToGeometry="false">
<ColorStop color="#1A0F3A" offset="0"/>
<ColorStop color="#150C30" offset="0.5"/>
<ColorStop color="#100825" offset="1"/>
</LinearGradient>
</Fill>
<Group>
<Path data="M0 560L50 515L100 535L180 475L230 498L300 452L360 485L420 442L480 465L540 422L580 450L650 412L700 438L760 408L820 432L880 402L940 428L1000 392L1050 418L1100 398L1150 422L1200 405"/>
<Stroke color="#8B5CF625" width="1.5"/>
</Group>
</Layer>
<Layer id="terrain">
<Path data="M0 800L0 605C60 595 120 580 200 575C300 568 380 585 440 570C500 550 540 560 600 545C660 530 720 545 780 538C840 530 900 550 960 540C1020 530 1080 545 1140 538L1200 535L1200 800Z"/>
<Fill>
<LinearGradient startPoint="600,-135" endPoint="600,135" fitsToGeometry="false">
<ColorStop color="#2D1854" offset="0"/>
<ColorStop color="#1F1240" offset="0.3"/>
<ColorStop color="#150D30" offset="0.7"/>
<ColorStop color="#0A0618" offset="1"/>
</LinearGradient>
</Fill>
<Group>
<Path data="M0 605C60 595 120 580 200 575C300 568 380 585 440 570C500 550 540 560 600 545C660 530 720 545 780 538C840 530 900 550 960 540C1020 530 1080 545 1140 538L1200 535"/>
<Stroke color="#8B5CF640" width="2"/>
</Group>
<Group>
<Path data="M0 800L0 645C80 640 160 628 240 638C340 650 400 634 480 628C560 622 640 636 720 628C800 620 880 634 960 628C1040 622 1120 636 1200 628L1200 800Z"/>
<Fill color="#18103A"/>
</Group>
<Group>
<Path data="M200 615L225 632L255 624L275 642"/>
<Stroke color="#8B5CF615"/>
</Group>
<Group>
<Path data="M650 575L675 592L705 584"/>
<Stroke color="#8B5CF612"/>
</Group>
<Group>
<Path data="M900 568L935 582L955 574L985 590"/>
<Stroke color="#8B5CF610"/>
</Group>
</Layer>
<Layer id="crater1" left="400" top="570">
<Ellipse left="-40" top="-10" width="80" height="20"/>
<Fill>
<RadialGradient radius="40" fitsToGeometry="false">
<ColorStop color="#0A0618" offset="0"/>
<ColorStop color="#120B28" offset="0.6"/>
<ColorStop color="#1F1240" offset="1"/>
</RadialGradient>
</Fill>
<Stroke color="#8B5CF625" width="1.5"/>
<Group>
<Ellipse left="-28" top="-4" width="56" height="12"/>
<Fill>
<RadialGradient center="0,2" radius="28" fitsToGeometry="false">
<ColorStop color="#050310" offset="0"/>
<ColorStop color="#05031000" offset="1"/>
</RadialGradient>
</Fill>
</Group>
</Layer>
<Layer id="crater2" left="760" top="539">
<Ellipse left="-25" top="-7" width="50" height="14"/>
<Fill>
<RadialGradient radius="25" fitsToGeometry="false">
<ColorStop color="#0A0618" offset="0"/>
<ColorStop color="#120B28" offset="0.6"/>
<ColorStop color="#1F1240" offset="1"/>
</RadialGradient>
</Fill>
<Stroke color="#8B5CF620"/>
</Layer>
<Layer id="crystal1" left="140" top="563">
<Path data="M-8 25L-15 0L-5 -50L5 -60L12 -10L8 25Z"/>
<Fill>
<LinearGradient startPoint="-10,0" endPoint="10,0" fitsToGeometry="false">
<ColorStop color="#4C1D95" offset="0"/>
<ColorStop color="#6D28D9" offset="0.5"/>
<ColorStop color="#4C1D95" offset="1"/>
</LinearGradient>
</Fill>
<Group>
<Path data="M-5 -45L0 -30L5 -55"/>
<Stroke color="#A78BFA40"/>
</Group>
<Group>
<Path data="M-2 -55L0 -65L2 -55"/>
<Stroke color="#A78BFA" width="2" cap="round"/>
</Group>
<Group>
<Ellipse left="-8" top="-70" width="16" height="16"/>
<Fill>
<RadialGradient center="0,-62" radius="8" fitsToGeometry="false">
<ColorStop color="#A78BFA80" offset="0"/>
<ColorStop color="#A78BFA00" offset="1"/>
</RadialGradient>
</Fill>
</Group>
</Layer>
<Layer id="crystal2" left="1060" top="516">
<Path data="M-12 22L-18 -10L-8 -55L0 -65L8 -50L15 -5L10 22Z"/>
<Fill>
<LinearGradient startPoint="-12,0" endPoint="12,0" fitsToGeometry="false">
<ColorStop color="#7E22CE" offset="0"/>
<ColorStop color="#A855F7" offset="0.5"/>
<ColorStop color="#7E22CE" offset="1"/>
</LinearGradient>
</Fill>
<Group>
<Path data="M18 22L22 -5L28 -35L32 -42L36 -22L34 22Z"/>
<Fill>
<LinearGradient startPoint="22,0" endPoint="36,0" fitsToGeometry="false">
<ColorStop color="#6D28D9" offset="0"/>
<ColorStop color="#8B5CF6" offset="0.5"/>
<ColorStop color="#6D28D9" offset="1"/>
</LinearGradient>
</Fill>
</Group>
<Group>
<Path data="M-22 22L-28 5L-24 -18L-20 -24L-16 -12L-18 22Z"/>
<Fill>
<LinearGradient startPoint="-28,0" endPoint="-16,0" fitsToGeometry="false">
<ColorStop color="#5B21B6" offset="0"/>
<ColorStop color="#7C3AED" offset="0.5"/>
<ColorStop color="#5B21B6" offset="1"/>
</LinearGradient>
</Fill>
</Group>
<Group>
<Ellipse left="-9" top="-74" width="18" height="18"/>
<Fill>
<RadialGradient center="0,-65" radius="9" fitsToGeometry="false">
<ColorStop color="#C4B5FDA0" offset="0"/>
<ColorStop color="#C4B5FD00" offset="1"/>
</RadialGradient>
</Fill>
</Group>
<Group>
<Ellipse left="25" top="-49" width="14" height="14"/>
<Fill>
<RadialGradient center="32,-42" radius="7" fitsToGeometry="false">
<ColorStop color="#C4B5FD80" offset="0"/>
<ColorStop color="#C4B5FD00" offset="1"/>
</RadialGradient>
</Fill>
</Group>
</Layer>
<Layer left="348.5" top="583.5">
<Path data="M-10.5 11.5L-13.5 -1.5L-4.5 -11.5L7.5 -8.5L13.5 1.5L11.5 11.5Z"/>
<Fill color="#2D1854"/>
</Layer>
<Layer left="499" top="570">
<Path data="M-7 8L-9 -2L-2 -8L6 -6L9 2L7 8Z"/>
<Fill color="#241548"/>
</Layer>
<Layer left="679" top="547">
<Path data="M-9 11L-11 -2L-3 -11L6 -9L11 1L9 11Z"/>
<Fill color="#3B1F6E"/>
</Layer>
<Layer left="829" top="543.5">
<Path data="M-5 6.5L-7 -1.5L-1 -6.5L5 -4.5L7 1.5L5 6.5Z"/>
<Fill color="#2D1854"/>
</Layer>
<Layer left="959" top="548">
<Path data="M-7 8L-9 -2L-2 -8L6 -6L9 1L7 8Z"/>
<Fill color="#251448"/>
</Layer>
<Layer left="260" top="568">
<Path data="M0 10C-2 -5 2 -20 -3 -38C-5 -45 -2 -52 0 -58"/>
<Stroke color="#10B981" width="3" cap="round"/>
<Group>
<Path data="M-3 -25C-22 -38 -28 -22 -14 -19"/>
<Path data="M2 -38C20 -50 25 -34 12 -31"/>
<Fill color="#059669"/>
</Group>
<Group>
<Path data="M-2 -48C-14 -53 -16 -42 -8 -41"/>
<Fill color="#047857"/>
</Group>
<Group>
<Ellipse left="-5" top="-67" width="10" height="12"/>
<Fill>
<RadialGradient center="0,-61" radius="6" fitsToGeometry="false">
<ColorStop color="#34D399" offset="0"/>
<ColorStop color="#10B981" offset="0.6"/>
<ColorStop color="#059669" offset="1"/>
</RadialGradient>
</Fill>
</Group>
<Group>
<Ellipse left="-17" top="-78" width="34" height="34"/>
<Fill>
<RadialGradient center="0,-61" radius="17" fitsToGeometry="false">
<ColorStop color="#34D39968" offset="0"/>
<ColorStop color="#34D39935" offset="0.5"/>
<ColorStop color="#34D39900" offset="1"/>
</RadialGradient>
</Fill>
</Group>
</Layer>
<Layer id="vine" left="910" top="542">
<Path data="M0 10C3 -8 -1 -25 2 -42C4 -50 1 -58 0 -65"/>
<Stroke color="#14B8A6" width="2.5" cap="round"/>
<Group>
<Path data="M-1 -20C-18 -30 -22 -16 -10 -14M2 -40C17 -50 20 -36 10 -34M-2 -52C-14 -58 -16 -48 -8 -46"/>
<Fill color="#0D9488"/>
</Group>
<Group>
<Ellipse left="-4" top="-73" width="8" height="10"/>
<Fill>
<RadialGradient center="0,-68" radius="5" fitsToGeometry="false">
<ColorStop color="#2DD4BF" offset="0"/>
<ColorStop color="#14B8A6" offset="0.6"/>
<ColorStop color="#0D9488" offset="1"/>
</RadialGradient>
</Fill>
</Group>
<Group>
<Ellipse left="-14" top="-82" width="28" height="28"/>
<Fill>
<RadialGradient center="0,-68" radius="14" fitsToGeometry="false">
<ColorStop color="#2DD4BF58" offset="0"/>
<ColorStop color="#2DD4BF28" offset="0.5"/>
<ColorStop color="#2DD4BF00" offset="1"/>
</RadialGradient>
</Fill>
</Group>
</Layer>
<Layer id="plant2" left="1130" top="538">
<Path data="M0 8C-1 -5 1 -20 -2 -32"/>
<Stroke color="#10B981" width="2" cap="round"/>
<Group>
<Path data="M14 8C16 -3 13 -18 15 -26"/>
<Stroke color="#14B8A6" width="2" cap="round"/>
</Group>
<Group>
<Ellipse left="-3.5" top="-39" width="7" height="8"/>
<Fill color="#34D399"/>
</Group>
<Group>
<Ellipse left="11" top="-32.5" width="6" height="7"/>
<Fill color="#2DD4BF"/>
</Group>
<Group>
<Ellipse left="-7" top="-45" width="26" height="26"/>
<Fill>
<RadialGradient center="6,-32" radius="13" fitsToGeometry="false">
<ColorStop color="#34D39958" offset="0"/>
<ColorStop color="#34D39900" offset="1"/>
</RadialGradient>
</Fill>
</Group>
</Layer>
<Layer id="plant1" left="455" top="565">
<Path data="M0 6C1 -4 -1 -14 0 -22"/>
<Stroke color="#10B981" width="2" cap="round"/>
<Group>
<Path data="M-1 -12C-10 -18 -12 -10 -6 -9"/>
<Fill color="#059669"/>
</Group>
<Group>
<Ellipse left="-3" top="-29" width="6" height="8"/>
<Fill color="#34D399"/>
</Group>
<Group>
<Ellipse left="-10" top="-35" width="20" height="20"/>
<Fill>
<RadialGradient center="0,-25" radius="10" fitsToGeometry="false">
<ColorStop color="#34D39955" offset="0"/>
<ColorStop color="#34D39900" offset="1"/>
</RadialGradient>
</Fill>
</Group>
</Layer>
<Layer id="alien1" left="860" top="523">
<Ellipse left="-9" top="-7" width="18" height="14"/>
<Fill>
<RadialGradient radius="9" fitsToGeometry="false">
<ColorStop color="#A78BFA" offset="0"/>
<ColorStop color="#7C3AED" offset="0.7"/>
<ColorStop color="#6D28D9" offset="1"/>
</RadialGradient>
</Fill>
<Group>
<Ellipse left="-6.5" top="-6" width="5" height="6"/>
<Ellipse left="1.5" top="-6" width="5" height="6"/>
<Fill color="#E0E7FF"/>
</Group>
<Group>
<Ellipse left="-5" top="-4.5" width="2" height="3"/>
<Ellipse left="3" top="-4.5" width="2" height="3"/>
<Fill color="#1E1B4B"/>
</Group>
<Group>
<Path data="M-3 -7C-6 -15 -8 -18 -10 -22M3 -7C6 -15 8 -18 10 -22"/>
<Stroke color="#A78BFA" width="1.5" cap="round"/>
</Group>
<Group>
<Ellipse left="-12.5" top="-24.5" width="5" height="5"/>
<Ellipse left="7.5" top="-24.5" width="5" height="5"/>
<Fill color="#C4B5FD"/>
</Group>
<Group>
<Path data="M-6 5L-8 12M6 5L8 12M-2 6L-3 13M2 6L3 13"/>
<Stroke color="#7C3AED" width="2" cap="round"/>
</Group>
</Layer>
<Layer id="alien2" left="330" top="573">
<Ellipse left="-7" top="-5" width="14" height="10"/>
<Fill>
<RadialGradient radius="7" fitsToGeometry="false">
<ColorStop color="#67E8F9" offset="0"/>
<ColorStop color="#22D3EE" offset="0.7"/>
<ColorStop color="#0891B2" offset="1"/>
</RadialGradient>
</Fill>
<Group>
<Ellipse left="-5" top="-4.5" width="4" height="5"/>
<Ellipse left="1" top="-4.5" width="4" height="5"/>
<Fill color="#E0F2FE"/>
</Group>
<Group>
<Ellipse left="-4" top="-3" width="2" height="2"/>
<Ellipse left="2" top="-3" width="2" height="2"/>
<Fill color="#164E63"/>
</Group>
<Group>
<Path data="M-2 -5C-4 -12 -3 -15 -5 -18M2 -5C4 -12 3 -15 5 -18"/>
<Stroke color="#22D3EE" width="1.5" cap="round"/>
</Group>
<Group>
<Ellipse left="-6.5" top="-19.5" width="3" height="3"/>
<Ellipse left="3.5" top="-19.5" width="3" height="3"/>
<Fill color="#67E8F9"/>
</Group>
</Layer>
<Layer id="ship" left="280" top="310">
<Path data="M-15 30C-28 58 -12 95 0 120C12 95 28 58 15 30"/>
<Fill>
<LinearGradient startPoint="0,30" endPoint="0,120" fitsToGeometry="false">
<ColorStop color="#38BDF890" offset="0"/>
<ColorStop color="#06B6D460" offset="0.3"/>
<ColorStop color="#0891B230" offset="0.6"/>
<ColorStop color="#0891B200" offset="1"/>
</LinearGradient>
</Fill>
<Group>
<Path data="M-38 10C-42 0 -32 -16 -22 -24C-12 -30 12 -30 22 -24C32 -16 42 0 38 10C28 20 -28 20 -38 10Z"/>
<Fill>
<LinearGradient startPoint="0,-28" endPoint="0,20" fitsToGeometry="false">
<ColorStop color="#94A3B8" offset="0"/>
<ColorStop color="#CBD5E1" offset="0.3"/>
<ColorStop color="#E2E8F0" offset="0.6"/>
<ColorStop color="#94A3B8" offset="1"/>
</LinearGradient>
</Fill>
</Group>
<Group>
<Path data="M-30 5C-15 8 15 8 30 5"/>
<Stroke color="#64748B"/>
</Group>
<Group>
<Path data="M-14 -24C-14 -38 14 -38 14 -24"/>
<Fill>
<LinearGradient startPoint="-14,-38" endPoint="14,-24" fitsToGeometry="false">
<ColorStop color="#38BDF8" offset="0"/>
<ColorStop color="#7DD3FC" offset="0.5"/>
<ColorStop color="#38BDF8" offset="1"/>
</LinearGradient>
</Fill>
</Group>
<Group>
<Path data="M-7 -33C-7 -36 2 -37 4 -35"/>
<Stroke color="#FFFFFF80" width="1.5" cap="round"/>
</Group>
<Group>
<Path data="M-32 10C-22 16 22 16 32 10"/>
<Stroke color="#64748B" width="2"/>
</Group>
<Group>
<Ellipse left="-22" top="12.5" width="4" height="3"/>
<Ellipse left="-2" top="14.5" width="4" height="3"/>
<Ellipse left="18" top="12.5" width="4" height="3"/>
<Fill color="#FBBF24"/>
</Group>
<Group>
<Ellipse left="-47.5" top="-27.5" width="95" height="55"/>
<Fill>
<RadialGradient radius="48" fitsToGeometry="false">
<ColorStop color="#38BDF800" offset="0.6"/>
<ColorStop color="#38BDF820" offset="1"/>
</RadialGradient>
</Fill>
</Group>
<DropShadowStyle offsetY="5" blurX="18" blurY="18" color="#38BDF850"/>
</Layer>
<Layer left="499" top="344">
<Path data="M-9 7L-11 -3L-4 -9L6 -7L11 1L8 9Z"/>
<Fill color="#3B2060"/>
<Stroke color="#8B5CF620"/>
</Layer>
<Layer left="559.5" top="379.5">
<Path data="M-5.5 4.5L-6.5 -2.5L-1.5 -5.5L4.5 -4.5L6.5 1.5L4.5 5.5Z"/>
<Fill color="#2D1854"/>
</Layer>
<Layer left="469.5" top="394.5">
<Path data="M-4.5 3.5L-5.5 -1.5L-1.5 -4.5L3.5 -3.5L5.5 1.5L3.5 4.5Z"/>
<Fill color="#351C5A"/>
</Layer>
<Layer left="640" top="314.5">
<Path data="M-7 5.5L-8 -2.5L-3 -6.5L4 -5.5L8 1.5L5 6.5Z"/>
<Fill color="#2D1854"/>
<Stroke color="#8B5CF615"/>
</Layer>
<Layer left="789.5" top="354.5">
<Path data="M-4.5 4.5L-5.5 -1.5L-1.5 -4.5L4.5 -3.5L5.5 2.5L3.5 4.5Z"/>
<Fill color="#301A58"/>
</Layer>
<Layer id="station" left="1130" top="395" alpha="0.65">
<Rectangle left="-6" top="-4" width="12" height="8" roundness="2"/>
<Fill color="#94A3B8"/>
<Group>
<Rectangle left="-20" top="-2" width="12" height="4"/>
<Rectangle left="8" top="-2" width="12" height="4"/>
<Fill color="#38BDF8"/>
</Group>
<Group>
<Path data="M-8 0L-20 0M8 0L20 0"/>
<Stroke color="#94A3B8"/>
</Group>
<Group>
<Ellipse left="-1" top="-6" width="2" height="2"/>
<Fill color="#EF4444"/>
</Group>
</Layer>
<Layer id="shadow" left="600" top="545">
<Ellipse left="-70" top="-12.5" width="140" height="25"/>
<Fill>
<RadialGradient radius="70" fitsToGeometry="false">
<ColorStop color="#00000060" offset="0"/>
<ColorStop color="#00000030" offset="0.6"/>
<ColorStop color="#00000000" offset="1"/>
</RadialGradient>
</Fill>
</Layer>
<Layer id="astronaut" left="600" top="433">
<Rectangle left="10" top="-19" width="30" height="54" roundness="6"/>
<Fill>
<LinearGradient startPoint="10,0" endPoint="40,0" fitsToGeometry="false">
<ColorStop color="#94A3B8" offset="0"/>
<ColorStop color="#CBD5E1" offset="0.5"/>
<ColorStop color="#94A3B8" offset="1"/>
</LinearGradient>
</Fill>
<Group>
<Path data="M14 -12L36 -12M14 5L36 5M14 20L36 20"/>
<Stroke color="#64748B"/>
</Group>
<Group>
<Ellipse left="17" top="28" width="6" height="4"/>
<Ellipse left="27" top="28" width="6" height="4"/>
<Fill color="#475569"/>
</Group>
<Group>
<Path data="M32 -16L34 -38L36 -42"/>
<Stroke color="#94A3B8" width="2" cap="round"/>
</Group>
<Group>
<Ellipse left="33.5" top="-44.5" width="5" height="5"/>
<Fill color="#EF4444"/>
</Group>
<Group>
<Ellipse left="29" top="-49" width="14" height="14"/>
<Fill>
<RadialGradient center="36,-42" radius="7" fitsToGeometry="false">
<ColorStop color="#EF444470" offset="0"/>
<ColorStop color="#EF444400" offset="1"/>
</RadialGradient>
</Fill>
</Group>
<Group>
<Path data="M-22 -22C-27 -10 -27 16 -24 32C-22 42 -16 48 0 50C16 48 22 42 24 32C27 16 27 -10 22 -22C12 -27 -12 -27 -22 -22Z"/>
<Fill>
<LinearGradient startPoint="-27,0" endPoint="27,0" fitsToGeometry="false">
<ColorStop color="#94A3B8" offset="0"/>
<ColorStop color="#CBD5E1" offset="0.15"/>
<ColorStop color="#F1F5F9" offset="0.5"/>
<ColorStop color="#CBD5E1" offset="0.85"/>
<ColorStop color="#94A3B8" offset="1"/>
</LinearGradient>
</Fill>
</Group>
<Group>
<Path data="M0 -22L0 48"/>
<Stroke color="#E2E8F0" width="3"/>
</Group>
<Group>
<Ellipse left="-22" top="-17" width="8" height="6"/>
<Fill color="#EF4444"/>
</Group>
<Group>
<Ellipse left="14" top="-17" width="8" height="6"/>
<Fill color="#3B82F6"/>
</Group>
<Group>
<Rectangle left="-11" top="-4" width="22" height="18" roundness="3"/>
<Fill color="#475569"/>
</Group>
<Group>
<Ellipse left="-6" width="4" height="4"/>
<Fill color="#22C55E"/>
</Group>
<Group>
<Ellipse left="2" width="4" height="4"/>
<Fill color="#EAB308"/>
</Group>
<Group>
<Ellipse left="-6" top="6" width="4" height="4"/>
<Fill color="#3B82F6"/>
</Group>
<Group>
<Ellipse left="2" top="6" width="4" height="4"/>
<Fill color="#EF4444"/>
</Group>
<Group>
<Rectangle left="-7" top="12" width="14" height="4" roundness="1"/>
<Fill color="#0F172A"/>
</Group>
<Group>
<Rectangle left="-5" top="13" width="6" height="2" roundness="1"/>
<Fill color="#22D3EE60"/>
</Group>
<Group>
<Path data="M-24 32C-12 36 12 36 24 32"/>
<Stroke color="#64748B" width="4"/>
</Group>
<Group>
<Rectangle left="-5" top="30" width="10" height="8" roundness="2"/>
<Fill color="#94A3B8"/>
</Group>
<Group>
<Rectangle left="-18" top="33" width="8" height="6" roundness="2"/>
<Rectangle left="10" top="33" width="8" height="6" roundness="2"/>
<Fill color="#64748B"/>
</Group>
<Group>
<Path data="M-22 -12C-30 -15 -38 -20 -44 -26C-46 -28 -46 -30 -46 -32"/>
<Stroke color="#CBD5E1" width="14" cap="round" join="round"/>
</Group>
<Group>
<Ellipse left="-54" top="-54" width="16" height="18"/>
<Fill>
<LinearGradient startPoint="-54,-45" endPoint="-38,-45" fitsToGeometry="false">
<ColorStop color="#94A3B8" offset="0"/>
<ColorStop color="#E2E8F0" offset="0.5"/>
<ColorStop color="#94A3B8" offset="1"/>
</LinearGradient>
</Fill>
</Group>
<Group>
<Path data="M-50 -52L-50 -58M-46 -53L-46 -60M-42 -52L-42 -58"/>
<Stroke color="#CBD5E1" width="3" cap="round"/>
</Group>
<Group>
<Ellipse left="-54" top="-39" width="16" height="6"/>
<Fill color="#64748B"/>
</Group>
<Group>
<Path data="M22 -12C32 -5 40 12 44 28"/>
<Stroke color="#CBD5E1" width="14" cap="round" join="round"/>
</Group>
<Group>
<Ellipse left="36" top="21" width="16" height="18"/>
<Fill>
<LinearGradient startPoint="36,30" endPoint="52,30" fitsToGeometry="false">
<ColorStop color="#94A3B8" offset="0"/>
<ColorStop color="#E2E8F0" offset="0.5"/>
<ColorStop color="#94A3B8" offset="1"/>
</LinearGradient>
</Fill>
</Group>
<Group>
<Ellipse left="36" top="17" width="16" height="6"/>
<Fill color="#64748B"/>
</Group>
<Group>
<Rectangle left="40" top="7" width="8" height="22" roundness="2"/>
<Fill color="#475569"/>
</Group>
<Group>
<Rectangle left="41" top="6" width="6" height="6" roundness="1"/>
<Fill color="#0F172A"/>
</Group>
<Group>
<Ellipse left="42" top="7" width="4" height="4"/>
<Fill color="#22D3EE"/>
</Group>
<Group>
<Ellipse left="36" top="1" width="16" height="16"/>
<Fill>
<RadialGradient center="44,9" radius="8" fitsToGeometry="false">
<ColorStop color="#22D3EE55" offset="0"/>
<ColorStop color="#22D3EE00" offset="1"/>
</RadialGradient>
</Fill>
</Group>
<Group>
<Path data="M-12 48C-14 64 -16 78 -18 94M12 48C14 64 16 78 18 94"/>
<Stroke color="#CBD5E1" width="14" cap="round"/>
</Group>
<Group>
<Ellipse left="-20" top="66" width="10" height="8"/>
<Ellipse left="10" top="66" width="10" height="8"/>
<Fill color="#94A3B8"/>
</Group>
<Group>
<Path data="M-24 92C-26 99 -26 106 -22 109C-18 112 -8 112 -6 109C-4 104 -10 96 -14 92"/>
<Fill>
<LinearGradient startPoint="-26,92" endPoint="-6,109" fitsToGeometry="false">
<ColorStop color="#64748B" offset="0"/>
<ColorStop color="#94A3B8" offset="0.5"/>
<ColorStop color="#64748B" offset="1"/>
</LinearGradient>
</Fill>
</Group>
<Group>
<Path data="M14 92C12 99 12 106 16 109C20 112 28 112 30 109C32 104 24 96 18 92"/>
<Fill>
<LinearGradient startPoint="12,92" endPoint="30,109" fitsToGeometry="false">
<ColorStop color="#64748B" offset="0"/>
<ColorStop color="#94A3B8" offset="0.5"/>
<ColorStop color="#64748B" offset="1"/>
</LinearGradient>
</Fill>
</Group>
<Group>
<Path data="M-24 108C-18 112 -8 112 -6 108M14 108C20 112 28 112 30 108"/>
<Stroke color="#475569" width="3"/>
</Group>
<Group>
<Path data="M-21 110L-19 110M-16 110L-14 110M-11 110L-9 110M17 110L19 110M21 110L23 110M25 110L27 110"/>
<Stroke color="#475569" width="1.5" cap="round"/>
</Group>
<Group>
<Ellipse left="-27" top="-71" width="54" height="54"/>
<Fill>
<LinearGradient startPoint="-27,-71" endPoint="27,-17" fitsToGeometry="false">
<ColorStop color="#E2E8F0" offset="0"/>
<ColorStop color="#F8FAFC" offset="0.3"/>
<ColorStop color="#E2E8F0" offset="0.7"/>
<ColorStop color="#CBD5E1" offset="1"/>
</LinearGradient>
</Fill>
</Group>
<Group>
<Ellipse left="-21" top="-63" width="42" height="38"/>
<Fill>
<LinearGradient startPoint="-21,-63" endPoint="21,-25" fitsToGeometry="false">
<ColorStop color="#1E293B" offset="0"/>
<ColorStop color="#334155" offset="0.25"/>
<ColorStop color="#1E3A5F" offset="0.5"/>
<ColorStop color="#1E293B" offset="0.75"/>
<ColorStop color="#0F172A" offset="1"/>
</LinearGradient>
</Fill>
</Group>
<Group>
<Path data="M-15 -58C-8 -61 5 -60 12 -57"/>
<Stroke color="#FFFFFF50" width="2" cap="round"/>
</Group>
<Group>
<Path data="M-11 -55C-4 -57 6 -56 11 -54"/>
<Stroke color="#FFFFFF30" width="1.5" cap="round"/>
</Group>
<Group>
<Path data="M11 -48C15 -42 16 -37 14 -32"/>
<Stroke color="#F9731630" width="3" cap="round"/>
</Group>
<Group>
<Ellipse left="-9" top="-41" width="2" height="2"/>
<Fill color="#FFFFFF30"/>
</Group>
<Group>
<Ellipse left="4.5" top="-48.5" width="1" height="1"/>
<Fill color="#FFFFFF25"/>
</Group>
<Group>
<Ellipse left="-27" top="-71" width="54" height="54"/>
<Stroke color="#94A3B8" width="2"/>
</Group>
<Group>
<Rectangle left="-4" top="-72.5" width="8" height="5" roundness="2"/>
<Fill color="#475569"/>
</Group>
<Group>
<Ellipse left="-2" top="-71.5" width="4" height="3"/>
<Fill color="#FBBF24"/>
</Group>
<Group>
<Ellipse left="-19" top="-27" width="38" height="12"/>
<Fill color="#94A3B8"/>
<Stroke color="#64748B" width="1.5"/>
</Group>
<Group>
<Path data="M-19 -21C-24 -16 -26 0 -24 10M19 -21C24 -16 26 0 24 10"/>
<Stroke color="#94A3B8" width="3" cap="round"/>
</Group>
<Group>
<Path data="M38 -16L38 -44"/>
<Stroke color="#94A3B8" width="2" cap="round"/>
</Group>
<Group>
<Path data="M38 -44L54 -42L54 -32L38 -34Z"/>
<Fill>
<LinearGradient startPoint="38,-44" endPoint="54,-32" fitsToGeometry="false">
<ColorStop color="#1E293B" offset="0"/>
<ColorStop color="#0F172A" offset="0.5"/>
<ColorStop color="#1E293B" offset="1"/>
</LinearGradient>
</Fill>
</Group>
<Group>
<Path data="M45 -41L46 -37L47 -41Z"/>
<Fill color="#E2E8F0"/>
</Group>
<Group>
<Ellipse left="43" top="-37.5" width="6" height="3"/>
<Stroke color="#E2E8F0" width="0.8"/>
</Group>
</Layer>
<Layer id="hologram" left="505" top="385" alpha="0.75">
<Rectangle left="-30" top="-22.5" width="60" height="45" roundness="3"/>
<Stroke color="#22D3EE55" width="1.5"/>
<Group>
<Path data="M-30 -10L30 -10M-30 5L30 5"/>
<Stroke color="#22D3EE25"/>
</Group>
<Group>
<Path data="M-10 -22L-10 22M10 -22L10 22"/>
<Stroke color="#22D3EE20"/>
</Group>
<Group>
<Rectangle left="-22" top="-18" width="8" height="4" roundness="1"/>
<Fill color="#22D3EE40"/>
</Group>
<Group>
<Rectangle left="-12" top="-18" width="12" height="4" roundness="1"/>
<Fill color="#34D39940"/>
</Group>
<Group>
<Rectangle left="5" top="-18" width="6" height="4" roundness="1"/>
<Fill color="#FBBF2440"/>
</Group>
<Group>
<Ellipse left="-7" top="-7" width="14" height="14"/>
<Stroke color="#22D3EE50"/>
</Group>
<Group>
<Ellipse left="-7" top="-3" width="14" height="6"/>
<Stroke color="#22D3EE30"/>
</Group>
<Group>
<Rectangle left="-16" top="13" width="16" height="2" roundness="1"/>
<Fill color="#22D3EE30"/>
</Group>
<Group>
<Rectangle left="3" top="13" width="10" height="2" roundness="1"/>
<Fill color="#22D3EE25"/>
</Group>
<Group>
<Rectangle left="-14" top="17" width="20" height="2" roundness="1"/>
<Fill color="#22D3EE20"/>
</Group>
<Group>
<Ellipse left="-37.5" top="-29" width="75" height="58"/>
<Fill>
<RadialGradient radius="38" fitsToGeometry="false">
<ColorStop color="#22D3EE00" offset="0.5"/>
<ColorStop color="#22D3EE12" offset="1"/>
</RadialGradient>
</Fill>
</Group>
</Layer>
<Layer id="scanner" left="644" top="442" alpha="0.45">
<Path data="M0 0L40 100L-15 108Z"/>
<Fill>
<LinearGradient startPoint="0,0" endPoint="12,108" fitsToGeometry="false">
<ColorStop color="#22D3EE70" offset="0"/>
<ColorStop color="#22D3EE40" offset="0.3"/>
<ColorStop color="#22D3EE18" offset="0.6"/>
<ColorStop color="#22D3EE00" offset="1"/>
</LinearGradient>
</Fill>
<Group>
<Path data="M6 28L22 28"/>
<Stroke color="#22D3EE25"/>
</Group>
<Group>
<Path data="M3 55L28 55"/>
<Stroke color="#22D3EE18"/>
</Group>
<Group>
<Path data="M0 82L32 82"/>
<Stroke color="#22D3EE12"/>
</Group>
</Layer>
<Layer id="beacon" left="780" top="510">
<Rectangle left="-2" top="-5" width="4" height="30" roundness="1"/>
<Fill color="#64748B"/>
<Group>
<Ellipse left="-7" top="22.5" width="14" height="5"/>
<Fill color="#475569"/>
</Group>
<Group>
<Ellipse left="-4" top="-9" width="8" height="8"/>
<Fill color="#22C55E"/>
</Group>
<Group>
<Ellipse left="-11" top="-16" width="22" height="22"/>
<Fill>
<RadialGradient center="0,-5" radius="11" fitsToGeometry="false">
<ColorStop color="#22C55E55" offset="0"/>
<ColorStop color="#22C55E28" offset="0.5"/>
<ColorStop color="#22C55E00" offset="1"/>
</RadialGradient>
</Fill>
</Group>
<Group>
<Ellipse left="-15" top="-20" width="30" height="30"/>
<Stroke color="#22C55E18"/>
</Group>
<Group>
<Ellipse left="-21" top="-26" width="42" height="42"/>
<Stroke color="#22C55E10"/>
</Group>
</Layer>
<Layer left="500" top="470" alpha="0.5">
<Ellipse left="-1.5" top="-1.5" width="3" height="3"/>
<Fill color="#A78BFA"/>
</Layer>
<Layer left="520" top="490" alpha="0.4">
<Ellipse left="-1" top="-1" width="2" height="2"/>
<Fill color="#C4B5FD"/>
</Layer>
<Layer left="700" top="500" alpha="0.5">
<Ellipse left="-1.5" top="-1.5" width="3" height="3"/>
<Fill color="#22D3EE"/>
</Layer>
<Layer left="740" top="480" alpha="0.35">
<Ellipse left="-1" top="-1" width="2" height="2"/>
<Fill color="#67E8F9"/>
</Layer>
<Layer left="560" top="520" alpha="0.45">
<Ellipse left="-1" top="-1" width="2" height="2"/>
<Fill color="#34D399"/>
</Layer>
<Layer left="660" top="530" alpha="0.4" composition="@comp3"/>
<Layer left="450" top="510" alpha="0.3">
<Ellipse left="-1" top="-1" width="2" height="2"/>
<Fill color="#C4B5FD"/>
</Layer>
<Layer left="770" top="520" alpha="0.5">
<Ellipse left="-1" top="-1" width="2" height="2"/>
<Fill color="#22D3EE"/>
</Layer>
<Layer left="580" top="430" alpha="0.35">
<Ellipse left="-1" top="-1" width="2" height="2"/>
<Fill color="#FBBF24"/>
</Layer>
<Layer left="630" top="410" alpha="0.3">
<Ellipse left="-1" top="-1" width="2" height="2"/>
<Fill color="#F97316"/>
</Layer>
<Layer left="540" top="455" alpha="0.4" composition="@comp3"/>
<Layer left="670" top="445" alpha="0.3">
<Ellipse left="-1" top="-1" width="2" height="2"/>
<Fill color="#34D399"/>
</Layer>
<Layer left="480" top="435" alpha="0.35">
<Ellipse left="-1" top="-1" width="2" height="2"/>
<Fill color="#C4B5FD"/>
</Layer>
<Layer left="465" top="548" alpha="0.3">
<Ellipse left="-6" top="-8" width="12" height="16"/>
<Fill color="#1A1040"/>
</Layer>
<Layer left="510" top="550" alpha="0.25">
<Ellipse left="-6" top="-8" width="12" height="16"/>
<Fill color="#1A1040"/>
</Layer>
<Layer left="548" top="546" alpha="0.2">
<Ellipse left="-6" top="-8" width="12" height="16"/>
<Fill color="#1A1040"/>
</Layer>
<Layer>
<Path data="M0 800L0 710C50 700 120 690 200 695C300 702 400 685 500 690C600 695 700 682 800 688C900 694 1000 680 1100 685L1200 682L1200 800Z"/>
<Fill>
<LinearGradient startPoint="600,-58" endPoint="600,58" fitsToGeometry="false">
<ColorStop color="#120B28" offset="0"/>
<ColorStop color="#0D0820" offset="0.5"/>
<ColorStop color="#080515" offset="1"/>
</LinearGradient>
</Fill>
<Group>
<Path data="M25 755L10 715L38 690L72 705L80 745L55 760Z"/>
<Fill color="#1A1040"/>
</Group>
<Group>
<Path data="M85 760L72 735L92 720L110 730L105 755Z"/>
<Fill color="#150D30"/>
</Group>
<Group>
<Path data="M1110 740L1095 708L1125 688L1155 700L1162 735L1135 748Z"/>
<Fill color="#18103A"/>
</Group>
<Group>
<Path data="M1168 745L1160 715L1175 700L1188 720L1184 748Z"/>
<Fill>
<LinearGradient startPoint="1160,722" endPoint="1188,722" fitsToGeometry="false">
<ColorStop color="#5B21B6" offset="0"/>
<ColorStop color="#7C3AED" offset="0.5"/>
<ColorStop color="#5B21B6" offset="1"/>
</LinearGradient>
</Fill>
</Group>
</Layer>
<Layer left="40" top="710">
<Path data="M0 0C-1 -30 2 -65 -2 -95"/>
<Stroke color="#7C3AED" width="6" cap="round"/>
<Group>
<Ellipse left="-22" top="-113" width="40" height="30"/>
<Fill>
<RadialGradient center="-2,-98" radius="20" fitsToGeometry="false">
<ColorStop color="#DDD6FE" offset="0"/>
<ColorStop color="#C4B5FD" offset="0.3"/>
<ColorStop color="#A78BFA" offset="0.7"/>
<ColorStop color="#7C3AED" offset="1"/>
</RadialGradient>
</Fill>
</Group>
<Group>
<Ellipse left="-10" top="-104" width="16" height="12"/>
<Fill color="#EDE9FE90"/>
</Group>
<Group>
<Ellipse left="-30" top="-119" width="56" height="42"/>
<Fill>
<RadialGradient center="-2,-98" radius="28" fitsToGeometry="false">
<ColorStop color="#C4B5FD55" offset="0"/>
<ColorStop color="#C4B5FD00" offset="1"/>
</RadialGradient>
</Fill>
</Group>
<Group>
<Path data="M-2 -83C-14 -74 -22 -58 -18 -42"/>
<Stroke color="#A78BFA55" width="3" cap="round"/>
</Group>
<Group>
<Path data="M-2 -83C10 -74 18 -58 14 -42"/>
<Stroke color="#A78BFA40" width="3" cap="round"/>
</Group>
<Group>
<Ellipse left="-23" top="-43" width="10" height="10"/>
<Fill color="#C4B5FD60"/>
</Group>
<Group>
<Ellipse left="10" top="-42" width="8" height="8"/>
<Fill color="#A78BFA50"/>
</Group>
</Layer>
<Layer left="80" top="720">
<Path data="M0 0C1 -18 -1 -38 0 -52"/>
<Stroke color="#6D28D9" width="4" cap="round"/>
<Group>
<Ellipse left="-14" top="-66" width="28" height="22"/>
<Fill>
<RadialGradient center="0,-55" radius="14" fitsToGeometry="false">
<ColorStop color="#C4B5FD" offset="0"/>
<ColorStop color="#A78BFA" offset="0.5"/>
<ColorStop color="#7C3AED" offset="1"/>
</RadialGradient>
</Fill>
</Group>
<Group>
<Ellipse left="-5" top="-59" width="10" height="8"/>
<Fill color="#EDE9FE80"/>
</Group>
<Group>
<Ellipse left="-20" top="-71" width="40" height="32"/>
<Fill>
<RadialGradient center="0,-55" radius="20" fitsToGeometry="false">
<ColorStop color="#C4B5FD40" offset="0"/>
<ColorStop color="#C4B5FD00" offset="1"/>
</RadialGradient>
</Fill>
</Group>
</Layer>
<Layer left="180" top="725">
<Path data="M0 0C-3 -20 2 -45 -2 -70"/>
<Stroke color="#059669" width="5" cap="round"/>
<Group>
<Path data="M-2 -35C-16 -44 -24 -36 -18 -26"/>
<Fill color="#10B981"/>
</Group>
<Group>
<Path data="M-2 -50C14 -58 22 -50 16 -40"/>
<Fill color="#059669"/>
</Group>
<Group>
<Path data="M-2 -60C-12 -66 -16 -58 -10 -52"/>
<Fill color="#14B8A6"/>
</Group>
<Group>
<Ellipse left="-9" top="-81" width="14" height="14"/>
<Fill color="#34D399"/>
</Group>
<Group>
<Ellipse left="-20" top="-90" width="36" height="36"/>
<Fill>
<RadialGradient center="-2,-72" radius="18" fitsToGeometry="false">
<ColorStop color="#34D39960" offset="0"/>
<ColorStop color="#34D39900" offset="1"/>
</RadialGradient>
</Fill>
</Group>
</Layer>
<Layer left="1080" top="695" alpha="0.9">
<Path data="M0 0C-2 -25 1 -55 -1 -80"/>
<Stroke color="#6D28D9" width="7" cap="round"/>
<Group>
<Ellipse left="-23" top="-101" width="44" height="34"/>
<Fill>
<RadialGradient center="-1,-84" radius="22" fitsToGeometry="false">
<ColorStop color="#F0ABFC" offset="0"/>
<ColorStop color="#D946EF" offset="0.3"/>
<ColorStop color="#A855F7" offset="0.7"/>
<ColorStop color="#7C3AED" offset="1"/>
</RadialGradient>
</Fill>
</Group>
<Group>
<Ellipse left="-10" top="-91" width="18" height="14"/>
<Fill color="#FDF4FF90"/>
</Group>
<Group>
<Ellipse left="-32" top="-108" width="62" height="48"/>
<Fill>
<RadialGradient center="-1,-84" radius="31" fitsToGeometry="false">
<ColorStop color="#F0ABFC50" offset="0"/>
<ColorStop color="#F0ABFC00" offset="1"/>
</RadialGradient>
</Fill>
</Group>
<Group>
<Path data="M-1 -67C-12 -55 -18 -40 -14 -28"/>
<Stroke color="#D946EF40" width="2.5" cap="round"/>
</Group>
<Group>
<Path data="M-1 -67C10 -55 16 -40 12 -28"/>
<Stroke color="#D946EF30" width="2.5" cap="round"/>
</Group>
<Group>
<Ellipse left="-18" top="-28" width="8" height="8"/>
<Fill color="#F0ABFC50"/>
</Group>
<Group>
<Ellipse left="8.5" top="-27.5" width="7" height="7"/>
<Fill color="#D946EF40"/>
</Group>
</Layer>
<Layer left="1160" top="708" alpha="0.9">
<Path data="M0 0C-2 -18 1 -40 -1 -58"/>
<Stroke color="#0891B2" width="5" cap="round"/>
<Group>
<Path data="M16 0C18 -14 15 -32 17 -46"/>
<Stroke color="#06B6D4" width="4" cap="round"/>
</Group>
<Group>
<Path data="M-12 0C-14 -12 -11 -28 -13 -40"/>
<Stroke color="#14B8A6" width="4" cap="round"/>
</Group>
<Group>
<Ellipse left="-9" top="-70" width="16" height="16"/>
<Fill color="#22D3EE"/>
</Group>
<Group>
<Ellipse left="10" top="-57" width="14" height="14"/>
<Fill color="#2DD4BF"/>
</Group>
<Group>
<Ellipse left="-19" top="-50" width="12" height="12"/>
<Fill color="#5EEAD4"/>
</Group>
<Group>
<Ellipse left="-25.5" top="-79.5" width="55" height="55"/>
<Fill>
<RadialGradient center="2,-52" radius="28" fitsToGeometry="false">
<ColorStop color="#22D3EE50" offset="0"/>
<ColorStop color="#22D3EE00" offset="1"/>
</RadialGradient>
</Fill>
</Group>
</Layer>
<Layer left="320" top="715" alpha="0.85">
<Path data="M0 0C-2 -16 1 -35 -1 -50"/>
<Stroke color="#7C3AED" width="5" cap="round"/>
<Group>
<Path data="M-1 -50C-12 -60 -8 -70 0 -65"/>
<Fill color="#D946EF"/>
</Group>
<Group>
<Path data="M-1 -50C10 -60 8 -70 0 -65"/>
<Fill color="#A855F7"/>
</Group>
<Group>
<Path data="M-1 -50C-6 -65 0 -75 2 -68"/>
<Fill color="#C084FC"/>
</Group>
<Group>
<Ellipse left="-5" top="-56" width="8" height="8"/>
<Fill color="#FBBF24"/>
</Group>
<Group>
<Ellipse left="-13" top="-64" width="24" height="24"/>
<Fill>
<RadialGradient center="-1,-52" radius="12" fitsToGeometry="false">
<ColorStop color="#FBBF2440" offset="0"/>
<ColorStop color="#FBBF2400" offset="1"/>
</RadialGradient>
</Fill>
</Group>
</Layer>
<Layer left="500" top="710" alpha="0.8">
<Path data="M0 0C-6 -15 -18 -35 -28 -48C-34 -54 -30 -42 -24 -30"/>
<Stroke color="#8B5CF6" width="4" cap="round"/>
<Group>
<Path data="M0 0C6 -18 16 -40 26 -55C32 -62 28 -48 22 -34"/>
<Stroke color="#7C3AED" width="4" cap="round"/>
</Group>
<Group>
<Path data="M0 0C-1 -22 0 -48 -2 -68"/>
<Stroke color="#6D28D9" width="6" cap="round"/>
</Group>
<Group>
<Ellipse left="-33" top="-58" width="10" height="12"/>
<Fill color="#C4B5FD"/>
</Group>
<Group>
<Ellipse left="21" top="-64" width="10" height="12"/>
<Fill color="#A78BFA"/>
</Group>
<Group>
<Ellipse left="-9" top="-80" width="14" height="16"/>
<Fill color="#DDD6FE"/>
</Group>
<Group>
<Ellipse left="-27" top="-91" width="50" height="50"/>
<Fill>
<RadialGradient center="-2,-66" radius="25" fitsToGeometry="false">
<ColorStop color="#C4B5FD45" offset="0"/>
<ColorStop color="#C4B5FD00" offset="1"/>
</RadialGradient>
</Fill>
</Group>
</Layer>
<Layer left="640" top="718" alpha="0.8">
<Path data="M0 0C-2 -12 1 -28 -1 -40"/>
<Stroke color="#7C3AED" width="4" cap="round"/>
<Group>
<Ellipse left="-12" top="-53" width="22" height="18"/>
<Fill>
<RadialGradient center="-1,-44" radius="11" fitsToGeometry="false">
<ColorStop color="#F0ABFC" offset="0"/>
<ColorStop color="#D946EF" offset="0.5"/>
<ColorStop color="#A21CAF" offset="1"/>
</RadialGradient>
</Fill>
</Group>
<Group>
<Ellipse left="-6" top="-48" width="10" height="8"/>
<Fill color="#FDF4FF90"/>
</Group>
<Group>
<Ellipse left="-19" top="-59" width="36" height="30"/>
<Fill>
<RadialGradient center="-1,-44" radius="18" fitsToGeometry="false">
<ColorStop color="#F0ABFC50" offset="0"/>
<ColorStop color="#F0ABFC00" offset="1"/>
</RadialGradient>
</Fill>
</Group>
</Layer>
<Layer left="770" top="705" alpha="0.85">
<Path data="M0 0C2 -24 -1 -52 1 -72"/>
<Stroke color="#059669" width="5" cap="round"/>
<Group>
<Path data="M1 -30C-14 -38 -24 -30 -18 -20"/>
<Fill color="#10B981"/>
</Group>
<Group>
<Path data="M1 -46C16 -54 24 -46 18 -36"/>
<Fill color="#059669"/>
</Group>
<Group>
<Path data="M1 -58C-10 -66 -16 -58 -10 -50"/>
<Fill color="#14B8A6"/>
</Group>
<Group>
<Path data="M1 -66C8 -72 12 -66 8 -60"/>
<Fill color="#2DD4BF"/>
</Group>
<Group>
<Ellipse left="-5" top="-82" width="12" height="12"/>
<Fill color="#34D399"/>
</Group>
<Group>
<Ellipse left="-18" top="-93" width="38" height="38"/>
<Fill>
<RadialGradient center="1,-74" radius="19" fitsToGeometry="false">
<ColorStop color="#34D39960" offset="0"/>
<ColorStop color="#34D39900" offset="1"/>
</RadialGradient>
</Fill>
</Group>
</Layer>
<Layer left="930" top="715" alpha="0.75">
<Path data="M0 0C-8 -12 -22 -22 -32 -28C-36 -30 -32 -20 -26 -10"/>
<Stroke color="#0891B2" width="3.5" cap="round"/>
<Group>
<Path data="M0 0C8 -14 20 -28 30 -35C34 -37 30 -26 24 -16"/>
<Stroke color="#06B6D4" width="3.5" cap="round"/>
</Group>
<Group>
<Path data="M0 0C0 -16 -1 -38 0 -52"/>
<Stroke color="#0E7490" width="5" cap="round"/>
</Group>
<Group>
<Ellipse left="-6" top="-63" width="12" height="14"/>
<Fill color="#22D3EE"/>
</Group>
<Group>
<Ellipse left="-36" top="-35" width="8" height="10"/>
<Fill color="#67E8F9"/>
</Group>
<Group>
<Ellipse left="26" top="-43" width="8" height="10"/>
<Fill color="#5EEAD4"/>
</Group>
<Group>
<Ellipse left="-22" top="-68" width="44" height="44"/>
<Fill>
<RadialGradient center="0,-46" radius="22" fitsToGeometry="false">
<ColorStop color="#22D3EE45" offset="0"/>
<ColorStop color="#22D3EE00" offset="1"/>
</RadialGradient>
</Fill>
</Group>
</Layer>
<Layer left="400" top="732" alpha="0.6">
<Path data="M-10 5L-16 -5L-8 -14L4 -12L12 -3L8 5Z"/>
<Fill color="#1A1040"/>
<Group>
<Path data="M14 5L10 -4L18 -10L26 -6L24 5Z"/>
<Fill color="#150D30"/>
</Group>
<Group>
<Path data="M-20 5L-24 -2L-18 -8L-12 -4L-14 5Z"/>
<Fill color="#120B28"/>
</Group>
</Layer>
<Layer left="850" top="728" alpha="0.6">
<Path data="M-8 4L-12 -4L-4 -12L6 -8L10 0L6 4Z"/>
<Fill color="#18103A"/>
<Group>
<Path data="M12 4L8 -3L14 -8L20 -4L18 4Z"/>
<Fill color="#1A1040"/>
</Group>
</Layer>
<Layer left="220" top="738" alpha="0.7">
<Path data="M0 0C-1 -10 0 -22 -1 -30"/>
<Stroke color="#10B981" width="3" cap="round"/>
<Group>
<Path data="M8 0C9 -8 7 -18 8 -24"/>
<Stroke color="#059669" width="2.5" cap="round"/>
</Group>
<Group>
<Ellipse left="-4" top="-38" width="8" height="8"/>
<Fill color="#34D399"/>
</Group>
<Group>
<Ellipse left="4.5" top="-31.5" width="7" height="7"/>
<Fill color="#2DD4BF"/>
</Group>
<Group>
<Ellipse left="-10" top="-44" width="28" height="28"/>
<Fill>
<RadialGradient center="4,-30" radius="14" fitsToGeometry="false">
<ColorStop color="#34D39950" offset="0"/>
<ColorStop color="#34D39900" offset="1"/>
</RadialGradient>
</Fill>
</Group>
</Layer>
<Layer left="550" top="735" alpha="0.5">
<Ellipse left="-15" top="-6" width="30" height="12"/>
<Fill>
<RadialGradient radius="15" fitsToGeometry="false">
<ColorStop color="#22D3EE40" offset="0"/>
<ColorStop color="#22D3EE20" offset="0.6"/>
<ColorStop color="#22D3EE00" offset="1"/>
</RadialGradient>
</Fill>
<Group>
<Ellipse left="10" top="-2" width="24" height="10"/>
<Fill>
<RadialGradient center="22,3" radius="12" fitsToGeometry="false">
<ColorStop color="#A78BFA35" offset="0"/>
<ColorStop color="#A78BFA18" offset="0.6"/>
<ColorStop color="#A78BFA00" offset="1"/>
</RadialGradient>
</Fill>
</Group>
<Group>
<Ellipse left="-28" width="20" height="8"/>
<Fill>
<RadialGradient center="-18,4" radius="10" fitsToGeometry="false">
<ColorStop color="#34D39930" offset="0"/>
<ColorStop color="#34D39900" offset="1"/>
</RadialGradient>
</Fill>
</Group>
</Layer>
<Layer left="130" top="748" alpha="0.4">
<Ellipse left="-13" top="-5" width="26" height="10"/>
<Fill>
<RadialGradient radius="13" fitsToGeometry="false">
<ColorStop color="#A78BFA35" offset="0"/>
<ColorStop color="#A78BFA00" offset="1"/>
</RadialGradient>
</Fill>
</Layer>
<Layer left="700" top="740" alpha="0.4">
<Ellipse left="-11" top="-4" width="22" height="8"/>
<Fill>
<RadialGradient radius="11" fitsToGeometry="false">
<ColorStop color="#22D3EE30" offset="0"/>
<ColorStop color="#22D3EE00" offset="1"/>
</RadialGradient>
</Fill>
</Layer>
<Layer alpha="0.9">
<Path data="M-71 840C-69 788 -47 611 -48 580C-42 611 -52 788 -53 840ZM-38 840C-38 796 -33 644 -29 618C-29 644 -25 796 -24 840ZM-9 840C-10 787 -8 606 -3 574C-3 606 6 787 7 840ZM21 840C22 790 54 620 52 590C58 620 37 790 35 840ZM35 840C34 795 49 644 52 617C53 644 46 795 47 840ZM62 840C63 796 89 646 90 619C92 646 74 796 74 840ZM75 840C73 788 54 611 60 580C58 611 86 788 87 840ZM109 840C111 796 152 644 149 618C155 644 121 796 119 840ZM130 840C129 799 105 658 110 634C110 658 144 799 145 840ZM146 840C145 798 125 654 129 629C129 654 157 798 158 840ZM169 840C169 799 185 662 188 637C189 662 183 799 183 840ZM201 840C201 800 228 664 229 640C233 664 219 800 218 840ZM225 840C224 800 217 664 220 640C221 664 238 800 238 840ZM259 840C258 789 209 616 215 586C213 616 269 789 271 840ZM276 840C277 787 320 608 319 576C324 608 291 787 290 840ZM299 840C299 791 309 625 310 595C313 625 313 791 313 840ZM331 840C332 782 357 583 356 548C361 583 345 782 344 840ZM352 840C352 786 367 601 369 568C372 601 370 786 370 840ZM376 840C378 800 421 664 420 640C426 664 395 800 394 840ZM400 840C400 785 378 599 382 566C383 599 417 785 417 840ZM418 840C419 781 443 581 441 546C448 581 436 781 434 840ZM439 840C440 799 448 658 448 633C452 658 452 799 452 840ZM451 840C451 799 454 659 456 634C459 659 468 799 468 840ZM486 840C487 785 501 599 502 567C505 599 500 785 499 840ZM499 840C498 790 513 622 518 592C519 622 516 790 517 840ZM543 840C543 798 544 655 544 629C547 655 554 798 553 840ZM556 840C557 792 593 631 593 602C597 631 572 792 571 840ZM571 840C571 791 582 623 585 593C585 623 582 791 583 840ZM605 840C606 799 610 660 610 635C614 660 620 799 619 840ZM623 840C624 785 637 599 637 566C642 599 642 785 641 840ZM654 840C656 786 702 604 698 572C705 604 667 786 665 840ZM686 840C685 799 657 658 660 633C660 658 695 799 696 840ZM699 840C698 782 658 586 663 552C662 586 713 782 714 840ZM714 840C715 793 743 634 742 606C746 634 727 793 726 840ZM750 840C749 794 750 637 754 609C753 637 759 794 760 840ZM770 840C772 798 815 656 814 631C820 656 789 798 787 840ZM801 840C803 796 821 646 821 619C825 646 816 796 815 840ZM826 840C827 791 868 626 867 597C873 626 844 791 842 840ZM836 840C836 797 872 650 873 624C876 650 852 797 852 840ZM859 840C858 788 842 611 847 580C847 611 874 788 875 840ZM886 840C886 781 900 581 901 546C905 581 904 781 904 840ZM903 840C901 787 892 606 898 575C896 606 915 787 917 840ZM926 840C927 794 958 638 956 610C961 638 937 794 936 840ZM959 840C958 782 937 587 940 552C941 587 971 782 971 840ZM980 840C980 795 964 640 966 613C967 640 992 795 992 840ZM1003 840C1003 798 1034 654 1034 629C1037 654 1015 798 1014 840ZM1021 840C1021 784 1037 594 1041 561C1041 594 1034 784 1035 840ZM1063 840C1063 782 1050 583 1051 548C1053 583 1074 782 1074 840ZM1086 840C1087 784 1132 593 1130 559C1137 593 1102 784 1100 840ZM1099 840C1097 782 1054 585 1062 550C1059 585 1115 782 1117 840ZM1113 840C1115 798 1141 655 1141 630C1146 655 1131 798 1129 840ZM1144 840C1144 785 1170 597 1172 564C1175 597 1161 785 1160 840ZM1178 840C1178 780 1196 577 1198 541C1199 577 1189 780 1189 840ZM1203 840C1204 785 1213 599 1213 566C1218 599 1222 785 1221 840ZM1222 840C1221 796 1226 648 1231 622C1232 648 1239 796 1239 840Z"/>
<Fill color="#14332A"/>
</Layer>
<Layer alpha="0.8">
<Path data="M-69 835C-68 797 -58 669 -58 647C-54 669 -54 797 -55 835ZM-33 835C-32 793 -12 650 -12 625C-8 650 -18 793 -19 835ZM-22 835C-22 795 -2 661 1 637C2 661 -9 795 -9 835ZM13 835C14 783 52 607 51 575C54 607 23 783 22 835ZM35 835C36 793 75 649 75 623C79 649 48 793 47 835ZM64 835C63 794 51 652 56 628C55 652 74 794 75 835ZM64 835C63 797 48 669 52 646C52 669 75 797 76 835ZM106 835C108 793 122 650 120 625C125 650 116 793 115 835ZM127 835C126 799 132 675 134 654C135 675 136 799 137 835ZM130 835C130 797 146 668 149 645C150 668 142 797 142 835ZM154 835C156 800 188 681 187 660C190 681 164 800 163 835ZM192 835C191 801 181 685 185 664C186 685 206 801 207 835ZM211 835C211 801 201 685 202 665C205 685 226 801 225 835ZM230 835C229 799 205 679 209 657C208 679 240 799 241 835ZM238 835C238 796 249 663 251 640C253 663 253 796 253 835ZM261 835C260 785 253 614 258 584C256 614 268 785 270 835ZM296 835C296 786 315 620 317 590C320 620 311 786 311 835ZM317 835C315 793 276 650 282 625C281 650 330 793 332 835ZM339 835C340 801 357 685 357 665C362 685 356 801 354 835ZM362 835C363 792 378 647 377 622C382 647 377 792 376 835ZM372 835C373 785 382 616 381 586C385 616 385 785 384 835ZM408 835C408 799 410 675 413 653C414 675 421 799 421 835ZM410 835C408 799 400 676 405 654C404 676 422 799 424 835ZM452 835C451 793 458 648 462 623C462 648 464 793 465 835ZM455 835C456 784 498 609 499 579C503 609 471 784 471 835ZM489 835C488 797 476 669 479 646C478 669 497 797 498 835ZM518 835C518 787 518 625 521 597C521 625 528 787 528 835ZM519 835C520 784 557 611 556 580C560 611 531 784 530 835ZM537 835C537 799 530 677 533 656C535 677 552 799 552 835ZM576 835C576 792 572 647 576 621C577 647 592 792 592 835ZM583 835C585 794 640 657 637 632C643 657 596 794 594 835ZM622 835C623 799 620 675 621 653C622 675 631 799 630 835ZM627 835C627 787 615 625 617 596C617 625 636 787 636 835ZM652 835C652 789 638 631 640 604C641 631 663 789 662 835ZM674 835C673 790 687 637 688 610C689 637 682 790 682 835ZM691 835C693 798 709 671 708 649C714 671 708 798 706 835ZM725 835C725 794 744 653 746 628C749 653 740 794 740 835ZM752 835C754 785 763 617 762 587C767 617 766 785 765 835ZM770 835C769 795 773 660 777 636C777 660 781 795 782 835ZM792 835C792 797 774 669 776 647C777 669 804 797 804 835ZM820 835C819 785 817 615 822 585C821 615 834 785 835 835ZM824 835C823 795 828 661 833 637C833 661 838 795 840 835ZM845 835C845 790 886 638 885 611C888 638 854 790 853 835ZM884 835C885 787 906 625 905 597C910 625 899 787 897 835ZM902 835C904 791 929 642 927 616C932 642 915 791 914 835ZM921 835C921 797 918 668 921 645C921 668 931 797 931 835ZM932 835C934 790 981 639 981 612C986 639 949 790 948 835ZM967 835C965 786 916 618 922 589C918 618 974 786 976 835ZM990 835C992 790 996 636 995 609C999 636 1000 790 999 835ZM1015 835C1013 787 1001 622 1007 593C1006 622 1029 787 1031 835ZM1019 835C1019 797 1052 670 1053 647C1056 670 1031 797 1030 835ZM1054 835C1053 792 1035 645 1041 619C1039 645 1065 792 1067 835ZM1069 835C1071 783 1123 608 1119 577C1126 608 1080 783 1078 835ZM1097 835C1096 792 1087 648 1090 622C1091 648 1112 792 1113 835ZM1110 835C1110 795 1126 657 1128 633C1130 657 1124 795 1124 835ZM1124 835C1122 793 1099 649 1105 623C1103 649 1136 793 1138 835ZM1148 835C1148 792 1143 645 1143 619C1146 645 1158 792 1158 835ZM1191 835C1190 796 1176 662 1178 639C1179 662 1202 796 1202 835ZM1212 835C1211 801 1221 684 1224 664C1224 684 1220 801 1221 835ZM1224 835C1225 788 1259 629 1257 601C1263 629 1238 788 1236 835Z"/>
<Fill color="#1A4038"/>
</Layer>
<Layer alpha="0.7">
<Path data="M-68 830C-68 789 -59 650 -59 626C-56 650 -55 789 -56 830ZM-24 830C-26 791 -64 660 -59 637C-61 660 -18 791 -16 830ZM-10 830C-9 787 24 639 24 613C28 639 1 787 0 830ZM1 830C2 794 -2 672 -1 651C2 672 13 794 13 830ZM12 830C11 791 4 658 8 634C7 658 23 791 25 830ZM44 830C45 792 65 663 65 640C68 663 56 792 55 830ZM55 830C55 789 33 650 36 625C37 650 67 789 68 830ZM82 830C83 791 119 660 117 637C121 660 90 791 89 830ZM98 830C98 799 132 693 133 675C136 693 111 799 111 830ZM117 830C117 797 119 683 120 663C123 683 130 797 129 830ZM138 830C137 801 132 701 135 683C135 701 148 801 149 830ZM163 830C164 802 172 706 172 689C176 706 177 802 175 830ZM180 830C181 802 207 706 206 690C209 706 190 802 188 830ZM204 830C203 792 191 663 195 640C195 663 214 792 215 830ZM231 830C230 787 212 642 215 616C215 642 239 787 240 830ZM248 830C249 796 262 682 262 662C265 682 260 796 259 830ZM270 830C269 790 239 654 242 630C242 654 277 790 278 830ZM287 830C287 799 314 695 315 677C318 695 300 799 300 830ZM307 830C309 802 348 706 347 690C352 706 322 802 320 830ZM329 830C328 798 309 691 313 672C313 691 340 798 341 830ZM355 830C356 789 354 649 355 625C357 649 366 789 365 830ZM360 830C358 799 361 693 366 674C365 693 372 799 374 830ZM376 830C376 799 369 694 370 675C372 694 388 799 387 830ZM401 830C402 799 422 693 422 674C426 693 416 799 415 830ZM418 830C420 795 476 676 475 655C480 676 434 795 432 830ZM443 830C444 797 480 684 479 664C483 684 453 797 451 830ZM460 830C458 800 440 697 445 679C444 697 472 800 473 830ZM475 830C474 795 460 678 464 657C463 678 485 795 486 830ZM505 830C504 800 489 696 493 678C492 696 514 800 515 830ZM514 830C512 798 505 691 510 672C509 691 526 798 528 830ZM545 830C547 801 564 704 563 686C567 704 558 801 556 830ZM568 830C569 799 587 693 586 674C589 693 576 799 575 830ZM585 830C585 792 596 661 597 638C600 661 597 792 597 830ZM599 830C598 802 598 705 601 688C602 705 609 802 609 830ZM631 830C632 787 656 642 656 616C658 642 639 787 638 830ZM648 830C649 798 686 688 685 669C690 688 662 798 660 830ZM663 830C663 792 682 663 684 641C684 663 671 792 672 830ZM690 830C690 797 711 685 711 666C714 685 699 797 699 830ZM716 830C718 794 756 673 754 651C759 673 726 794 725 830ZM736 830C737 789 760 650 759 626C762 650 746 789 745 830ZM742 830C743 789 761 648 762 623C765 648 756 789 755 830ZM761 830C760 787 767 639 771 613C770 639 770 787 771 830ZM776 830C776 788 779 644 780 618C781 644 783 788 783 830ZM798 830C798 792 781 662 785 639C785 662 811 792 812 830ZM813 830C812 789 805 649 808 624C808 649 824 789 825 830ZM853 830C851 797 821 683 825 663C824 683 861 797 863 830ZM861 830C860 796 865 680 869 660C868 680 869 796 871 830ZM885 830C885 790 884 652 885 628C886 652 893 790 893 830ZM905 830C906 795 926 677 926 656C929 677 917 795 917 830ZM922 830C920 791 889 657 894 634C894 657 934 791 935 830ZM939 830C939 797 933 686 934 667C935 686 947 797 947 830ZM956 830C956 798 968 688 968 668C971 688 966 798 965 830ZM975 830C975 787 973 639 974 613C976 639 984 787 984 830ZM1007 830C1005 799 988 692 993 673C993 692 1019 799 1021 830ZM1014 830C1015 793 1039 669 1038 647C1042 669 1027 793 1025 830ZM1046 830C1044 791 1039 658 1044 634C1042 658 1056 791 1058 830ZM1067 830C1069 790 1087 653 1085 629C1090 653 1079 790 1077 830ZM1097 830C1098 795 1124 676 1125 655C1127 676 1106 795 1105 830ZM1112 830C1114 794 1162 670 1160 648C1165 670 1126 794 1124 830ZM1132 830C1132 801 1137 702 1139 685C1140 702 1144 801 1144 830ZM1152 830C1153 790 1203 655 1201 632C1207 655 1167 790 1165 830ZM1159 830C1158 793 1120 665 1123 643C1122 665 1165 793 1167 830ZM1194 830C1194 799 1209 693 1211 674C1213 693 1207 799 1207 830ZM1195 830C1196 792 1220 662 1219 639C1223 662 1207 792 1206 830ZM1233 830C1232 795 1190 677 1194 656C1192 677 1240 795 1242 830Z"/>
<Fill color="#1F4D42"/>
</Layer>
<Layer alpha="0.6">
<Path data="M-67 825C-67 797 -62 703 -60 687C-59 703 -57 797 -57 825ZM-36 825C-36 791 -61 674 -59 653C-58 674 -26 791 -26 825ZM-8 825C-6 794 31 690 30 672C34 690 3 794 1 825ZM17 825C16 789 23 667 24 646C25 667 24 789 24 825ZM47 825C47 790 60 671 61 650C62 671 54 790 54 825ZM85 825C85 791 71 677 74 657C75 677 96 791 96 825ZM95 825C95 797 114 703 115 686C116 703 102 797 102 825ZM135 825C136 791 144 673 143 653C146 673 142 791 141 825ZM158 825C159 799 167 713 166 697C170 713 169 799 167 825ZM175 825C176 797 206 701 205 684C208 701 183 797 182 825ZM203 825C203 801 209 721 209 707C210 721 209 801 209 825ZM241 825C241 803 266 727 267 714C269 727 252 803 252 825ZM261 825C260 803 233 728 236 714C236 728 270 803 271 825ZM278 825C277 801 284 718 287 703C288 718 289 801 290 825ZM304 825C302 795 268 694 273 676C272 694 313 795 315 825ZM330 825C329 792 339 679 342 659C341 679 335 792 337 825ZM362 825C360 794 340 687 345 669C343 687 371 794 373 825ZM387 825C385 791 366 674 371 653C370 674 396 791 398 825ZM413 825C414 803 429 728 429 715C433 728 426 803 425 825ZM442 825C442 790 451 669 451 648C454 669 452 790 452 825ZM468 825C468 792 460 682 461 662C462 682 475 792 475 825ZM500 825C500 799 502 712 502 696C505 712 508 799 508 825ZM510 825C509 800 498 713 500 698C500 713 518 800 519 825ZM553 825C552 790 573 670 574 649C575 670 561 790 561 825ZM565 825C565 790 580 672 583 651C584 672 577 790 578 825ZM588 825C588 797 615 702 616 685C618 702 596 797 596 825ZM629 825C630 796 636 697 636 679C639 697 640 796 639 825ZM638 825C638 791 639 674 641 653C642 674 649 791 649 825ZM660 825C662 800 688 716 687 701C691 716 674 800 672 825ZM698 825C699 789 741 669 741 647C745 669 711 789 710 825ZM713 825C715 793 755 683 754 664C759 683 726 793 724 825ZM750 825C749 799 740 712 742 697C742 712 755 799 757 825ZM767 825C769 795 793 695 793 677C796 695 776 795 775 825ZM799 825C797 798 766 706 770 689C769 706 808 798 810 825ZM825 825C827 800 849 714 848 699C851 714 833 800 831 825ZM848 825C849 798 859 706 858 690C862 706 861 798 859 825ZM873 825C872 792 849 678 853 658C852 678 883 792 884 825ZM910 825C911 793 903 683 904 663C905 683 917 793 917 825ZM925 825C926 794 939 689 940 670C942 689 939 794 938 825ZM954 825C956 797 962 703 961 686C964 703 963 797 961 825ZM994 825C993 792 1008 680 1011 660C1011 680 1005 792 1005 825ZM1009 825C1008 794 1024 690 1026 672C1028 690 1021 794 1021 825ZM1028 825C1028 800 1047 716 1048 701C1049 716 1034 800 1035 825ZM1070 825C1071 796 1082 696 1082 679C1085 696 1079 796 1078 825ZM1085 825C1086 802 1111 722 1112 708C1115 722 1099 802 1098 825ZM1105 825C1106 797 1149 701 1148 684C1152 701 1117 797 1115 825ZM1139 825C1139 801 1165 718 1167 703C1169 718 1151 801 1151 825ZM1163 825C1161 793 1139 685 1143 666C1142 685 1168 793 1170 825ZM1192 825C1192 800 1180 714 1181 698C1182 714 1199 800 1199 825ZM1228 825C1226 794 1223 691 1227 672C1227 691 1239 794 1240 825Z"/>
<Fill color="#255A4E"/>
</Layer>
<Layer alpha="0.45">
<Path data="M-67 832C-68 806 -61 717 -59 702C-58 717 -57 806 -56 832ZM-24 832C-23 804 4 710 4 693C6 710 -16 804 -17 832ZM12 832C11 802 6 698 9 680C8 698 19 802 20 832ZM42 832C41 798 13 683 17 663C17 683 54 798 55 832ZM66 832C67 803 95 706 94 689C97 706 77 803 75 832ZM87 832C88 805 95 715 95 699C99 715 101 805 100 832ZM112 832C113 806 124 716 125 700C127 716 122 806 122 832ZM141 832C142 804 171 710 171 693C173 710 149 804 148 832ZM168 832C166 801 151 695 155 677C154 695 174 801 176 832ZM201 832C199 797 160 678 165 657C163 678 209 797 211 832ZM231 832C233 804 253 708 252 691C257 708 244 804 242 832ZM260 832C259 806 255 716 258 701C259 716 270 806 271 832ZM283 832C283 806 292 717 293 701C294 717 291 806 290 832ZM314 832C313 795 327 668 329 645C329 668 320 795 321 832ZM360 832C361 803 354 704 353 686C356 704 369 803 368 832ZM384 832C385 802 402 700 402 682C406 700 397 802 395 832ZM407 832C408 797 447 677 446 656C449 677 416 797 415 832ZM433 832C433 796 427 675 428 654C431 675 445 796 445 832ZM462 832C463 806 460 717 461 701C464 717 476 806 475 832ZM494 832C492 795 469 668 474 646C472 668 503 795 505 832ZM514 832C513 795 503 669 506 646C507 669 527 795 528 832ZM545 832C545 801 548 694 551 675C552 694 556 801 557 832ZM574 832C575 801 608 696 607 678C611 696 585 801 584 832ZM605 832C603 795 575 670 580 648C579 670 615 795 616 832ZM637 832C637 800 671 690 672 670C675 690 651 800 650 832ZM678 832C677 797 647 680 651 659C650 680 687 797 688 832ZM692 832C692 792 672 655 674 631C674 655 700 792 700 832ZM716 832C717 800 732 693 732 673C736 693 731 800 729 832ZM755 832C755 802 778 700 778 682C780 700 764 802 763 832ZM790 832C790 795 778 668 780 645C782 668 803 795 803 832ZM811 832C813 799 827 689 826 669C830 689 826 799 824 832ZM836 832C836 801 845 695 847 676C847 695 843 801 843 832ZM869 832C868 799 860 688 865 668C864 688 880 799 882 832ZM895 832C896 795 932 668 932 646C936 668 909 795 908 832ZM936 832C935 797 914 680 918 659C916 680 942 797 943 832ZM962 832C964 799 1004 686 1003 666C1008 686 975 799 973 832ZM998 832C1000 806 1030 716 1029 700C1033 716 1007 806 1005 832ZM1012 832C1011 803 1027 705 1029 688C1031 705 1024 803 1024 832ZM1043 832C1042 793 1027 661 1031 638C1031 661 1054 793 1055 832ZM1071 832C1070 806 1070 717 1074 701C1074 717 1083 806 1084 832ZM1097 832C1098 794 1124 667 1123 644C1127 667 1110 794 1109 832ZM1131 832C1131 802 1149 699 1150 680C1152 699 1141 802 1141 832ZM1150 832C1149 798 1132 683 1135 662C1134 683 1156 798 1157 832ZM1179 832C1177 800 1166 689 1170 670C1169 689 1189 800 1190 832ZM1223 832C1221 800 1227 692 1230 673C1229 692 1230 800 1231 832Z"/>
<Fill color="#2D6B5A"/>
</Layer>
<Layer alpha="0.2">
<Ellipse left="75" top="565" width="450" height="50"/>
<Fill>
<RadialGradient center="300,590" radius="225" fitsToGeometry="false">
<ColorStop color="#8B5CF6" offset="0"/>
<ColorStop color="#8B5CF600" offset="1"/>
</RadialGradient>
</Fill>
<Group>
<Ellipse left="410" top="558" width="380" height="40"/>
<Fill>
<RadialGradient center="600,578" radius="190" fitsToGeometry="false">
<ColorStop color="#A78BFA" offset="0"/>
<ColorStop color="#A78BFA00" offset="1"/>
</RadialGradient>
</Fill>
</Group>
<Group>
<Ellipse left="680" top="549" width="400" height="42"/>
<Fill>
<RadialGradient center="880,570" radius="200" fitsToGeometry="false">
<ColorStop color="#06B6D4" offset="0"/>
<ColorStop color="#06B6D400" offset="1"/>
</RadialGradient>
</Fill>
</Group>
<Group>
<Ellipse left="-10" top="582.5" width="280" height="35"/>
<Fill>
<RadialGradient center="130,600" radius="140" fitsToGeometry="false">
<ColorStop color="#7C3AED" offset="0"/>
<ColorStop color="#7C3AED00" offset="1"/>
</RadialGradient>
</Fill>
</Group>
</Layer>
<Layer left="700" top="110" alpha="0.7">
<Path data="M0 0L85 0"/>
<Stroke>
<LinearGradient startPoint="0,0" endPoint="85,0" fitsToGeometry="false">
<ColorStop color="#FFFFFF00" offset="0"/>
<ColorStop color="#FFFFFF40" offset="0.3"/>
<ColorStop color="#FFFFFFCC" offset="0.7"/>
<ColorStop color="#FFFFFF" offset="1"/>
</LinearGradient>
</Stroke>
<Group>
<Ellipse left="83" top="-2" width="4" height="4"/>
<Fill color="#FFFFFF"/>
</Group>
</Layer>
<Layer left="400" top="205" alpha="0.45">
<Path data="M0 0L50 0"/>
<Stroke>
<LinearGradient startPoint="0,0" endPoint="50,0" fitsToGeometry="false">
<ColorStop color="#FFFFFF00" offset="0"/>
<ColorStop color="#FFFFFF30" offset="0.4"/>
<ColorStop color="#FFFFFFAA" offset="1"/>
</LinearGradient>
</Stroke>
<Group>
<Ellipse left="48.5" top="-1.5" width="3" height="3"/>
<Fill color="#FFFFFFAA"/>
</Group>
</Layer>
<Layer left="1150" top="48" alpha="0.5">
<Path data="M0 0L45 0"/>
<Stroke>
<LinearGradient startPoint="0,0" endPoint="45,0" fitsToGeometry="false">
<ColorStop color="#67E8F900" offset="0"/>
<ColorStop color="#67E8F940" offset="0.5"/>
<ColorStop color="#67E8F9" offset="1"/>
</LinearGradient>
</Stroke>
<Group>
<Ellipse left="42" top="-3" width="6" height="6"/>
<Fill color="#67E8F9"/>
</Group>
<Group>
<Ellipse left="38" top="-7" width="14" height="14"/>
<Fill>
<RadialGradient center="45,0" radius="7" fitsToGeometry="false">
<ColorStop color="#67E8F945" offset="0"/>
<ColorStop color="#67E8F900" offset="1"/>
</RadialGradient>
</Fill>
</Group>
</Layer>
<Resources>
<Composition id="comp1" width="3" height="3">
<Layer>
<Ellipse left="-1.5" top="-1.5" width="3" height="3"/>
<Fill color="#FFFFFF"/>
</Layer>
</Composition>
<Composition id="comp2" width="3" height="3">
<Layer>
<Ellipse left="-1.5" top="-1.5" width="3" height="3"/>
<Fill color="#FFFFFFBB"/>
</Layer>
</Composition>
<Composition id="comp3" width="3" height="3">
<Layer>
<Ellipse left="-1.5" top="-1.5" width="3" height="3"/>
<Fill color="#A78BFA"/>
</Layer>
</Composition>
<Composition id="comp4" width="2" height="2">
<Layer>
<Ellipse left="-1" top="-1" width="2" height="2"/>
<Fill color="#FFFFFFCC"/>
</Layer>
</Composition>
<Composition id="comp5" width="2" height="2">
<Layer>
<Ellipse left="-1" top="-1" width="2" height="2"/>
<Fill color="#FFFFFFAA"/>
</Layer>
</Composition>
<Composition id="comp6" width="2" height="2">
<Layer>
<Ellipse left="-1" top="-1" width="2" height="2"/>
<Fill color="#FFFFFFBB"/>
</Layer>
</Composition>
<Composition id="comp7" width="2" height="2">
<Layer>
<Ellipse left="-1" top="-1" width="2" height="2"/>
<Fill color="#FFFFFF"/>
</Layer>
</Composition>
</Resources>
</pagx>