diff --git a/packages/g6/__tests__/demo/static/index.ts b/packages/g6/__tests__/demo/static/index.ts
index a19f3112bc8..955561f1138 100644
--- a/packages/g6/__tests__/demo/static/index.ts
+++ b/packages/g6/__tests__/demo/static/index.ts
@@ -7,6 +7,7 @@ export * from './edge-polyline';
export * from './edge-quadratic';
export * from './layered-canvas';
export * from './node-circle';
+export * from './node-rect';
export * from './node-star';
export * from './shape-badge';
export * from './shape-icon';
diff --git a/packages/g6/__tests__/demo/static/node-rect.ts b/packages/g6/__tests__/demo/static/node-rect.ts
new file mode 100644
index 00000000000..0095ccb06ca
--- /dev/null
+++ b/packages/g6/__tests__/demo/static/node-rect.ts
@@ -0,0 +1,73 @@
+import { Rect } from '../../../src/elements/nodes';
+import type { StaticTestCase } from '../types';
+
+export const nodeRect: StaticTestCase = async (context) => {
+ const { canvas } = context;
+
+ const c1 = new Rect({
+ style: {
+ // key
+ x: 100,
+ y: 100,
+ fill: 'green',
+ size: [80, 80],
+ },
+ });
+
+ const c2 = new Rect({
+ style: {
+ // key
+ x: 300,
+ y: 100,
+ fill: 'red',
+ size: [80],
+ // label
+ labelText: 'rect node',
+ labelFontSize: 14,
+ labelFill: 'pink',
+ labelPosition: 'bottom',
+ // badge
+ badgeOptions: [
+ { text: 'A', position: 'right-top', backgroundFill: 'grey', fill: 'white', fontSize: 10, padding: [1, 4] },
+ { text: 'Important', position: 'right', backgroundFill: 'blue', fill: 'white', fontSize: 10 },
+ { text: 'Notice', position: 'left-bottom', backgroundFill: 'red', fill: 'white', fontSize: 10 },
+ ],
+ // anchor
+ anchorOptions: [
+ { position: [0, 0.5], r: 2, stroke: 'black', lineWidth: 1, zIndex: 2 },
+ { position: [1, 0.5], r: 2, stroke: 'yellow', lineWidth: 2, zIndex: 2 },
+ { position: [0.5, 0], r: 2, stroke: 'green', lineWidth: 1, zIndex: 2 },
+ { position: [0.5, 1], r: 2, stroke: 'grey', lineWidth: 1, zIndex: 2 },
+ ],
+ // icon
+ iconSrc: 'https://gw.alipayobjects.com/zos/basement_prod/012bcf4f-423b-4922-8c24-32a89f8c41ce.svg',
+ iconWidth: 32,
+ iconHeight: 32,
+ // halo
+ haloOpacity: 0.4,
+ haloStroke: 'grey',
+ haloLineWidth: 12,
+ haloPointerEvents: 'none',
+ },
+ });
+
+ const c3 = new Rect({
+ style: {
+ // key
+ x: 100,
+ y: 300,
+ fill: 'pink',
+ size: 80,
+ // icon
+ iconText: 'Y',
+ iconFontSize: 32,
+ iconFill: 'black',
+ },
+ });
+
+ await canvas.init();
+
+ canvas.appendChild(c1);
+ canvas.appendChild(c2);
+ canvas.appendChild(c3);
+};
diff --git a/packages/g6/__tests__/integration/snapshots/static/node-rect.svg b/packages/g6/__tests__/integration/snapshots/static/node-rect.svg
new file mode 100644
index 00000000000..0732e1de881
--- /dev/null
+++ b/packages/g6/__tests__/integration/snapshots/static/node-rect.svg
@@ -0,0 +1,304 @@
+
\ No newline at end of file
diff --git a/packages/g6/src/elements/nodes/index.ts b/packages/g6/src/elements/nodes/index.ts
index 830584c8496..df3bf02a416 100644
--- a/packages/g6/src/elements/nodes/index.ts
+++ b/packages/g6/src/elements/nodes/index.ts
@@ -1,7 +1,9 @@
export { BaseNode } from './base-node';
export { Circle } from './circle';
+export { Rect } from './rect';
export { Star } from './star';
export type { BaseNodeStyleProps } from './base-node';
export type { CircleStyleProps } from './circle';
+export type { RectStyleProps } from './rect';
export type { StarStyleProps } from './star';
diff --git a/packages/g6/src/elements/nodes/rect.ts b/packages/g6/src/elements/nodes/rect.ts
new file mode 100644
index 00000000000..feded58336b
--- /dev/null
+++ b/packages/g6/src/elements/nodes/rect.ts
@@ -0,0 +1,49 @@
+import { Path as GPath } from '@antv/g';
+import { getRectPath } from '../../utils/element';
+import { subStyleProps } from '../../utils/prefix';
+import { BaseNode } from './base-node';
+
+import type { DisplayObjectConfig, PathStyleProps as GPathStyleProps, Group } from '@antv/g';
+import type { BaseNodeStyleProps } from './base-node';
+
+type KeyShapeStyleProps = GPathStyleProps & {
+ size: number[] | number;
+};
+
+export type RectStyleProps = BaseNodeStyleProps;
+
+type ParsedRectStyleProps = Required;
+
+type RectOptions = DisplayObjectConfig;
+
+/**
+ * Draw Rect based on BaseNode, override drawKeyShape.
+ */
+export class Rect extends BaseNode {
+ constructor(options: RectOptions) {
+ super(options);
+ }
+
+ protected getKeyStyle(attributes: ParsedRectStyleProps): KeyShapeStyleProps {
+ const keyStyle = super.getKeyStyle(attributes);
+ const { size } = keyStyle;
+ const d = getRectPath(size);
+ return { ...keyStyle, d };
+ }
+
+ protected getHaloStyle(attributes: ParsedRectStyleProps): KeyShapeStyleProps {
+ const haloStyle = subStyleProps(this.getGraphicStyle(attributes), 'halo') as Partial;
+ const keyStyle = this.getKeyStyle(attributes);
+
+ return {
+ ...keyStyle,
+ ...haloStyle,
+ } as KeyShapeStyleProps;
+ }
+
+ protected drawKeyShape(attributes: ParsedRectStyleProps, container: Group): GPath {
+ return this.upsert('key', GPath, this.getKeyStyle(attributes), container) as GPath;
+ }
+
+ connectedCallback() {}
+}
diff --git a/packages/g6/src/utils/element.ts b/packages/g6/src/utils/element.ts
index fe362876e13..91166914dd8 100644
--- a/packages/g6/src/utils/element.ts
+++ b/packages/g6/src/utils/element.ts
@@ -1,6 +1,6 @@
import type { AABB, TextStyleProps } from '@antv/g';
import type { PathArray } from '@antv/util';
-import { get, isString } from '@antv/util';
+import { get, isArray, isString } from '@antv/util';
import type { Point } from '../types';
import type { AnchorPosition, LabelPosition, RelativePosition, StarAnchorPosition } from '../types/node';
@@ -120,3 +120,20 @@ export function getStarAnchors(outerR: number, innerR: number): Record) {
return get(anchors, position.toLocaleLowerCase(), anchors['default']);
}
+
+/**
+ * Get Rect PathArray.
+ * @param size - width or height
+ * @returns The PathArray for G
+ */
+export function getRectPath(size: number[] | number): PathArray {
+ const [width = 40, height = width] = (isArray(size) ? size : [size, size]) as [number, number];
+
+ return [
+ ['M', width / 2, -height / 2],
+ ['L', width / 2, height / 2],
+ ['L', -width / 2, height / 2],
+ ['L', -width / 2, -height / 2],
+ ['Z'],
+ ];
+}