美文网首页Vue技术
AntV 开发 —— x6 图编辑引擎

AntV 开发 —— x6 图编辑引擎

作者: 潇潇剑_易水阁 | 来源:发表于2022-07-15 15:25 被阅读0次

    其实,只是想找个轮子

    前序:

    碎碎念:

    • 此文个人笔记,官网文档搬运
    • 因为官网对于vue结合Element-UI的示例就几个,本意只是看中了人工智能建模 DAG 图,但是这玩意demo是React,虽然核心的东西一样,但是傻瓜式才方便,故特意按vue2写了一次,没用ts,用ts的其实转换的成本不高
    • demo 待上传中【ps:因demo已魔改,那应该是不可能上传了 →_→ 】
    • x6 版本:1.32.8
    • x6-vue-shape 版本:1.4.0
    • composition-api 版本:1.7.0
    • vue 版本:2.6.11
    • vue-template-compiler 版本:2.6.11
    • Element-UI 版本:2.15.9

    一:步骤:

    1 —— 创建vue2项目:详情请看 vue开发 —— CLI(开发环境)搭建

    2 —— 引入开发组件【Element-UI、antv.x6】

    npm i element-ui
    npm install @antv/x6 
    npm install @antv/x6-vue-shape
    // 在vue2.x 若你引入x6-vue-shape,目前这个版本是必须要的,因由在常见问题2
    npm install @vue/composition-api --dev
    

    2.1 —— 生成的项目目录如下:

    Demo 
    ├─ node_modules
    ├─ public
         ├─ favicon.ico
         └─ index.html
    ├─ src
         ├─ assets
               └─ logo.png
         ├─ components
               └─ HelloWorld.vue
         ├─ App.vue
         └─ main.js
    ├─ .browserslistrc
    ├─ .eslintrc.js
    ├─  babel.config.js
    ├─ package.json
    ├─ package-lock.json
    └─ README.md
    

    2.2 —— 修改生成的项目【编辑package.json、编辑main.js、编辑App.vue、新增vue.config.js】

    // package.json 
    // 此处修改为解决常见问题一
     "dependencies": {
        "vue": "2.6.11"
      },
      "devDependencies": {
        "vue-template-compiler": "2.6.11"
      }
    
    // main.js
    import Vue from "vue";
    import App from "./App.vue";
    import ElementUI from 'element-ui';
    import 'element-ui/lib/theme-chalk/index.css';
    
    Vue.config.productionTip = false;
    Vue.use(ElementUI);
    
    new Vue({
      render: (h) => h(App),
    }).$mount("#app");
    
    // App.vue
    <template>
      <div id="app"></div>
    </template>
    
    <script>
    export default {
      name: 'App',
      components: {},
    };
    </script>
    
    <style></style>
    
    // vue.config.js
    // 此处新增为解决常见问题二
    module.exports = {
        runtimeCompiler: true
    }
    

    2.3 —— 开启搬运工生涯【创建画布、创建节点模板、新增节点、新增多个节点、定时改变多个节点状态、达成官网效果、搭配Element-UI、收工】

    官网效果图

    2.3.1 —— 创建画布

    // App.vue
    <template>
      <div id="app"></div>
    </template>
    
    <script>
    import { Graph } from '@antv/x6';
    import '@antv/x6-vue-shape'
    
    let graph = null;
    
    export default {
      name: 'App',
      components: {},
      mounted() {
        graph = new Graph({
          container: document.getElementById('app'),
          grid: true,
          autoResize: true,
        });
      },
    };
    </script>
    
    <style>
    #app {
      width: 100%;
      min-height: 500px;
    }
    </style>
    
    创建画布

    2.3.2 —— 创建节点模板

    // components/NodeTemplate.vue
    <template>
      <div :class="nodeClass" class="node">
        <img :src="logo" />
        <span class="label">{{ label }}</span>
        <span class="status">
          <img :src="statusImg" v-if="statusImg" />
        </span>
      </div>
    </template>
    
    <script>
    export default {
      inject: ['getGraph', 'getNode'],
      data() {
        return {
          status: '',
          label: '',
          statusImg: '',
          logo: '',
        };
      },
    
      methods: {
        mapper(source, target) {
          for (let key in target) {
            target[key] = source?.[key] ?? target[key];
          }
        },
      },
    
      created() {
        let node = this.getNode();
        // 初始化数据绑定
        this.mapper(node.data, this.$data);
        console.info(node);
        // 节点数据变化监听,从而绑定数据
        node.on('change:data', ({ current }) => this.mapper(current, this.$data));
      },
    
      computed: {
        nodeClass: function () {
          let clazz = {};
          if (this.status) clazz[this.status] = true;
          return clazz;
        },
      },
    };
    </script>
    

    2.3.3 —— 新增节点

    // App.vue
    <template>
      <div id="app"></div>
    </template>
    
    <script>
    import { Graph } from '@antv/x6';
    import '@antv/x6-vue-shape';
    import nodetemplate from '@/components/NodeTemplate';
    
    let graph = null;
    
    export default {
      name: 'App',
    
      data() {
        return {
          node: {
            id: '1',
            shape: 'vue-shape',
            component: 'nodetemplate',
            width: 180,
            height: 36,
            x: 290,
            y: 110,
            data: {
              label: '读数据',
              status: 'success',
              statusImg: '',
              logo: 'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*evDjT5vjkX0AAAAAAAAAAAAAARQnAQ',
            },
            ports: {
              groups: {
                top: {
                  position: 'top',
                  attrs: {
                    circle: {
                      r: 4,
                      magnet: true,
                      stroke: '#C2C8D5',
                      strokeWidth: 1,
                      fill: '#fff',
                    },
                  },
                },
                bottom: {
                  position: 'bottom',
                  attrs: {
                    circle: {
                      r: 4,
                      magnet: true,
                      stroke: '#C2C8D5',
                      strokeWidth: 1,
                      fill: '#fff',
                    },
                  },
                },
              },
              items: [
                {
                  id: '1-1',
                  group: 'bottom',
                },
              ],
            },
          },
        };
      },
    
      mounted() {
        graph = new Graph({
          container: document.getElementById('app'),
          grid: true,
          autoResize: true,
        });
        // 注册 nodeTemplate
        Graph.registerVueComponent(
          'nodetemplate',
          {
            template: `<nodetemplate />`,
            components: {
              nodetemplate,
            },
          },
          true
        );
    
        graph.addNode(this.node);
    
        graph.centerContent();
      },
    };
    </script>
    
    <style>
    #app {
      width: 100%;
      min-height: 500px;
    }
    
    .node {
      display: flex;
      align-items: center;
      width: 100%;
      height: 100%;
      background-color: #fff;
      border: 1px solid #c2c8d5;
      border-left: 4px solid #5f95ff;
      border-radius: 4px;
      box-shadow: 0 2px 5px 1px rgba(0, 0, 0, 0.06);
    }
    .node img {
      width: 20px;
      height: 20px;
      flex-shrink: 0;
      margin-left: 8px;
    }
    .node .label {
      display: inline-block;
      flex-shrink: 0;
      width: 104px;
      margin-left: 8px;
      color: #666;
      font-size: 12px;
    }
    .node .status {
      flex-shrink: 0;
    }
    .node.success {
      border-left: 4px solid #52c41a;
    }
    .node.failed {
      border-left: 4px solid #ff4d4f;
    }
    .node.running .status img {
      animation: spin 1s linear infinite;
    }
    .x6-node-selected .node {
      border-color: #1890ff;
      border-radius: 2px;
      box-shadow: 0 0 0 4px #d4e8fe;
    }
    .x6-node-selected .node.success {
      border-color: #52c41a;
      border-radius: 2px;
      box-shadow: 0 0 0 4px #ccecc0;
    }
    .x6-node-selected .node.failed {
      border-color: #ff4d4f;
      border-radius: 2px;
      box-shadow: 0 0 0 4px #fedcdc;
    }
    .x6-edge:hover path:nth-child(2) {
      stroke: #1890ff;
      stroke-width: 1px;
    }
    
    .x6-edge-selected path:nth-child(2) {
      stroke: #1890ff;
      stroke-width: 1.5px !important;
    }
    
    @keyframes running-line {
      to {
        stroke-dashoffset: -1000;
      }
    }
    @keyframes spin {
      from {
        transform: rotate(0deg);
      }
      to {
        transform: rotate(360deg);
      }
    }
    </style>
    
    单个节点

    2.3.4 —— 新增多个节点

    // App.vue
    // 因一些固定数据过于庞大,省略的部分请复制上一节的代码数据
    <template>
      <!--  -->
      <div id="app"></div>
    </template>
    
    <script>
    import { Graph } from '@antv/x6';
    import '@antv/x6-vue-shape';
    import nodetemplate from '@/components/NodeTemplate';
    
    let graph = null;
    
    export default {
      name: 'App',
    
      data() {
        return {
          nodeData: [
            {
              id: '1',
              shape: 'vue-shape',
              x: 290,
              y: 110,
              data: {
                label: '读数据',
                status: 'success',
              },
              ports: [
                {
                  id: '1-1',
                  group: 'bottom',
                },
              ],
            },
            {
              id: '2',
              shape: 'vue-shape',
              x: 290,
              y: 225,
              data: {
                label: '逻辑回归',
                status: 'success',
              },
              ports: [
                {
                  id: '2-1',
                  group: 'top',
                },
                {
                  id: '2-2',
                  group: 'bottom',
                },
                {
                  id: '2-3',
                  group: 'bottom',
                },
              ],
            },
            {
              id: '3',
              shape: 'vue-shape',
              x: 170,
              y: 350,
              data: {
                label: '模型预测',
                status: 'success',
              },
              ports: [
                {
                  id: '3-1',
                  group: 'top',
                },
                {
                  id: '3-2',
                  group: 'bottom',
                },
              ],
            },
            {
              id: '4',
              shape: 'vue-shape',
              x: 450,
              y: 350,
              data: {
                label: '读取参数',
                status: 'success',
              },
              ports: [
                {
                  id: '4-1',
                  group: 'top',
                },
                {
                  id: '4-2',
                  group: 'bottom',
                },
              ],
            },
            {
              id: '5',
              shape: 'dag-edge',
              source: {
                cell: '1',
                port: '1-1',
              },
              target: {
                cell: '2',
                port: '2-1',
              },
              zIndex: 0,
            },
            {
              id: '6',
              shape: 'dag-edge',
              source: {
                cell: '2',
                port: '2-2',
              },
              target: {
                cell: '3',
                port: '3-1',
              },
              zIndex: 0,
            },
            {
              id: '7',
              shape: 'dag-edge',
              source: {
                cell: '2',
                port: '2-3',
              },
              target: {
                cell: '4',
                port: '4-1',
              },
              zIndex: 0,
            },
          ],
          nodeImage: {
            logo: 'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*evDjT5vjkX0AAAAAAAAAAAAAARQnAQ',
            success:
              'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*6l60T6h8TTQAAAAAAAAAAAAAARQnAQ',
            failed:
              'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*SEISQ6My-HoAAAAAAAAAAAAAARQnAQ',
            running:
              'https://gw.alipayobjects.com/mdn/rms_43231b/afts/img/A*t8fURKfgSOgAAAAAAAAAAAAAARQnAQ',
          },
        };
      },
    
      methods: {
        init(data) {
          let cells = [];
          data.forEach((item) => {
            if (item.shape === 'vue-shape') {
              item.width = 180;
              item.height = 36;
              item.component = 'nodetemplate';
              item.data.logo = this.nodeImage.logo;
              item.data.statusImg = '';
              if (item.ports)
                item.ports = {
                  groups: {
                    top: {
                      position: 'top',
                      attrs: {
                        circle: {
                          r: 4,
                          magnet: true,
                          stroke: '#C2C8D5',
                          strokeWidth: 1,
                          fill: '#fff',
                        },
                      },
                    },
                    bottom: {
                      position: 'bottom',
                      attrs: {
                        circle: {
                          r: 4,
                          magnet: true,
                          stroke: '#C2C8D5',
                          strokeWidth: 1,
                          fill: '#fff',
                        },
                      },
                    },
                  },
                  items: item.ports,
                };
              cells.push(graph.createNode(item));
            } else {
              cells.push(graph.createEdge(item));
            }
          });
          graph.resetCells(cells);
        },
      },
    
      mounted() {
        graph = new Graph({
          container: document.getElementById('app'),
          grid: true,
          autoResize: true,
        });
        // 注册 nodeTemplate
        Graph.registerVueComponent(
          'nodetemplate',
          {
            template: `<nodetemplate />`,
            components: {
              nodetemplate,
            },
          },
          true
        );
    
        // 注册 nodeTemplate 的链接线
        Graph.registerEdge(
          'dag-edge',
          {
            inherit: 'edge',
            attrs: {
              line: {
                stroke: '#C2C8D5',
                strokeWidth: 1,
                targetMarker: null,
              },
            },
          },
          true
        );
    
        this.init(this.nodeData);
    
        graph.centerContent();
      },
    };
    </script>
    
    <style>
    /* 和上一节一样  */
    </style>
    
    多个节点

    2.3.5 —— 定时改变多个节点状态

    // App.vue
    // 因一些固定数据过于庞大,省略的部分请复制上一节的代码数据
    <template>
      <div id="app"></div>
    </template>
    
    <script>
    import { Graph } from '@antv/x6';
    import '@antv/x6-vue-shape';
    import nodetemplate from '@/components/NodeTemplate';
    
    let graph = null;
    
    export default {
      name: 'App',
    
      data() {
        return {
         // 和上节一样
          nodeStatusList: [
            [
              {
                id: '1',
                status: 'running',
              },
              {
                id: '2',
                status: 'default',
              },
              {
                id: '3',
                status: 'default',
              },
              {
                id: '4',
                status: 'default',
              },
            ],
            [
              {
                id: '1',
                status: 'success',
              },
              {
                id: '2',
                status: 'running',
              },
              {
                id: '3',
                status: 'default',
              },
              {
                id: '4',
                status: 'default',
              },
            ],
            [
              {
                id: '1',
                status: 'success',
              },
              {
                id: '2',
                status: 'success',
              },
              {
                id: '3',
                status: 'running',
              },
              {
                id: '4',
                status: 'running',
              },
            ],
            [
              {
                id: '1',
                status: 'success',
              },
              {
                id: '2',
                status: 'success',
              },
              {
                id: '3',
                status: 'success',
              },
              {
                id: '4',
                status: 'failed',
              },
            ],
          ],
        };
      },
    
      methods: {
      // 和上节一样
        showNodeStatus(statusList) {
          let status = statusList.shift();
          status?.forEach((item) => {
            let { id, status } = item;
            let node = graph.getCellById(id);
            let data = node.getData();
            node.setData({
              ...data,
              status: status,
              statusImg: this.nodeImage[status],
            });
          });
          setTimeout(() => {
            this.showNodeStatus(statusList);
          }, 3000);
        },
      },
    
      mounted() {
        // 和上节一样
        this.init(this.nodeData);
        this.showNodeStatus(this.nodeStatusList);
        graph.centerContent();
      },
    };
    </script>
    
    <style>
      // 和上节一样
    </style>
    
    
    定时改变多个节点状态

    2.3.6 —— 添加属性达成官网效果

    // App.vue
    // 因一些固定数据过于庞大,省略的部分请复制上一节的代码数据
    <template>
      <div id="app"></div>
    </template>
    
    <script>
    import { Graph, Path } from '@antv/x6';
    import '@antv/x6-vue-shape';
    import nodetemplate from '@/components/NodeTemplate';
    
    let graph = null;
    
    export default {
      name: 'App',
    
      data() {
        return {
           // 和上节一样
        };
      },
    
      methods: {
        // 和上节一样
      },
    
      mounted() {
        graph = new Graph({
          container: document.getElementById('app'),
          grid: true,
          autoResize: true,
          panning: {
            enabled: true,
            eventTypes: ['leftMouseDown', 'mouseWheel'],
          },
          mousewheel: {
            enabled: true,
            modifiers: 'ctrl',
            factor: 1.1,
            maxScale: 1.5,
            minScale: 0.5,
          },
          highlighting: {
            magnetAdsorbed: {
              name: 'stroke',
              args: {
                attrs: {
                  fill: '#fff',
                  stroke: '#31d0c6',
                  strokeWidth: 4,
                },
              },
            },
          },
          connecting: {
            snap: true,
            allowBlank: false,
            allowLoop: false,
            highlight: true,
            connector: 'algo-connector',
            connectionPoint: 'anchor',
            anchor: 'center',
            validateMagnet({ magnet }) {
              return magnet.getAttribute('port-group') !== 'top';
            },
            createEdge() {
              return graph.createEdge({
                shape: 'dag-edge',
                attrs: {
                  line: {
                    strokeDasharray: '5 5',
                  },
                },
                zIndex: -1,
              });
            },
          },
          selecting: {
            enabled: true,
            multiple: true,
            rubberEdge: true,
            rubberNode: true,
            modifiers: 'shift',
            rubberband: true,
          },
        });
        // 注册 nodeTemplate
        Graph.registerVueComponent(
          'nodetemplate',
          {
            template: `<nodetemplate />`,
            components: {
              nodetemplate,
            },
          },
          true
        );
    
        // 注册 nodeTemplate 的链接线
        Graph.registerEdge(
          'dag-edge',
          {
            inherit: 'edge',
            attrs: {
              line: {
                stroke: '#C2C8D5',
                strokeWidth: 1,
                targetMarker: null,
              },
            },
          },
          true
        );
    
        // 注册 nodeTemplate 的链接之间的线样式
        Graph.registerConnector(
          'algo-connector',
          (s, e) => {
            const offset = 4;
            const deltaY = Math.abs(e.y - s.y);
            const control = Math.floor((deltaY / 3) * 2);
    
            const v1 = { x: s.x, y: s.y + offset + control };
            const v2 = { x: e.x, y: e.y - offset - control };
    
            return Path.normalize(
              `M ${s.x} ${s.y}
           L ${s.x} ${s.y + offset}
           C ${v1.x} ${v1.y} ${v2.x} ${v2.y} ${e.x} ${e.y - offset}
           L ${e.x} ${e.y}
          `
            );
          },
          true
        );
    
        // 控制线链接时的样式
        graph.on('edge:connected', ({ edge }) => {
          edge.attr({
            line: {
              strokeDasharray: '',
            },
          });
        });
    
        // 控制节点数据变更时线的样式
        graph.on('node:change:data', ({ node }) => {
          let edges = graph.getIncomingEdges(node);
          let { status } = node.getData();
          edges?.forEach((edge) => {
            if (status === 'running') {
              edge.attr('line/strokeDasharray', 5);
              edge.attr('line/style/animation', 'running-line 30s infinite linear');
            } else {
              edge.attr('line/strokeDasharray', '');
              edge.attr('line/style/animation', '');
            }
          });
        });
    
        this.init(this.nodeData);
        this.showNodeStatus(this.nodeStatusList);
        graph.centerContent();
      },
    };
    </script>
    
    <style>
        // 和上节一样
    </style>
    
    
    达成效果

    2.3.6 —— 废话了这么多,就是和一开始引入的Element-UI无关,客官莫急,菜来也

    // components/EleTemplate.vue
    <template>
      <el-alert title="潇风剑易水" type="warning" close-text="赛雷"> </el-alert>
    </template>
    
    // App.vue
    import eletemplate from '@/components/EleTemplate';
       // 注册 eletemplate
        Graph.registerVueComponent(
          'eletemplate',
          {
            template: `<eletemplate />`,
            components: {
              eletemplate,
            },
          },
          true
        );
    
        graph.addNode({
          id: '1',
          shape: 'vue-shape',
          component: 'eletemplate',
          width: 180,
          height: 36,
          x: 290,
          y: 110,
        });
        
        graph.centerContent();
    
    搭配Element-UI效果图

    2.3.7 —— 收工,翻归卖豉油

    二:常见问题:

    1 —— 运行时编译报错如下:

    Vue packages version mismatch:
    
    - vue@2.6.14 (D:\x6-dag\node_modules\vue\dist\vue.runtime.common.js)
    - vue-template-compiler@2.7.5 (D:\x6-dag\node_modules\vue-template-compiler\package.json)
    
    This may cause things to work incorrectly. Make sure to use the same version for both.
    If you are using vue-loader@>=10.0, simply update vue-template-compiler.
    If you are using vue-loader@<10.0 or vueify, re-installing vue-loader/vueify should bump vue-template-compiler to the latest.
    
    Error: 
    
    Vue packages version mismatch:
    
    - vue@2.6.14 (D:\x6-dag\node_modules\vue\dist\vue.runtime.common.js)
    - vue-template-compiler@2.7.5 (D:\x6-dag\node_modules\vue-template-compiler\package.json)
    
    This may cause things to work incorrectly. Make sure to use the same version for both.
    If you are using vue-loader@>=10.0, simply update vue-template-compiler.
    If you are using vue-loader@<10.0 or vueify, re-installing vue-loader/vueify should bump vue-template-compiler to the latest.
    

    解决方法:

    // package.json
    // 仅修改 package.json的dependencies节点的vue版本和devDependencies节点的vue-template-compiler版本,
    // 均需要去掉^,保持2者的版本一致,重新npm i 再启动
     "dependencies": {
        "vue": "2.6.11"
      },
      "devDependencies": {
        "vue-template-compiler": "2.6.11"
      }
    

    2 —— 运行时浏览器报错如下:

    [Vue warn]: You are using the runtime-only build of Vue where the template compiler is not available. Either pre-compile the templates into render functions, or use the compiler-included build.
    

    解决方法:

    // 项目根目录创建vue.config.js并添加如下内容
    // vue.config.js
    module.exports = {
        runtimeCompiler: true
    }
    

    2 —— 编译时报错如下:

    This dependency was not found:
    
    * @vue/composition-api in ./node_modules/vue-demi/lib/index.esm.js
    
    To install it, you can run: npm install --save @vue/composition-api
    

    解决方法:

    npm install --save @vue/composition-api
    

    3 —— 当卸载composition-api在运行时不会报错正常运行,问题2不会在提示,同时缺失这个,导致vue-demi的判断会一直是isVue2为false,isVue3为true,从而当渲染vue模板时会一直走vue3的方法,个人认为vue-demi应该去检查运行中的vue对象,由vue对象的某些vue3特性来判断是否走vue3内容,vue-demi是x6-vue-shape的依赖非本项目的依赖:

    4 —— 拖曳创建的节点,使用node.setData不触发绑定的change:data事件,但是实际上打印出来的node的data的确改动了,在数据为null的情况下,这个的确生效,但是并不是通过change:data事件触发的,暂时从源码也没法看出错误点,还没排除新版本是否已修复,源码看的新版本的,后续会更新此版本写的demo:setData为updateData

    临时解决方法:

    // 从packages/x6/src/model/cell.ts的源码来看这个updateData还是调用了自身的setData,其他地方找不到setData此方法的定义,唯一的区别也加了deep:false还是没法生效,暂时没时间去研究这个
    node.updateData
    

    5 —— 关于html元素如何拥有链接桩的功能请看另一篇博文,待更,因由为现成的链接桩自定义过于麻烦,且那些算法排定位,其实对于html元素固有的元素布局来看并不是很适用,除非是大规模改进用自定义,那无疑回到了原始的画图状态,增加了复杂性,故简单而为是用现有的元素进行划分为链接桩,但是这无疑脱离了x6所管理链接桩的机制,后续会看看要不要对接上这种机制,还是它那个自定义链接桩就是这种玩法,我只不过在绕个圈,但是文档关于这部分很模糊,毕竟脱离了它ponts定义的机制,而是添加了成为链接桩的特征去实现链接状功能的做法

    6 —— 关于右键菜单自定义问题,因为菜单这一块,很不好的是这玩意是要引入React组件,其实为何会有问题5和6,明显就是依赖过重,抽象层不应该牵扯太多具体的框架,即使明明知道这玩意靠监听事件就能处理右键菜单的问题,这一块的自定义也是基于html元素来完成,会有新的一篇博文来说明这个,待更

    三:吹水:

    1 首先这次的排版不是很好,毕竟本意是要循序渐进,和状态机一样,一步步到达终点,总感觉这里的makadown定制化太少了,不能尽情表达一些动态效果

    2 然后是x6-vue-shape这个感觉只是意思下而已,没有x6-react-shape和React的配合好,毕竟主打的方向就不是vue的,这从数据的传递到组件可以看出,单单用注入方法是不太好的,毕竟你的data变化还需要监听一层,何不在写template时传参prop,由prop去更新里面的状态或者附加到data,通过父组件通知子组件更新的方式,减少使用者对这一层的处理,然后就是注册vue模板时,那些属性不能像普通那些传参进去,必须要addNode带进去的才符合,这明显与结构和数据分离不相符合,我觉得在addNode时,应该是各自变化的数据,而不是一些基础数据,然后节点命名那一块需要注意,大小驼峰写法都不行,仅首字母大写和全小写,因为时间有限我暂时都用全小写去规避组件名称找不到问题,还有那个ports节点,官网的数据为数组形式,但是因为注册vue模板这个玩意加了ports是没效的,为了省事,只能是addNode时进行改变ports结构进行处理了,当然也可以改节点数据,其实这短短的几个效果就需要这么多的东西支撑,还是不太好玩,后续会自行改写,以便适应生产

    3 要是没浏览器的兼容考虑,还是用vue3去搭建,而且用上ts,让继承来处理这种很绕的复用关系,毕竟写出来是给人看的

    相关文章

      网友评论

        本文标题:AntV 开发 —— x6 图编辑引擎

        本文链接:https://www.haomeiwen.com/subject/bpwxirtx.html