Compare commits
24 Commits
@antv/x6@1
...
v1
Author | SHA1 | Date | |
---|---|---|---|
b7d0c0bd62 | |||
753bc29dcc | |||
9f200a272d | |||
556121d68d | |||
2a132a2c19 | |||
6d37943855 | |||
1ca2114005 | |||
032cce51df | |||
add5600a26 | |||
0fb2dcc96a | |||
d440dfc6b1 | |||
87fac22cd0 | |||
de35af5d84 | |||
d59e62605a | |||
b242a00bd0 | |||
0b27331a4d | |||
05c3821ee9 | |||
afeab7b300 | |||
897a1a1812 | |||
399ca71075 | |||
7c4e1b9272 | |||
b8330d164b | |||
d761f59789 | |||
a07be165f0 |
81
examples/x6-example-features/src/pages/animation/3.tsx
Normal file
81
examples/x6-example-features/src/pages/animation/3.tsx
Normal file
@ -0,0 +1,81 @@
|
||||
import React from 'react'
|
||||
import { Graph, EdgeView, NodeView } from '@antv/x6'
|
||||
import { animateAlongEdge, animateAlongNode, clearAnimation } from './animation'
|
||||
import '../index.less'
|
||||
|
||||
export default class Example extends React.Component {
|
||||
private container: HTMLDivElement
|
||||
private animate = false
|
||||
|
||||
componentDidMount() {
|
||||
const graph = new Graph({
|
||||
container: this.container,
|
||||
width: 800,
|
||||
height: 600,
|
||||
grid: true,
|
||||
})
|
||||
|
||||
const source = graph.addNode({
|
||||
id: 'source',
|
||||
shape: 'rect',
|
||||
x: 80,
|
||||
y: 250,
|
||||
width: 160,
|
||||
height: 60,
|
||||
})
|
||||
|
||||
const target = graph.addNode({
|
||||
id: 'target',
|
||||
shape: 'rect',
|
||||
x: 520,
|
||||
y: 250,
|
||||
width: 160,
|
||||
height: 60,
|
||||
})
|
||||
|
||||
graph.addEdge({
|
||||
id: 'edge',
|
||||
source,
|
||||
target,
|
||||
})
|
||||
|
||||
document.addEventListener('click', () => {
|
||||
if (this.animate) {
|
||||
this.animate = false
|
||||
clearAnimation()
|
||||
} else {
|
||||
this.animate = true
|
||||
this.play(graph)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
play(graph: Graph) {
|
||||
const sourceNodeView = graph.findViewByCell(
|
||||
graph.getCellById('source'),
|
||||
) as NodeView
|
||||
const targetNodeView = graph.findViewByCell(
|
||||
graph.getCellById('target'),
|
||||
) as NodeView
|
||||
const edgeView = graph.findViewByCell(graph.getCellById('edge')) as EdgeView
|
||||
|
||||
animateAlongNode(sourceNodeView, 'M 0 30 L 0 0 L 160 0 L 160 30')
|
||||
animateAlongNode(sourceNodeView, 'M 0 30 L 0 60 L 160 60 L 160 30', () => {
|
||||
animateAlongEdge(edgeView, () => {
|
||||
animateAlongNode(targetNodeView, 'M 0 0 L 160 0 L 160 60 L 0 60')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
refContainer = (container: HTMLDivElement) => {
|
||||
this.container = container
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<div className="x6-graph-wrap">
|
||||
<div ref={this.refContainer} className="x6-graph" />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
}
|
@ -0,0 +1,94 @@
|
||||
import { Vector, EdgeView, NodeView, Dom } from '@antv/x6'
|
||||
|
||||
const animateToken: SVGElement[] = []
|
||||
|
||||
export const removeAnimationElem = (elem: SVGElement) => {
|
||||
const index = animateToken.findIndex((token) => token === elem)
|
||||
if (index) {
|
||||
animateToken.splice(index, 1)
|
||||
}
|
||||
Dom.remove(elem)
|
||||
}
|
||||
|
||||
export const animateAlongEdge = (
|
||||
edgeView: EdgeView,
|
||||
compelete?: () => void,
|
||||
) => {
|
||||
const token = Vector.create('circle', { r: 4, fill: 'red' })
|
||||
const path = edgeView.container.querySelector('path')
|
||||
const animate = Dom.createSvgElement<SVGAnimateMotionElement>('animateMotion')
|
||||
const mpath = Dom.createSvgElement('mpath')
|
||||
|
||||
const attrs = {
|
||||
dur: '1000ms',
|
||||
repeatCount: '1',
|
||||
calcMode: 'linear',
|
||||
fill: 'freeze',
|
||||
}
|
||||
|
||||
const id = Dom.ensureId(path!)
|
||||
animate.appendChild(mpath)
|
||||
token.node.appendChild(animate)
|
||||
token.appendTo(edgeView.container)
|
||||
Dom.attr(mpath, { 'xlink:href': `#${id}` })
|
||||
Dom.attr(animate, attrs)
|
||||
|
||||
animateToken.push(token.node)
|
||||
animate.addEventListener('endEvent', () => {
|
||||
removeAnimationElem(token.node)
|
||||
if (compelete) {
|
||||
compelete()
|
||||
}
|
||||
})
|
||||
|
||||
const ani = animate as any
|
||||
|
||||
setTimeout(() => {
|
||||
ani.beginElement()
|
||||
})
|
||||
}
|
||||
|
||||
export const animateAlongNode = (
|
||||
nodeView: NodeView,
|
||||
path: string,
|
||||
compelete?: () => void,
|
||||
) => {
|
||||
const token = Vector.create('circle', { r: 4, fill: 'red' })
|
||||
const animate = Dom.createSvgElement<SVGAnimateMotionElement>('animateMotion')
|
||||
|
||||
const attrs = {
|
||||
dur: '2000ms',
|
||||
repeatCount: '1',
|
||||
calcMode: 'linear',
|
||||
fill: 'freeze',
|
||||
}
|
||||
|
||||
Dom.attr(animate, {
|
||||
...attrs,
|
||||
path,
|
||||
})
|
||||
|
||||
token.append(animate)
|
||||
nodeView.container.appendChild(token.node)
|
||||
animateToken.push(token.node)
|
||||
|
||||
animate.addEventListener('endEvent', () => {
|
||||
removeAnimationElem(token.node)
|
||||
if (compelete) {
|
||||
compelete()
|
||||
}
|
||||
})
|
||||
|
||||
const ani = animate as any
|
||||
|
||||
setTimeout(() => {
|
||||
ani.beginElement()
|
||||
})
|
||||
}
|
||||
|
||||
export const clearAnimation = () => {
|
||||
const animations = [...animateToken]
|
||||
animations.forEach((item) => {
|
||||
removeAnimationElem(item)
|
||||
})
|
||||
}
|
@ -27,7 +27,7 @@
|
||||
"package:inherit": "yarn package-inherit update",
|
||||
"prepare": "is-ci || husky install configs/husky-config",
|
||||
"precommit": "yarn lint-staged && lerna run --concurrency 1 --stream precommit",
|
||||
"publish:latest": "yarn build:dev && lerna publish --no-private --ignore-scripts"
|
||||
"publish:latest": "lerna publish from-package --no-private --ignore-scripts --dist-tag v1"
|
||||
},
|
||||
"lint-staged": {
|
||||
"**/*.{js,jsx,tsx,ts,less,md,json}": [
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@antv/x6-angular-shape",
|
||||
"version": "1.3.1",
|
||||
"version": "1.3.2",
|
||||
"description": "X6 shape for rendering angular components.",
|
||||
"main": "lib/index.js",
|
||||
"module": "es/index.js",
|
||||
@ -49,7 +49,7 @@
|
||||
"@angular/cdk": ">=10.2.3",
|
||||
"@angular/common": "^10.2.3",
|
||||
"@angular/core": ">=10.2.3",
|
||||
"@antv/x6": ">=1.0.0"
|
||||
"@antv/x6": "^1.x"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@angular/cdk": "^10.2.3",
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@antv/x6-react-shape",
|
||||
"version": "1.6.3",
|
||||
"version": "1.6.6",
|
||||
"description": "X6 shape for rendering react components.",
|
||||
"main": "lib/index.js",
|
||||
"module": "es/index.js",
|
||||
@ -31,9 +31,8 @@
|
||||
"build:watch:esm": "yarn build:esm --w",
|
||||
"build:watch:cjs": "yarn build:cjs --w",
|
||||
"build": "run-p build:cjs build:esm build:umd",
|
||||
"prebuild": "run-s lint clean",
|
||||
"prepare": "yarn build",
|
||||
"precommit": "lint-staged"
|
||||
"prebuild": "run-s clean",
|
||||
"prepare": "yarn build"
|
||||
},
|
||||
"lint-staged": {
|
||||
"src/**/*.ts": [
|
||||
@ -47,7 +46,7 @@
|
||||
"@antv/x6-package-json/rollup.json"
|
||||
],
|
||||
"peerDependencies": {
|
||||
"@antv/x6": ">=1.0.0",
|
||||
"@antv/x6": "^1.x",
|
||||
"react": ">=16.8.6 || >=17.0.0",
|
||||
"react-dom": ">=16.8.6 || >=17.0.0"
|
||||
},
|
||||
|
@ -6,17 +6,24 @@ import { Portal } from './portal'
|
||||
import { Wrap } from './wrap'
|
||||
|
||||
export class ReactShapeView extends NodeView<ReactShape> {
|
||||
protected targetId() {
|
||||
return `${this.graph.view.cid}:${this.cell.id}`
|
||||
}
|
||||
|
||||
protected init() {
|
||||
super.init()
|
||||
this.cell.on('removed', () => {
|
||||
Portal.disconnect(this.cell.id)
|
||||
Portal.disconnect(this.targetId())
|
||||
})
|
||||
}
|
||||
|
||||
getComponentContainer() {
|
||||
return this.cell.prop('useForeignObject') === false
|
||||
? (this.selectors.content as SVGElement)
|
||||
: (this.selectors.foContent as HTMLDivElement)
|
||||
return (
|
||||
this.selectors &&
|
||||
(this.cell.prop('useForeignObject') === false
|
||||
? (this.selectors.content as SVGElement)
|
||||
: (this.selectors.foContent as HTMLDivElement))
|
||||
)
|
||||
}
|
||||
|
||||
confirmUpdate(flag: number) {
|
||||
@ -42,7 +49,7 @@ export class ReactShapeView extends NodeView<ReactShape> {
|
||||
const component = this.graph.hook.getReactComponent(node)
|
||||
const elem = React.createElement(Wrap, { graph, node, component })
|
||||
if (Portal.isActive()) {
|
||||
Portal.connect(this.cell.id, ReactDOM.createPortal(elem, root))
|
||||
Portal.connect(this.targetId(), ReactDOM.createPortal(elem, root))
|
||||
} else {
|
||||
ReactDOM.render(elem, root)
|
||||
}
|
||||
@ -58,7 +65,7 @@ export class ReactShapeView extends NodeView<ReactShape> {
|
||||
}
|
||||
|
||||
unmount() {
|
||||
Portal.disconnect(this.cell.id)
|
||||
Portal.disconnect(this.targetId())
|
||||
this.unmountReactComponent()
|
||||
super.unmount()
|
||||
return this
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@antv/x6-vue-shape",
|
||||
"version": "1.5.3",
|
||||
"version": "1.5.4",
|
||||
"description": "X6 shape for rendering vue components.",
|
||||
"main": "lib/index.js",
|
||||
"module": "es/index.js",
|
||||
@ -48,7 +48,7 @@
|
||||
"vue-demi": "latest"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@antv/x6": ">=1.0.0",
|
||||
"@antv/x6": "^1.x",
|
||||
"@vue/composition-api": "^1.0.0-rc.6",
|
||||
"vue": "^2.6.12 || ^3.0.0"
|
||||
},
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@antv/x6",
|
||||
"version": "1.34.9",
|
||||
"version": "1.35.0",
|
||||
"description": "JavaScript diagramming library that uses SVG and HTML for rendering.",
|
||||
"main": "lib/index.js",
|
||||
"module": "es/index.js",
|
||||
|
@ -521,6 +521,11 @@ export class Transform extends Widget<Transform.Options> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected onRemove() {
|
||||
this.stopListening()
|
||||
super.onRemove()
|
||||
}
|
||||
}
|
||||
|
||||
export namespace Transform {
|
||||
|
@ -76,11 +76,16 @@ export class ObstacleMap {
|
||||
const excludeShapes = options.excludeShapes
|
||||
const excType = shape ? excludeShapes.includes(shape) : false
|
||||
const excTerminal = excludedTerminals.some((cell) => cell.id === node.id)
|
||||
const excNode = options.excludeNodes.includes(node)
|
||||
const excludedNode = options.excludeNodes.some((item) => {
|
||||
if (typeof item === 'string') {
|
||||
return node.id === item
|
||||
}
|
||||
return item === node
|
||||
})
|
||||
const excAncestor = excludedAncestors.includes(node.id)
|
||||
const excHidden = options.excludeHiddenNodes && !node.isVisible()
|
||||
const excluded =
|
||||
excType || excTerminal || excNode || excAncestor || excHidden
|
||||
excType || excTerminal || excludedNode || excAncestor || excHidden
|
||||
|
||||
if (!excluded) {
|
||||
const bbox = node.getBBox().moveAndExpand(options.paddingBox)
|
||||
|
@ -49,7 +49,7 @@ export interface ResolvedOptions {
|
||||
/**
|
||||
* Should certain nodes not be considered as obstacles?
|
||||
*/
|
||||
excludeNodes: Node[]
|
||||
excludeNodes: (Node | string)[]
|
||||
|
||||
/**
|
||||
* Should certain hidden nodes not be considered as obstacles?
|
||||
|
@ -77,7 +77,7 @@ export namespace HTML {
|
||||
}
|
||||
|
||||
protected renderHTMLComponent() {
|
||||
const container = this.selectors.foContent
|
||||
const container = this.selectors && this.selectors.foContent
|
||||
if (container) {
|
||||
const $wrap = this.$(container).empty()
|
||||
const component = this.graph.hook.getHTMLComponent(this.cell)
|
||||
|
@ -1153,15 +1153,18 @@ export class EdgeView<
|
||||
|
||||
for (let i = 0, ii = labels.length; i < ii; i += 1) {
|
||||
const label = labels[i]
|
||||
const labelNode = this.labelCache[i]
|
||||
|
||||
if (!labelNode) {
|
||||
continue
|
||||
}
|
||||
|
||||
const labelPosition = this.normalizeLabelPosition(
|
||||
label.position as Edge.LabelPosition,
|
||||
)
|
||||
const pos = ObjectExt.merge({}, defaultPosition, labelPosition)
|
||||
const matrix = this.getLabelTransformationMatrix(pos)
|
||||
this.labelCache[i].setAttribute(
|
||||
'transform',
|
||||
Dom.matrixToTransformString(matrix),
|
||||
)
|
||||
labelNode.setAttribute('transform', Dom.matrixToTransformString(matrix))
|
||||
}
|
||||
|
||||
return this
|
||||
|
@ -861,7 +861,9 @@ export class NodeView<
|
||||
if (options.frontOnly) {
|
||||
if (candidates.length > 0) {
|
||||
const zIndexMap = ArrayExt.groupBy(candidates, 'zIndex')
|
||||
const maxZIndex = ArrayExt.max(Object.keys(zIndexMap))
|
||||
const maxZIndex = ArrayExt.max(
|
||||
Object.keys(zIndexMap).map((z) => parseInt(z, 10)),
|
||||
)
|
||||
if (maxZIndex) {
|
||||
candidates = zIndexMap[maxZIndex]
|
||||
}
|
||||
|
@ -421,7 +421,7 @@ export namespace ToolsView {
|
||||
return this
|
||||
}
|
||||
|
||||
protected stamp(elem: Element = this.container) {
|
||||
protected stamp(elem: Element) {
|
||||
if (elem) {
|
||||
elem.setAttribute('data-cell-id', this.cellView.cell.id)
|
||||
}
|
||||
|
@ -185,7 +185,7 @@ export const MyComponent = memo(
|
||||
return // ...
|
||||
},
|
||||
(prev, next) => {
|
||||
return Boolean(next.node?.hasChanged('data'))
|
||||
return !next.node?.hasChanged('data')
|
||||
},
|
||||
)
|
||||
```
|
||||
|
@ -183,7 +183,7 @@ export const MyComponent = memo(
|
||||
return // ...
|
||||
},
|
||||
(prev, next) => {
|
||||
return Boolean(next.node?.hasChanged('data'))
|
||||
return !next.node?.hasChanged('data')
|
||||
},
|
||||
)
|
||||
```
|
||||
|
@ -29,7 +29,7 @@ class Edge extends BaseEdge {
|
||||
|
||||
#### 第二步:配置
|
||||
|
||||
调用继承的静态方法 `config(options)` 来配置[边选项](/en/docs/tutorial/basic/edge/#选项)的默认值、[自定义选项](/en/docs/tutorial/basic/cell#自定义选项)和[自定义属性](),例如通过 [markup](/en/docs/tutorial/basic/cell#markup) 来指定边默认的 SVG/HTML 结构,通过 [attrs](/en/docs/tutorial/basic/cell#attrs-1) 来指定边的默认属性样式,通过 [defaultlabel](/en/docs/tutorial/basic/edge#defaultlabel) 来指定边的默认标签样式。
|
||||
调用继承的静态方法 `config(options)` 来配置[边选项](/en/docs/tutorial/basic/edge/#选项)的默认值、[自定义选项](/en/docs/tutorial/basic/cell#自定义选项)和[自定义属性](),例如通过 [markup](/en/docs/tutorial/basic/cell#markup) 来指定边默认的 SVG/HTML 结构,通过 [attrs](/en/docs/tutorial/basic/cell#attrs-1) 来指定边的默认属性样式,通过 [defaultLabel](/en/docs/tutorial/basic/edge#defaultlabel) 来指定边的默认标签样式。
|
||||
|
||||
| 名称 | 类型 | 是否必选 | 默认值 | 说明 |
|
||||
|-----------|----------------------------------|----------|-----------|-------------------------------------------|
|
||||
|
@ -29,7 +29,7 @@ class Edge extends BaseEdge {
|
||||
|
||||
#### 第二步:配置
|
||||
|
||||
调用继承的静态方法 `config(options)` 来配置[边选项](/zh/docs/tutorial/basic/edge/#选项)的默认值、[自定义选项](/zh/docs/tutorial/basic/cell#自定义选项)和[自定义属性](),例如通过 [markup](/zh/docs/tutorial/basic/cell#markup) 来指定边默认的 SVG/HTML 结构,通过 [attrs](/zh/docs/tutorial/basic/cell#attrs-1) 来指定边的默认属性样式,通过 [defaultlabel](/zh/docs/tutorial/basic/edge#defaultlabel) 来指定边的默认标签样式。
|
||||
调用继承的静态方法 `config(options)` 来配置[边选项](/zh/docs/tutorial/basic/edge/#选项)的默认值、[自定义选项](/zh/docs/tutorial/basic/cell#自定义选项)和[自定义属性](),例如通过 [markup](/zh/docs/tutorial/basic/cell#markup) 来指定边默认的 SVG/HTML 结构,通过 [attrs](/zh/docs/tutorial/basic/cell#attrs-1) 来指定边的默认属性样式,通过 [defaultLabel](/zh/docs/tutorial/basic/edge#defaultlabel) 来指定边的默认标签样式。
|
||||
|
||||
| 名称 | 类型 | 是否必选 | 默认值 | 说明 |
|
||||
|-----------|----------------------------------|----------|-----------|-------------------------------------------|
|
||||
|
@ -494,7 +494,7 @@ function parseStringLabel(label: string): Label {
|
||||
|
||||
```ts
|
||||
Edge.config({
|
||||
defaultlabel: {
|
||||
defaultLabel: {
|
||||
markup: [
|
||||
{
|
||||
tagName: 'rect',
|
||||
|
@ -494,7 +494,7 @@ function parseStringLabel(label: string): Label {
|
||||
|
||||
```ts
|
||||
Edge.config({
|
||||
defaultlabel: {
|
||||
defaultLabel: {
|
||||
markup: [
|
||||
{
|
||||
tagName: 'rect',
|
||||
|
@ -79,7 +79,7 @@ const IndexPage = () => {
|
||||
type: 'News',
|
||||
title: 'X6 2.0 来了!',
|
||||
date: '2022.11.22',
|
||||
link: 'https://www.yuque.com/antv/operation/bgo171',
|
||||
link: 'https://www.yuque.com/antv/blog/kt8nugz3kdg32h7v',
|
||||
},
|
||||
]
|
||||
|
||||
|
Reference in New Issue
Block a user