Antv X6中的markup和attr是如何配合使用的

1,225 阅读3分钟
  • markup的定义是:指定了渲染节点/边时使用的 SVG/HTML 片段,我理解就是页面上当前的节点或者边都有哪几个部分,比如我们最常用的矩形rect,我们能看到的就是一个矩形和里面的文字,而markup的默认也是只有这两个 ,所以Shape.Rect 后的markup是这样

  •   {
        markup: [
          // 这部分代表矩形
          {
            tagName: 'rect',
            selector: 'body',
          }, 
          // 这部分代表文字
          {
            tagName: 'text',
            selector: 'label',
          },
        ],
      }
    
  •  attrs 选项定制节点样式,我认为其实attrs改的就是markup中的几部分,我们默认的写法里面有body和label,正好对应markup的两部分,我们既能使用tagName,也能使用selector,都能生效

  •   attrs: { 
          body: {
            fill: '#2ECC71',
            stroke: '#000',
          },
          label: {
            text: 'rect',
            fill: '#333',
            fontSize: 13,
          },
        }
    
  • 一般情况下,我们建一个新矩形节点是这样的,如果没有特殊增加或者减少markup,markup基本都默认不写


    const rect = graph.addNode({
      shape: 'rect', // 指定使用何种图形,默认值为 'rect'
      x: 100,
      y: 200,
      width: 80,
      height: 40,
      angle: 30,
      attrs: {
        body: {
          fill: 'blue',
        },
        label: {
          text: 'Hello',
          fill: 'white',
        },
      },
    })

**
  • 下面这是不隐藏markup的写法

  •   const rect = graph.addNode({
        shape: 'rect', // 指定使用何种图形,默认值为 'rect'
        x: 100,
        y: 200,
        width: 80,
        height: 40,
        angle: 30,
        markup: [
          {
            tagName: 'rect',
            selector: 'body',
          }, 
          {
            tagName: 'text',
            selector: 'label',
          },
        ],
        attrs: {
          body: {
            fill: 'blue',
          },
          label: {
            text: 'Hello',
            fill: 'white',
          },
        },
      })
    
  •  如果新增markup时,记得连默认的也写上,不然就会当成删除了默认的

举例

  • 比如我要实现一个两点之间连线,然后有一个点一直匀速移动

  • 首先新增两个点,然后连线,然后线中图中多了一个圆,创建过程就先不写了,按照官网写法就行了

  • 我们可以先获取到这个线,在线的markup上新增一个圆

  •   const edge = graph.addEdge({
          shape: 'edge', // 指定使用何种图形,默认值为 'edge'
          id: '1001',
          source: ball,
          target: ball1,
          markup: [
              {
                  "tagName": "path",
                  "selector": "wrap",
                  "groupSelector": "lines",
                  "attrs": {
                      "fill": "none",
                      "cursor": "pointer",
                      "stroke": "transparent",
                      "strokeLinecap": "round"
                  }
              },
              {
                  "tagName": "path",
                  "selector": "line",
                  "groupSelector": "lines",
                  "attrs": {
                      "fill": "none",
                      "pointerEvents": "none"
                  }
              },
              //上面两个是边的默认的markup,这个是我们新增的,而"tagName": "circle",也是默认的写法,
      证明我们新增的节点是一个圆。而selector的名字则是随便取的
              {
                  "tagName": "circle",
                  "selector": "ceshi"
              }
          ],
          attrs: {
           //attr和markup是配合使用的一一对应,只有markup或者attrs圆都是出不来的,
      使用markup创建出来圆的html,attrs给其加上样式,圆才能出来
              "lines": {
                  "connection": true,
                  "strokeLinejoin": "round"
              },
              "wrap": {
                  "strokeWidth": 10
              },
              "line": {
                  "stroke": "#333",
                  "strokeWidth": 2,
                  "targetMarker": "classic"
              },
              //我们新增了一个圆,这里设置样式
              "circle": {
                  "r": 5,
                  "fill": "#00FFFF",
                  // atConnectionRatio表示将指定元素移动到指定比例 [0, 1] 位置处,可以去官网上搜索
                  "atConnectionRatio": 0,
                  "refCy": -10
              }
          }
      })
      接下来我们将atConnectionRatio从0变成1就实现了匀速运动,我们使用动画,
      不过这是动画只运行一次的代码,如果想要循环一直运行,需要将动画运行完再次运行,循环进行
      edge1.transition(
      'attrs/circle/atConnectionRatio', 
      1, 
      {delay: 0, // 等待时间   duration: 3000}
      )
      
      //补充完整代码
      <template> 
        <div class="app">    
          <div id="container" class="area-center-container" /> 
         </div>
      </template>
      <script>import { defineComponent, onMounted } from 'vue';
      import { Graph, Timing } from "@antv/x6";
      export default defineComponent({   
       setup() {      
        let graph = null;    
          const initGraph = () => {    
              graph = new Graph({       
                container: document.getElementById('container'),     
                 background: {            
                  color: "#44546c"            
                 },  
                 connecting: {   
                       allowBlank: true,     
                 },           
                 mousewheel: true,  
                 panning: true,
                 grid: {
                          size: 10,
                           visible: true
                       },
                 napline: { 
                        enabled: true, 
                         sharp: true    
                  },           
                 scroller: {   
                       enabled: false,  
                        pageVisible: false,    
                      pageBreak: false,    
                      pannable: false    
                  }, })       
               const ball = graph.addNode({     
                 shape: 'circle',        
                  id: 'node999',          
                  x: 0,         
                  y: 40,      
                  width: 60,  
                  height: 60,  
                  attrs: {
                          label: {
                              text: 'ball', 
                             fontSize: 20    
                      },          
                  body: {         
                     fill: '#FFFFFF',   
                       }          
                        },          
                 })          
         const ball1 = graph.addNode({  
                    shape: 'circle',    
                  id: 'node1000',     
                  x: 1000,           
                  y: 40,          
                  width: 60,   
                   height: 60,   
                   attrs: {        
                  label: {          
                    text: 'ball',     
                         fontSize: 20 
                         },       
                   body: {       
                       fill: '#FFFFFF',
                          }   
                   },       
           })          
        const edge = graph.addEdge({   
                   shape: 'edge', 
                      id: '1001',   
                   source: ball,  
                    target: ball1,  
                })      
            let edge1 = graph.getCellById('1001')    
              let markup = edge1.getMarkup()     
             markup.push({    
                   tagName: 'circle',   
                   selector: 'circle'    
                   })     
             edge1.setMarkup(markup)            
        let attrOption = {           
           circle: {       
                   r: 5,   
                       fill: '#00FFFF',   
                       atConnectionRatio: 0,   
                       refCy: -10            
                  }
                  }           
               let options1 = {          
                      delay: 0,
                      duration: 3000,      
                      timing: Timing.linear,     
                      complete: () => {       
                        edge1.setAttrs(attrOption)     
                        edge1.transition('attrs/circle/atConnectionRatio', 1, options1)    
                       },             }         
               edge1.setAttrs(attrOption)      
               edge1.transition('attrs/circle/atConnectionRatio', 1, options1)        };   
              onMounted(() => {initGraph()});  
            return {            initGraph        }    },});
      </script>
      <style scoped>
      body {    padding: 0;    margin: 0;}
      .app {    height: 100vh;    width: 100vw;    display: flex;}
      #container {    flex: 1;}
      </style>