This is the fourth day of my participation in Gwen Challenge


First, a brief introduction to X6

X6 is a graph editing engine, which is characterized by strong customization ability of nodes, edges and other elements. It is often used to build flow charts, ER charts, DAG charts, brain maps and other applications.

According to the examples on the website, the X6 can do the following

Concrete example, see here: x6. Antv. Vision/useful/examples…

What is a component?

Nodes and edges are collectively referred to as components. A component is a common base class for nodes and edges.

Components define node and edge common properties and methods, such as property styles, visibility, business data, and so on, and have the same behavior in instantiation, custom styles, configuring default options, and so on.

Look at the following inheritance:

graph LR
Cell --> Node
Cell --> Edge

Node --> Shape.Rect       
Node --> Shape.Circle    
Node --> Shape.Ellipse    
Node --> Shape.Xxx...    

Edge --> Shape.Edge
Edge --> Shape.DoubleEdge
Edge --> Shape.ShadowEdge

Built-in components

There are a number of built-in nodes and edges

In addition to the built-in components mentioned above, there is another component that is not mentioned in the documentation but is quite useful.

The constructor The name of the shape describe
Shape.Empty empty A node with no preset configuration. Need to pass throughmarkupattrSpecify graphics and styles

Custom component

While many built-in components are officially available, they may not meet the rich business requirements. This is where you need to create custom components.

The official term for custom components is custom node

How to develop a custom component?

Inherit the node development component provided by the official

The official gives an example of inheritance

But beyond what the official documentation says, we can even write inherited classes directly without calling static methods. This part of the implementation is inspired by “scaling nodes according to image size”.

Here we use the custom switch node example to demonstrate creating custom components by writing directly to inherited classes

First, draw a switch that looks like this:

Markup and attR attributes are used to draw SVG graphics. Among them

  • markupResponsible for creating nodes of elements in SVG in a structured way
  • attrResponsible for setting properties such as the style of the node

These two sections refer to the Markup and attR sections in the code listing.

const switchCenter = {
  x: 35.y: -2,}const switchOpen = `rotate(-30 ${switchCenter.x} ${switchCenter.y}) `
const switchClose = `rotate(-12 ${switchCenter.x} ${switchCenter.y}) `

const markup = [
  {
    tagName: 'g'.selector: 'left-group'.children: [{tagName: 'rect'.selector: 'left'.groupSelector: 'line'.attrs: {
          x: 0.y: 0,}}, {tagName: 'circle'.selector: 'lco'.groupSelector: 'co'.attrs: {
          cx: 30,}}, {tagName: 'circle'.selector: 'lci'.groupSelector: 'ci'.attrs: {
          cx: 30,},},],}, {tagName: 'rect'.selector: 'switch'.groupSelector: 'line'}, {tagName: 'g'.selector: 'right-group'.children: [{tagName: 'rect'.selector: 'right'.groupSelector: 'line'.attrs: {
          x: 70.y: 0,}}, {tagName: 'circle'.selector: 'rco'.groupSelector: 'co'.attrs: {
          cx: 70,}}, {tagName: 'circle'.selector: 'rci'.groupSelector: 'ci'.attrs: {
          cx: 70,},},],},const attrs = {
  line: {
    width: 30.height: 2.fill: '# 000'.stroke: '# 000',},co: {
    r: 8.fill: '# 000',},ci: {
    r: 4.fill: '#fff',},switch: {
    ...switchCenter,
    width: 35.transform: switchOpen,
  },
}
Copy the code

In the code above

  • switchCenterSpecifies the rotation center coordinates of the switch,
  • switchOpenswitchCloseRepresents the style of switch on and switch off, respectively.

Second, create a custom node

import { Graph, Shape } from '@antv/x6'

class Switch extends Shape.Empty {

  constructor(metadata) {
    super(Object.assign({}, metadata, { markup, attrs }))
  }
}

Graph.registerNode('switch', Switch, true)
Copy the code

Notice the constructor part above?

constructor(metadata) {
  super(Object.assign({}, metadata, { markup, attrs }))
}
Copy the code

If a constructor is present, the super method must be called, passing in the metadata value. The style data specified above can be passed in when the super method is called.

Then, add the switch dynamic effect

Add the toggleOpen method to the custom node created above. This method can switch switch states.

class Switch extends Shape.Empty {

  constructor(metadata) {
    super(Object.assign({}, metadata, { markup, attrs }))
  }

  toggleOpen() {
    const attrPath = 'attrs/switch/transform'
    const current = this.prop(attrPath)
    const target = current === switchOpen ? switchClose : switchOpen
    this.transition(attrPath, target, {
      interp: (a, b) = > {
        const reg = / -? \d+/g
        const start = parseInt(a.match(reg)[0].10)
        const end = parseInt(b.match(reg)[0].10)
        const d = end - start
        return (t) = > {
          return `rotate(${start + d * t} ${switchCenter.x} ${switchCenter.y}) `}},})}}Copy the code

We expect to perform the switch action when we click.

According to the developers, you need to create a custom view, register the binding event in the view, and then specify it as the custom View when you create or declare the component.

For information about views, see View

Since we are creating a node, the corresponding view needs to inherit from the NodeView.

Specific implementation process:

First, create a View
class SwitchView extends NodeView {
  onClick() {
    this.cell? .toggleOpen(); } } Graph.registerView('switch', SwitchView, true)
Copy the code
Then modify the custom component to specify the correspondingview

The view property has the same value as the first parameter in graph.registerView

class Switch extends Shape.Empty {

  constructor(metadata) {
- super(Object.assign({}, metadata, { markup, attrs }))
+ super(Object.assign({}, metadata, { markup, attrs, view: 'switch' }))}... }Copy the code

Complete the above steps and a custom switch component is complete.

Use the same way as the built-in components:

graph.addNode({
   x: 320.y: 120.shape: 'switch'
})
Copy the code

See Github Gist for the full code listing

Render the content using HTML/Vue/React

According to the official story

There is a special

element in SVG that can be embedded with any XHTML element, so we can use this element to render HTML elements and React components in place.

<svg xmlns="http://www.w3.org/2000/svg">
  <foreignObject width="120" height="50">
    <body xmlns="http://www.w3.org/1999/xhtml">
      <p>Hello World</p>
    </body>
  </foreignObject>
</svg>
Copy the code

Render with HTML/React/Vue

According to the document, we can draw the following conclusions:

  • HTMLRender content is passedDOM APIoperate
  • Vue/ReactRendering content is essentially executed$mountoperation