属性
X6 提供了非常多的内置属性以及自定义属性方式。
内置属性
ref
指向一个元素的选择器,指向的元素作为参考元素。
graph.addNode({
...,
markup: [
{
tagName: 'rect',
selector: 'body,'
},
{
tagName: 'rect',
selector: 'custom,'
}
],
attrs: {
body: {
width: 100,
height: 50,
},
// custom 元素的长度和宽度是 body 的一半
custom: {
ref: 'body',
refWidth: 0.5,
refHeight: 0.5,
}
}
})
refX
设置元素 x 坐标,目标 x 坐标相对于 ref 指代的元素的左上角 x 坐标。
- 当其值在
[0, 1]之间或为百分比(如50%)时,表示目标x坐标是参照x坐标相对于参照元素宽度百分比的相对偏移量。例如refX: 0.5表示,目标x坐标在参照x坐标基础上,向右偏移参照宽度的50%。 - 当其值
<0或>1时,表示目标x坐标是参照x坐标的绝对偏移量。例如refX: 20表示,目标x坐标在参照x坐标基础上,向右偏移20px。
refX2
与 refX 一样,当需要同时指定相对偏移量和绝对偏移量时使用。
{
refX: '50%',
refX2: 20,
}
上面代码表示,目标 x 坐标在参照 x 坐标的基础上,向右偏移参照元素宽度的 50% 并加上 20px。
refY
设置元素 y 坐标,目标 y 坐标相对于 ref 指代的参照元素的左上角 y 坐标(参照 y 坐标)
- 当其值在
[0, 1]之间或为百分比(如50%)时,表示目标y坐标是参照y坐标相对于参照元素高度百分比的相对偏移量。例如refY: 0.5表示,目标y坐标在参照y坐标基础上,向下偏移参照高度的50%。 - 当其值
<0或>1时,表示目标y坐标是参照y坐标的绝对偏移量。例如refY: 20表示,目标y坐标在参照y坐标基础上,向下偏移20px。
refY2
与 refY 一样,当需要同时指定相对偏移量和绝对偏移量时使用。
{
refY: '50%',
refY2: 20,
}
上面代码表示,目标 y 坐标在参照 y 坐标的基础上,向下偏移参照元素高度的 50% 并加上 20px。
refDx
设置元素 x 坐标,目标 x 坐标相对于 ref 指代的参照元素的右下角 x 坐标(参照 x 坐标)
- 当其值在
[0, 1]之间或为百分比(如50%)时,表示目标x坐标是参照x坐标相对于参照元素宽度百分比的相对偏移量。例如refDx: 0.5表示,目标x坐标在参照x坐标基础上,向右偏移参照宽度的50%。 - 当其值
<0或>1时,表示目标x坐标是参照x坐标的绝对偏移量。例如refDx: 20表示,目标x坐标在参照x坐标基础上,向右偏移20px。
refDy
设置元素 y 坐标,目标 y 坐标相对于 ref 指代的参照元素的右下角 y 坐标(参照 y 坐标)
- 当其值在
[0, 1]之间或为百分比(如50%)时,表示目标y坐标是参照y坐标相对于参照元素高度百分比的相对偏移量。例如refDy: 0.5表示,目标y坐标在参照y坐标基础上,向下偏移参照高度的50%。 - 当其值
<0或>1时,表示目标y坐标是参照y坐标的绝对偏移量。例如refDy: 20表示,目标y坐标在参照y坐标基础上,向下偏移20px。
refWidth
设置元素宽度,宽度计算相对于 ref 指代的参照元素的宽度(参照宽度)
- 当其值在
[0, 1]之间或为百分比(如75%)时,表示元素的宽度是参照宽度百分之多少。例如refWidth: 0.75表示元素的宽度是参照宽度的75%。 - 当其值
<0或>1时,表示元素的宽度在参照宽度的基础上减少或增加多少。例如refWidth: 20表示元素比相对元素宽20px。
注意
该属性只适用于那些支持宽度 width 和高度 height 的元素,如 <rect> 元素。
refWidth2
与 refWidth 一样,当需要同时指定绝对宽度和相对宽度时使用。
{
refWidth: '75%',
refWidth2: 20,
}
上面代码表示目标宽度是参照宽度 75% 并加上 20px。
refHeight
设置元素高度,高度计算相对于 ref 指代的参照元素的高度(参照高度)
- 当其值在
[0, 1]之间或为百分比(如75%)时,表示元素的高度是参照高度百分之多少。例如refHeight: 0.75表示元素的高度是参照高度的75%。 - 当其值
<0或>1时,表示元素的高度在参照高度的基础上减少或增加多少。例如refHeight: 20表示元素比相对元素高20px。
注意
该属性只适用于那些支持宽度 width 和高度 height 的元素,如 <rect> 元素。
refHeight2
与 refHeight 一样,当需要同时指定绝对宽度和相对宽度时使用。
{
refHeight: '75%',
refHeight2: 20,
}
上面代码表示目标高度是参照高度 75% 并加上 20px。
refCx
设置元素中心 x 坐标,即原生 cx 属性,目标值计算相对于 ref 指代的参照元素的宽度(参照宽度)
- 当其值在
[0, 1]之间或为百分比(如75%)时,表示元素的cx是参照宽度百分之多少。例如refCx: 0.75表示元素中心x坐标位于参照宽度的75%处。 - 当其值
<0或>1时,表示元素的cx是在参照宽度的基础上减少或增加多少。例如refCx: 20表示元素中心x坐标位于参照宽度加20px处。
注意
该属性只适用于那些支持 cx 和 cy 属性的元素,如 <ellipse> 元素。
refCy
设置元素中心 y 坐标,即原生 cy 属性,目标值计算相对于 ref 指代的参照元素的高度(参照高度)
- 当其值在
[0, 1]之间或为百分比(如75%)时,表示元素的cy是参照高度百分之多少。例如refCy: 0.75表示元素中心y坐标位于参照高度的75%处。 - 当其值
<0或>1时,表示元素的cy是在参照宽度的基础上减少或增加多少。例如refCy: 20表示元素中心y坐标位于参照高度加20px处。
注意
该属性只适用于那些支持 cx 和 cy 属性的元素,如 <ellipse> 元素。
refRx
设置元素的 rx 属性,目标值计算相对于 ref 指代的参照元素的宽度(参照宽度)
- 当其值在
[0, 1]之间或为百分比(如75%)时,表示元素的rx是参照宽度百分之多少。例如refRx: 0.75表示元素的rx是参照宽度的75%。 - 当其值
<0或>1时,表示元素的rx是在参照宽度的基础上减少或增加多少。例如refRx: 20表示元素的rx是参照宽度加20px。
注意
该属性只适用于那些支持 rx 和 ry 属性的元素,如 <rect> 元素。
refRy
设置元素的 ry 属性,目标值计算相对于 ref 指代的参照元素的高度(参照高度)
- 当其值在
[0, 1]之间或为百分比(如75%)时,表示元素的ry是参照高度百分之多少。例如refRy: 0.75表示元素的ry是参照高度的75%。 - 当其值
<0或>1时,表示元素的ry是在参照宽度的基础上减少或增加多少。例如refRy: 20表示元素的ry是参照高度加20px。
注意
该属性只适用于那些支持 rx 和 ry 属性的元素,如 <rect> 元素。
refRCircumscribed
设置元素的 r 属性,目标值相对于 ref 指代的参照元素的对角线长度(参照长度)
- 当其值在
[0, 1]之间或为百分比(如75%)时,表示r是参照长度百分之多少。例如refRCircumscribed: 0.75表示r是参照长度的75%。 - 当其值
<0或>1时,表示r是在参照长度的基础上减少或增加多少。例如refRCircumscribed: 20表示r是参照长度加20px。
注意
该属性只适用于那些支持 r 属性的元素,如 <rect> 元素。
refRInscribed
简称:refR
设置元素的 r 属性,目标值相对于 ref 指代的参照元素的宽高的最小值(参照长度)
- 当其值在
[0, 1]之间或为百分比(如75%)时,表示r是参照长度百分之多少。例如refRInscribed: 0.75表示r是参照长度的75%。 - 当其值
<0或>1时,表示r是在参照长度的基础上减少或增加多少。例如refRInscribed: 20表示r是参照长度加20px。
注意
该属性只适用于那些支持 r 属性的元素,如 <rect> 元素。
refDKeepOffset
设置 <path> 元素的 d 属性。通过缩放原始的 pathData 使目标 <path> 元素的大小与 ref 指代的参照元素的大小一样,通过平移原始的 pathData 使目标 <path> 元素的起点坐标与 ref 指代的参照元素的起点坐标对齐。
同时,提供的 pathData 的偏移量将被保留,这意味着如果提供的 pathData 的左上角不在坐标原点 0, 0,当 <path> 元素被渲染到画布后这个偏移量将被保留。
import { Graph, Node } from '@antv/x6'
const Path = Node.define({
markup: [{ tagName: 'path' }],
attrs: {
path: {
refDKeepOffset: 'M 10 10 30 10 30 30 z', // path offset is 10,10
fill: 'red',
stroke: 'black',
},
},
})
const container = document.getElementById('container')!
const graph = new Graph({
container,
width: 800,
height: 80,
grid: true,
})
const path = new Path().resize(40, 40).addTo(graph)
const view = graph.findView(path)
console.log(view.findOne('path').getAttribute('d'))
// 'M 10 10 50 10 50 50 z'
refDResetOffset
简称:refD
设置 <path> 元素的 d 属性。通过缩放原始的 pathData 使目标 <path> 元素的大小与 ref 指代的参照元素的大小一样,通过平移原始的 pathData 使目标 <path> 元素的起点坐标与 ref 指代的参照元素的起点坐标对齐。
同时,提供的 pathData 的偏移量将被移除,这意味着如果提供的 pathData 的左上角不在坐标原点 0, 0,将同时通过平移使其位于原点,当 <path> 元素被渲染到画布后将于参照元素严格对齐。
import { Graph, Node } from '@antv/x6'
const Path = Node.define({
markup: [{ tagName: 'path' }],
attrs: {
path: {
refDResetOffset: 'M 10 10 30 10 30 30 z', // path offset is 10,10
fill: 'red',
stroke: 'black',
},
},
})
const container = document.getElementById('container')!
const graph = new Graph({
container,
width: 800,
height: 80,
grid: true,
})
const path = new Path().resize(40, 40).addTo(graph)
const view = graph.findView(path)
console.log(view.findOne('path').getAttribute('d'))
// 'M 0 0 40 0 40 40 z'
resetOffset
当 resetOffset 属性值为 true 时,将平移点阵,使点阵的左上角位于原点。
path.attr({
path: {
d: 'M 10 10 20 20',
resetOffset: true, // 平移后的 d 属性值为 "M 0 0 10 10"
},
})
refPointsKeepOffset
设置 <polygon> 或 <polyline> 元素的 points 属性,通过缩放原始点阵使目标元素的大小与 ref 指代的参照元素的大小一样,通过平移原始点阵使目标元素的起点坐标与 ref 指代的参照元素的起点坐标对齐。
同时,点阵的偏移量将被保留,这意味着如果点阵的左上角不在坐标原点 0, 0,当元素被渲染到画布后这个偏移量将被保留。
import { Graph, Node } from '@antv/x6'
const Polygon = Node.define({
markup: [{ tagName: 'polygon' }],
attrs: {
polygon: {
refPointsKeepOffset: '10,10 30,10 30,30', // points offset is 10,10
fill: 'red',
stroke: 'black',
},
},
})
const container = document.getElementById('container')!
const graph = new Graph({
container,
width: 800,
height: 80,
grid: true,
})
const polygon = new Polygon().resize(40, 40).addTo(graph)
const view = graph.findView(polygon)
console.log(view.findOne('polygon').getAttribute('points'))
// '10,10 50,10 50,50'
refPointsResetOffset
简称:refPoints
设置 <polygon> 或 <polyline> 元素的 points 属性,通过缩放原始点阵使目标元素的大小与 ref 指代的参照元素的大小一样,通过平移原始点阵使目标元素的起点坐标与 ref 指代的参照元素的起点坐标对齐。
同时,点阵的偏移量将被移除,这意味着如果点阵的左上角不在坐标原点 0, 0,将同时通过平移使其位于原点,当 <path> 元素被渲染到画布后将于参照元素严格对齐。
import { Graph, Node } from '@antv/x6'
const Polygon = Node.define({
markup: [{ tagName: 'polygon' }],
attrs: {
polygon: {
refPointsResetOffset: '10,10 30,10 30,30', // points offset is 10,10
fill: 'red',
stroke: 'black',
},
},
})
const container = document.getElementById('container')!
const graph = new Graph({
container,
width: 800,
height: 80,
grid: true,
})
const polygon = new Polygon().resize(40, 40).addTo(graph)
const view = graph.findView(polygon)
console.log(view.findOne('polygon').getAttribute('points'))
// '100,0 40,0 40,40'
xAlign
元素与其 x 坐标在水平方向的对齐方式。
'left'目标元素的左侧与x对齐。'middle'目标元素的中心与x对齐。'right'目标元素的右侧与x对齐。
yAlign
元素与 y 坐标在垂直方向的对齐方式。
'top'目标元素的顶部与y对齐。'middle'目标元素的中心与y对齐。'bottom'目标元素与的底部与y对齐。
import './index.less'
import { createRoot } from 'react-dom/client'
import React from 'react'
import { Graph, Cell } from '@antv/x6'
import { Settings, State } from './settings.tsx'
export default class Example extends React.Component {
private container: HTMLDivElement
private node: Cell
componentDidMount() {
const graph = new Graph({
container: this.container,
width: 400,
height: 250,
background: {
color: '#F2F7FA',
},
})
this.node = graph.addNode({
x: 60,
y: 60,
width: 280,
height: 120,
markup: [
{
tagName: 'rect',
selector: 'body',
},
{
tagName: 'path',
selector: 'hLine',
},
{
tagName: 'path',
selector: 'vLine',
},
{
tagName: 'rect',
selector: 'ref',
},
{
tagName: 'circle',
selector: 'dot',
},
{
tagName: 'text',
selector: 'label',
},
],
attrs: {
body: {
refWidth: '100%',
refHeight: '100%',
fill: '#ffffff',
stroke: '#8f8f8f',
strokeWidth: 1,
},
dot: {
r: 3,
fill: '#8f8f8f',
stroke: 'none',
refX: 0.5,
refY: 0.5,
},
ref: {
width: 70,
height: 30,
refX: 0.5,
refY: 0.5,
// xAlign: 'middle',
// yAlign: 'middle',
fill: '#8f8f8f',
stroke: '#8f8f8f',
},
label: {
fontSize: 14,
fill: '#333333',
fontFamily: 'Arial, helvetica, sans-serif',
},
hLine: {
refY: 0.5,
d: 'M -30 0 310 0',
stroke: '#8f8f8f',
strokeDasharray: '5 5',
},
vLine: {
refX: 0.5,
d: 'M 0 -30 0 150',
stroke: '#8f8f8f',
strokeDasharray: '5 5',
},
},
})
}
onAttrsChanged = (attrs: State) => {
this.node.attr({
ref: attrs,
hLine: { refY: attrs.refY },
vLine: { refX: attrs.refX },
} as any)
}
refContainer = (container: HTMLDivElement) => {
this.container = container
}
render() {
return (
<div className="attrs-x-align-app">
<div className="app-left">
<Settings onChange={this.onAttrsChanged} />
</div>
<div ref={this.refContainer} className="app-content" />
</div>
)
}
}
const root = createRoot(document.getElementById('container')!)
root.render(<Example />)
window.addEventListener('pagehide', () => root.unmount(), { once: true })
.attrs-x-align-app {
display: flex;
border: 1px solid #f0f0f0;
padding: 16px 8px;
font-family: sans-serif;
.app-left {
bottom: 0;
width: 336px;
padding: 0 8px;
}
.app-content {
flex: 1;
margin-right: 8px;
margin-left: 8px;
box-shadow: 0 0 10px 1px #e9e9e9;
}
.ant-card {
box-shadow: 0 0 10px 1px #e9e9e9;
}
.ant-card-head-title {
text-align: center;
}
.ant-row {
margin: 16px 0;
text-align: left;
}
.slider-value {
display: inline-block;
margin-left: 8px;
padding: 3px 7px;
color: #333;
font-size: 12px;
line-height: 1.25;
background: #eee;
border-radius: 10px;
}
}import React from 'react'
import { Radio, Slider, Card, Row, Col } from 'antd'
export interface Props {
onChange: (state: State) => void
}
export interface State {
refX: number
refY: number
xAlign?: string
yAlign?: string
}
export class Settings extends React.Component<Props, State> {
state: State = {
refX: 0.5,
refY: 0.5,
xAlign: 'left',
yAlign: 'top',
}
notifyChange() {
this.props.onChange(this.state)
}
onRefXChanged = (refX: number) => {
this.setState({ refX }, () => {
this.notifyChange()
})
}
onRefYChanged = (refY: number) => {
this.setState({ refY }, () => {
this.notifyChange()
})
}
onXAlignChange = (e: any) => {
this.setState(
{
xAlign: e.target.value,
},
() => {
this.notifyChange()
},
)
}
onYAlignChange = (e: any) => {
this.setState(
{
yAlign: e.target.value,
},
() => {
this.notifyChange()
},
)
}
render() {
return (
<Card title="Attrs" size="small" bordered={false} style={{ width: 320 }}>
<Row align="middle">
<Col span={5}>refX</Col>
<Col span={14}>
<Slider
min={0}
max={0.99}
step={0.01}
value={this.state.refX}
onChange={this.onRefXChanged}
/>
</Col>
<Col span={1} offset={1}>
<div className="slider-value">
{(this.state.refX * 100).toFixed(0)}%
</div>
</Col>
</Row>
<Row align="middle">
<Col span={5}>refY</Col>
<Col span={14}>
<Slider
min={0}
max={0.99}
step={0.01}
value={this.state.refY}
onChange={this.onRefYChanged}
/>
</Col>
<Col span={1} offset={1}>
<div className="slider-value">
{(this.state.refY * 100).toFixed(0)}%
</div>
</Col>
</Row>
<Row align="middle">
<Col span={5}>xAlign</Col>
<Col span={19}>
<Radio.Group
onChange={this.onXAlignChange}
value={this.state.xAlign}
>
<Radio value={'left'}>Left</Radio>
<Radio value={'middle'}>Middle</Radio>
<Radio value={'right'}>Right</Radio>
</Radio.Group>
</Col>
</Row>
<Row align="middle">
<Col span={5}>yAlign</Col>
<Col span={19}>
<Radio.Group
onChange={this.onYAlignChange}
value={this.state.yAlign}
>
<Radio value={'top'}>Top</Radio>
<Radio value={'middle'}>Middle</Radio>
<Radio value={'bottom'}>Bottom</Radio>
</Radio.Group>
</Col>
</Row>
</Card>
)
}
}fill
当提供的 fill 属性值为对象时,表示使用渐变色填充,否则使用字符串颜色填充。
rect.attr('body/fill', {
type: 'linearGradient',
stops: [
{ offset: '0%', color: '#E67E22' },
{ offset: '20%', color: '#D35400' },
{ offset: '40%', color: '#E74C3C' },
{ offset: '60%', color: '#C0392B' },
{ offset: '80%', color: '#F39C12' },
],
})
filter
当提供的 filter 属性值为对象时,表示使用自定义滤镜填充,否则使用原生字符串形式(如 "url(#myfilter)")。
rect.attr('body/filter', {
name: 'dropShadow',
args: {
dx: 2,
dy: 2,
blur: 3,
},
})
stroke
当提供的 stroke 属性值为对象时,表示使用渐变色填充,否则使用字符串颜色填充。使用方式与 fill 属性一样。
style
使用 jQuery 的 css() 方法为指定的元素应用行内 CSS 样式。
html
使用 jQuery 的 html() 方法为指定的元素设置 innerHTML。
title
为指定的元素添加一个 <title> 子元素,<title> 元素并不影响渲染结果,而只是添加一个描述性说明。
rect.attr('body/title', 'Description of the rectangle')
text
仅适用于 <text> 元素,用于设置文本内容。如果提供的文本是单行文本(不包含换行符 '\n'),那么文本被直接设置为 <text> 元素的内容;否则为每一行文本将创建一个 <tspan> 元素,然后将该元素添加到<text> 元素中,
textWrap
仅适用于 <text> 元素,用于设置文本内容。与 text 属性不同的是,该属性将自动为文本添加换行,使提供的文本完全被包围在参照元素的包围盒(bounding box)中。
其属性值为一个简单对象,通过 text 来指定文本内容。可以提供可以选的 width 和 height 选项来调整元素的大小,当为负数时,表示减少相应的宽度或高度(相当于为文本设置了对应的 padding 边距);反之为正数时,增加相应的宽度或高度;当为百分比时,表示高度或宽度是参照元素宽度或高度的多少百分比。
当提供的文本超出显示范围时,文本将被自动截断,如果 ellipsis 选项被设置为 true ,则在截断文本的末尾添加一个省略号 ...。
默认情况下,英文单词会被截断显示,如果不想一个完整的单词被截断可以设置 breakWord: false,此时为了显示完整单词,文本可能超出宽度范围。
textWrap: {
text: 'lorem ipsum dolor sit amet consectetur adipiscing elit',
width: -10, // 宽度减少 10px
height: '50%', // 高度为参照元素高度的一半
ellipsis: true, // 文本超出显示范围时,自动添加省略号
breakWord: true, // 是否截断单词
}
import './index.less'
import { createRoot } from 'react-dom/client'
import * as React from 'react'
import { Graph } from '@antv/x6'
export default class Example extends React.Component {
private container: HTMLDivElement
componentDidMount() {
const graph = new Graph({
container: this.container,
background: {
color: '#F2F7FA',
},
})
graph.addNode({
x: 40,
y: 40,
width: 160,
height: 48,
attrs: {
body: {
fill: '#ffffff',
stroke: '#8f8f8f',
strokeWidth: 1,
rx: 5,
ry: 5,
},
label: {
textAnchor: 'left',
refX: 8,
text: 'hello test foo bar lint css jsvascript typescript',
textWrap: {
width: 144,
height: 40,
ellipsis: true,
},
},
},
})
graph.addNode({
x: 240,
y: 40,
width: 160,
height: 48,
attrs: {
body: {
fill: '#ffffff',
stroke: '#8f8f8f',
strokeWidth: 1,
rx: 5,
ry: 5,
},
label: {
textAnchor: 'left',
refX: 8,
text: 'hello test foo bar 中文字符 lint css jsvascript typescript',
textWrap: {
width: 144,
height: 40,
ellipsis: true,
},
},
},
})
graph.addNode({
x: 440,
y: 40,
width: 160,
height: 48,
attrs: {
body: {
fill: '#ffffff',
stroke: '#8f8f8f',
strokeWidth: 1,
rx: 5,
ry: 5,
},
label: {
textAnchor: 'left',
refX: 8,
text: 'hello-test-foo-bar-lint-css-jsvascript-typescript',
textWrap: {
width: 144,
height: 40,
ellipsis: true,
},
},
},
})
graph.addNode({
x: 40,
y: 128,
width: 160,
height: 48,
attrs: {
body: {
fill: '#ffffff',
stroke: '#8f8f8f',
strokeWidth: 1,
rx: 5,
ry: 5,
},
label: {
textAnchor: 'left',
refX: 8,
text: 'Thisissomeveryveryverylong word. Words will break according to usual rules.',
textWrap: {
width: 144,
height: 40,
ellipsis: true,
},
},
},
})
graph.addNode({
x: 240,
y: 128,
width: 160,
height: 48,
attrs: {
body: {
fill: '#ffffff',
stroke: '#8f8f8f',
strokeWidth: 1,
rx: 5,
ry: 5,
},
label: {
textAnchor: 'left',
refX: 8,
text: 'Thisissomeveryveryverylong word. Words will break according to usual rules.',
textWrap: {
width: 144,
height: 40,
ellipsis: true,
breakWord: false,
},
},
},
})
graph.addNode({
x: 440,
y: 128,
width: 160,
height: 48,
attrs: {
body: {
fill: '#ffffff',
stroke: '#8f8f8f',
strokeWidth: 1,
rx: 5,
ry: 5,
},
label: {
textAnchor: 'left',
refX: 8,
text: 'hello test foo bar lint css jsvascript typescript',
textWrap: {
width: 144,
height: 20,
ellipsis: true,
},
},
},
})
}
refContainer = (container: HTMLDivElement) => {
this.container = container
}
render() {
return (
<div className="attrs-text-wrap-app">
<div ref={this.refContainer} className="app-content" />
</div>
)
}
}
const root = createRoot(document.getElementById('container')!)
root.render(<Example />)
window.addEventListener('pagehide', () => root.unmount(), { once: true })
.attrs-text-wrap-app {
display: flex;
width: 100%;
border: 1px solid #f0f0f0;
padding: 16px 8px;
font-family: sans-serif;
.app-content {
flex: 1;
height: 216px;
margin-right: 8px;
margin-left: 8px;
box-shadow: 0 0 10px 1px #e9e9e9;
}
}textPath
仅适用于 <text> 元素,用于渲染沿路径的文本。
当提供的属性值为字符串时,表示文本沿字符串(pathData)代表的路径渲染。
当提供的属性值为对象时,可以通过 d 选项来指定文本的渲染路径,或者通过 selector 来指定一个节点/边中的 SVGPathElement 元素,支持 CSS 选择器和 Markup 中定义的选择器。同时可以通过 startOffset 选项来指定文本在路径中的位置,例如 50% 表示文本在沿路径 50% 处,20 表示文本在偏离路径起点 20px 处。
lineHeight
仅适用于 <text> 元素,用于指定文本的行高。
textVerticalAnchor
仅适用于 <text> 元素,元素与 y 坐标在垂直方向的对齐方式。。
'top'目标元素的顶部与y对齐。'middle'目标元素的中心与y对齐。'bottom'目标元素与的底部与y对齐。
import './index.less'
import { createRoot } from 'react-dom/client'
import * as React from 'react'
import { Graph, Cell } from '@antv/x6'
import { Settings, State } from './settings.tsx'
export default class Example extends React.Component {
private container: HTMLDivElement
private node: Cell
componentDidMount() {
const graph = new Graph({
container: this.container,
background: {
color: '#F2F7FA',
},
})
this.node = graph.addNode({
x: 120,
y: 80,
width: 200,
height: 80,
markup: [
{
tagName: 'rect',
selector: 'body',
},
{
tagName: 'path',
selector: 'hLine',
},
{
tagName: 'path',
selector: 'vLine',
},
{
tagName: 'rect',
selector: 'bg',
},
{
tagName: 'text',
selector: 'label',
},
{
tagName: 'circle',
selector: 'dot',
},
],
attrs: {
body: {
refWidth: '100%',
refHeight: '100%',
fill: '#ffffff',
stroke: '#8f8f8f',
strokeWidth: 1,
},
dot: {
r: 2,
fill: '#8f8f8f',
stroke: 'none',
refX: 0.5,
refY: 0.5,
},
bg: {
ref: 'label',
fill: '#8f8f8f',
stroke: '#8f8f8f',
rx: 2,
ry: 2,
refWidth: 1,
refHeight: 1,
refX: 0,
refY: 0,
},
label: {
fontSize: 14,
refX: 0.5,
refY: 0.5,
textAnchor: 'start',
textVerticalAnchor: 'top',
fill: '#fff',
stroke: '#fff',
fontFamily: 'Arial, helvetica, sans-serif',
text: 'Hello World',
},
hLine: {
refY: 0.5,
d: 'M -40 0 240 0',
stroke: '#8f8f8f',
strokeDasharray: '5 5',
},
vLine: {
refX: 0.5,
d: 'M 0 -40 0 120',
stroke: '#8f8f8f',
strokeDasharray: '5 5',
},
},
})
}
onGridChanged = (attrs: State) => {
this.node.attr({
label: attrs,
hLine: { refY: attrs.refY },
vLine: { refX: attrs.refX },
} as any)
}
refContainer = (container: HTMLDivElement) => {
this.container = container
}
render() {
return (
<div className="attrs-text-anchor-app">
<div className="app-left">
<Settings onChange={this.onGridChanged} />
</div>
<div ref={this.refContainer} className="app-content" />
</div>
)
}
}
const root = createRoot(document.getElementById('container')!)
root.render(<Example />)
window.addEventListener('pagehide', () => root.unmount(), { once: true })
.attrs-text-anchor-app {
display: flex;
border: 1px solid #f0f0f0;
padding: 16px 8px;
font-family: sans-serif;
.app-left {
bottom: 0;
width: 336px;
padding: 0 8px;
}
.app-content {
flex: 1;
margin-right: 8px;
margin-left: 8px;
box-shadow: 0 0 10px 1px #e9e9e9;
}
.ant-card {
box-shadow: 0 0 10px 1px #e9e9e9;
}
.ant-card-head-title {
text-align: center;
}
.ant-row {
margin: 16px 0;
text-align: left;
}
.slider-value {
display: inline-block;
margin-left: 8px;
padding: 3px 7px;
color: #333;
font-size: 12px;
line-height: 1.25;
background: #eee;
border-radius: 10px;
}
}import React from 'react'
import { Radio, Slider, Card, Row, Col } from 'antd'
export interface Props {
onChange: (state: State) => void
}
export interface State {
refX: number
refY: number
textAnchor?: string
textVerticalAnchor?: string
}
export class Settings extends React.Component<Props, State> {
state: State = {
refX: 0.5,
refY: 0.5,
textAnchor: 'start',
textVerticalAnchor: 'top',
}
notifyChange() {
this.props.onChange(this.state)
}
onRefXChanged = (refX: number) => {
this.setState({ refX }, () => {
this.notifyChange()
})
}
onRefYChanged = (refY: number) => {
this.setState({ refY }, () => {
this.notifyChange()
})
}
onTextAnchorAlignChange = (e: any) => {
this.setState(
{
textAnchor: e.target.value,
},
() => {
this.notifyChange()
},
)
}
onTextVerticalAnchorAlignChange = (e: any) => {
this.setState(
{
textVerticalAnchor: e.target.value,
},
() => {
this.notifyChange()
},
)
}
render() {
return (
<Card title="Attrs" size="small" bordered={false} style={{ width: 320 }}>
<Row align="middle">
<Col span={5}>refX</Col>
<Col span={14}>
<Slider
min={0}
max={0.99}
step={0.01}
value={this.state.refX}
onChange={this.onRefXChanged}
/>
</Col>
<Col span={1} offset={1}>
<div className="slider-value">
{(this.state.refX * 100).toFixed(0)}%
</div>
</Col>
</Row>
<Row align="middle">
<Col span={5}>refY</Col>
<Col span={14}>
<Slider
min={0}
max={0.99}
step={0.01}
value={this.state.refY}
onChange={this.onRefYChanged}
/>
</Col>
<Col span={1} offset={1}>
<div className="slider-value">
{(this.state.refY * 100).toFixed(0)}%
</div>
</Col>
</Row>
<Row align="middle">
<Col>textAnchor</Col>
</Row>
<Row align="middle">
<Col span={19} offset={5}>
<Radio.Group
onChange={this.onTextAnchorAlignChange}
value={this.state.textAnchor}
>
<Radio value={'start'}>Start</Radio>
<Radio value={'middle'}>Middle</Radio>
<Radio value={'end'}>end</Radio>
</Radio.Group>
</Col>
</Row>
<Row align="middle">
<Col>textVerticalAnchor</Col>
</Row>
<Row align="middle">
<Col span={19} offset={5}>
<Radio.Group
onChange={this.onTextVerticalAnchorAlignChange}
value={this.state.textVerticalAnchor}
>
<Radio value={'top'}>Top</Radio>
<Radio value={'middle'}>Middle</Radio>
<Radio value={'bottom'}>Bottom</Radio>
</Radio.Group>
</Col>
</Row>
</Card>
)
}
}connection
仅适用于边的 <path> 元素,当该属性为 true 时,表示将在该元素上渲染边,即设置该元素的 d 为边的 pathData。
edge.attr('pathSelector', {
connection: true,
stroke: 'red',
fill: 'none',
})
也支持包含 stubs 和 reserve 选项的对象。
-
当
reverse为false时:- 当
stubs为正数时,表示渲染的起始和终止部分长度。例如connection: { stubs: 20 }表示连线只渲染起始的20px和终止的20px,其余部分不渲染。 - 当
stubs为负数时,表示中间缺失(不渲染)部分的长度。例如connection: { stubs: -20 }表示连线中间有20px不渲染。
- 当
-
当
reverse为true时:- 当
stubs为正数时,起始和终止部分不渲染的长度。例如connection: { stubs: 20 }表示起始的20px和终止的20px不渲染。 - 当
stubs为负数时,表示中间部分的长度。例如connection: { stubs: -20 }表示只渲染中间20px。
- 当
edge.attr('pathSelector', {
connection: { stubs: -20, reverse: true },
})
atConnectionLengthKeepGradient
简称:atConnectionLength
将边中的指定元素移动到指定偏移量的位置处,并自动旋转元素,使其方向与所在位置边的斜率保持一致。
- 为正数时,表示距离边起点的偏移量。
- 为负数时,表示距离边终点的偏移量。
edge.attr('rectSelector', {
atConnectionLengthKeepGradient: 30,
// atConnectionLength: 30,
width: 10,
height: 10,
fill: 'red',
})
atConnectionLengthIgnoreGradient
将边中的指定元素移动到指定偏移量的位置处,忽略边的方向,即不会像 atConnectionLengthKeepGradient 属性那样自动旋转元素。
- 为正数时,表示距离边起点的偏移量。
- 为负数时,表示距离边终点的偏移量。
edge.attr('rectSelector', {
atConnectionLengthIgnoreGradient: 30,
width: 10,
height: 10,
fill: 'red',
})
atConnectionRatioKeepGradient
简称:atConnectionRatio
将边中的指定元素移动到指定比例 [0, 1] 位置处,并自动旋转元素,使其方向与所在位置边的斜率保持一致。
edge.attr('rectSelector', {
atConnectionRatioKeepGradient: 0.5,
// atConnectionRatio: 0.5,
width: 10,
height: 10,
fill: 'red',
})
atConnectionRatioIgnoreGradient
将边中的指定元素移动到指定比例 [0, 1] 位置处,忽略边的方向,即不会像 atConnectionRatioKeepGradient 属性那样自动旋转元素。
edge.attr('rectSelector', {
atConnectionRatioIgnoreGradient: 0.5,
width: 10,
height: 10,
fill: 'red',
})
sourceMarker
适用于所有 <path> 元素,在路径的起点添加一个 SVG 元素(如起始箭头),并自动旋转该元素,使其与根据路径方向保持一致。了解更多详情请参考这篇教程。
edge.attr('connection/sourceMarker', {
tagName: 'circle',
fill: '#666',
stroke: '#333',
r: 5,
cx: 5,
})
targetMarker
适用于所有 <path> 元素,在路径的终点添加一个 SVG 元素(如终点箭头),并自动旋转该元素,使其与根据路径方向保持一致。了解更多详情请参考这篇教程。
注意
该元素初始时就被旋转了 180 度,在此基础上再自动调整旋转角度,并与路径的方向保持一致。例如,对于一个水平的直线,我们为其起点指定了一个向左的箭头,我们也可以为其重点指定相同的箭头,这个箭头会自动指向右侧(自动旋转了 180 度)。
vertexMarker
适用于所有 <path> 元素,使用方法与 sourceMarker 一致,将在 <path> 元素所有顶点位置添加额外元素。
magnet
当 magnet 属性为 true 时,表示该元素可以被链接,即在连线过程中可以被当做连线的起点或终点,与连接桩类似。
port
为标记为 magnet 的元素指定连接桩 ID,当边链接到该元素时,该 ID 将保存到边的 source 或 target 中。
- 为字符串时,该字符串作为连接桩 ID。
- 为对象时,对象的
id属性值将作为连接桩 ID。
event
在指定的元素上自定义一个点击事件,然后可以在 Graph 上添加该事件的回调。
node.attr({
// 表示一个删除按钮,点击时删除该节点
image: {
event: 'node:delete',
xlinkHref: 'trash.png',
width: 20,
height: 20,
},
})
// 绑定事件回调,触发时删除节点
graph.on('node:delete', ({ view, e }) => {
e.stopPropagation()
view.cell.remove()
})
xlinkHref
属性 xlink:href 的别名,例如:
node.attr({
image: {
xlinkHref: 'xxx.png',
},
})
xlinkShow
属性 xlink:show 的别名。
xlinkType
属性 xlink:type 的别名。
xlinkTitle
属性 xlink:title 的别名。
xlinkArcrole
属性 xlink:arcrole 的别名。
xmlSpace
属性 xml:space 的别名。
xmlBase
属性 xml:base 的别名。
xmlLang
属性 xml:lang 的别名。
registry
我们在 registry 对象上提供了 register 和 unregister 两个方法来注册和删除特殊属性。
register
register(entities: { [name: string]: Definition }, force?: boolean): void
register(name: string, entity: Definition, force?: boolean): Definition
注册自定义属性。
unregister
unregister(name: string): Definition | null
删除注册的属性。
同时,我们将 register 和 unregister 两个方法挂载为 Graph 的两个静态方法 Graph.registerAttr 和 Graph.unregisterAttr,推荐使用这两个静态方法来注册和删除特殊属性。
Definition
在内部实现中,我们将特殊属性转换为浏览器可以识别的属性,所以理论上来讲特殊属性的值可以是任何类型,同时特殊属性的定义也支持多种形态。
字符串
为原生属性定义别名,例如特殊属性 xlinkHref 的定义,实际上是为 xlink:href 属性定义了一个更易书写的别名。
// 定义
Graph.registerAttr('xlinkHref', 'xlink:href')
// 使用
node.attr({
image: {
xlinkHref: 'xxx.png',
},
})
继续介绍属性定义前,我们先了解一下属性限定 qualify 函数,只有通过限定函数判定的属性值才会被特殊属性处理。例如,仅当 stroke 属性值为 Object 类型时,才会被当做特殊属性处理(使用渐变色填充边框)。看下面限定函数的定义。
type QualifyFunction = (
this: CellView, // 节点/边的视图
val: ComplexAttrValue, // 当前属性的属性值
options: {
elem: Element // 当前属性应用的元素
attrs: ComplexAttrs // 应用到该元素的属性键值对
cell: Cell // 节点/边
view: CellView // 节点/边的视图
},
) => boolean // 返回 true 时表示通过限定函数的判定
例如 stroke 属性的定义如下:
export const stroke: Attr.Definition = {
qualify(val) {
// 仅当属性值为对象时,才触发特殊属性加工逻辑。
return ObjectExt.isPlainObject(val)
},
set(stroke, { view }) {
return `url(#${view.graph.defineGradient(stroke as any)})`
},
}
set
设置属性定义,适用于大部分场景。
export interface SetDefinition {
qualify?: QualifyFunction // 限定函数
set: (
this: CellView, // 节点/边的视图
val: ComplexAttrValue, // 当前属性的属性值
options: {
refBBox: Rectangle // 参照元素的包围盒,没有参照元素时使用节点的位置和大小指代的矩形
elem: Element // 当前属性应用的元素
attrs: ComplexAttrs // 应用到该元素的属性键值对
cell: Cell // 节点/边
view: CellView // 节点/边的视图
},
) => SimpleAttrValue | SimpleAttrs | void
}
当 set 方法返回 string 或 number 时,表示将返回值作为特殊属性的值,这种方式通常用于扩展原生属性的定义,如支持渐变色填充的 stroke 和 fill 属性。
// stroke 属性定义
export const stroke: Attr.SetDefinition = {
qualify: ObjectExt.isPlainObject,
set(stroke, { view }) {
// 返回字符串,返回值作为 stroke 属性的属性值。
return `url(#${view.graph.defineGradient(stroke as any)})`
},
}
// 使用 stroke 属性
node.attr({
rect: {
stroke: {...},
},
})
当 set 方法返回一个简单对象时,则将该对象作为属性键值对应用到对应的元素上,如 sourceMarker 和 targetMarker 属性。
export const sourceMarker: Attr.SetDefinition = {
qualify: ObjectExt.isPlainObject,
set(marker: string | JSONObject, { view, attrs }) {
// 返回一个简单对象,返回值作为属性键值对被应用到对应的元素上。
return {
'marker-start': createMarker(marker, view, attrs),
},
},
}
当 set 方法没有返回值时,表示在在该方法内部完成了属性赋值,如 html 属性。
export const html: Attr.Definition = {
set(html, { view, elem }) {
// 没有返回值,在方法内部完成属性赋值
view.$(elem).html(`${html}`)
},
}
offset
偏移量属性定义。
export interface OffsetDefinition {
qualify?: QualifyFunction // 限定函数
offset: (
this: CellView, // 节点/边的视图
val: ComplexAttrValue, // 当前属性的属性值
options: {
refBBox: Rectangle // 参照元素的包围盒,没有参照元素时使用节点的位置和大小指代的矩形
elem: Element // 当前属性应用的元素
attrs: ComplexAttrs // 应用到该元素的属性键值对
cell: Cell // 节点/边
view: CellView // 节点/边的视图
},
) => Point.PointLike // 返回绝对偏移量
}
返回代表 x 和 y 方向绝对偏移量的 Point.PointLike 对象,如 xAlign 属性。
export const xAlign: Attr.OffsetDefinition = {
offset(alignment, { refBBox }) {
...
// 返回 x 轴的绝对偏移量
return { x, y: 0 }
},
}
position
定位属性定义。
export interface PositionDefinition {
qualify?: QualifyFunction // 限定函数
offset: (
this: CellView, // 节点/边的视图
val: ComplexAttrValue, // 当前属性的属性值
options: {
refBBox: Rectangle // 参照元素的包围盒,没有参照元素时使用节点的位置和大小指代的矩形
elem: Element // 当前属性应用的元素
attrs: ComplexAttrs // 应用到该元素的属性键值对
cell: Cell // 节点/边
view: CellView // 节点/边的视图
},
) => Point.PointLike // 返回绝对定位坐标
}
返回相对于节点的绝对定位坐标,如 refX 和 refDx 属性。
export const refX: Attr.PositionDefinition = {
position(val, { refBBox }) {
...
// 返回 x 轴的绝对定位
return { x, y: 0 }
},
}