Antv/X6<圖編輯引擎>

1618558278291.jpg
  • 你可以直接查看???? 官方文檔 ????,文檔寫的很棒????;
  • 你也可以簡(jiǎn)單聽我BBLL,順便告訴你我的親身經(jīng)歷姨拥,和常用參數(shù)帜乞;

遇到問(wèn)題:

當(dāng)你遇到下面這幾種圖形編輯時(shí)诊霹,你可能會(huì)想:
??這不是要專門搞一套js代碼嗎掌挚,canvas我也不熟練啊??玻淑,這工程量也太大了族壳,放棄吧?淄ァ梳侨!跟老板說(shuō)這個(gè)需求做不了

2021-04-16 10-46-28.gif

當(dāng)然x1蛉威,起初我也是這么想的,而且我也嘗試了自己用canvas寫一個(gè)這個(gè)東西~
當(dāng)然x2走哺,寫起來(lái)又是一碼事了蚯嫌,什么小問(wèn)題各種層出不窮,然后果斷棄坑~
當(dāng)然x3,我可以百度找現(xiàn)成的代碼啊丙躏,網(wǎng)上那么多插件啥的择示,后來(lái)才發(fā)現(xiàn)魚目混雜,根本無(wú)從下手晒旅,萬(wàn)一遇到問(wèn)題找誰(shuí)栅盲?
??♂???♂?所以,在最后朋友(朋友多還是有那么點(diǎn)用的,致敬 ??木兄)的推薦下废恋,還是遇到了它 ------???? Antv/X6????!!

步入正題:

2021-04-16 10-32-55.gif

上圖是我結(jié)合自身項(xiàng)目需求+ Vue谈秫,制作的類似于一個(gè)工廠智能小車工作線路圖扒寄,沒聽懂的,也可以忽略這句話??~
???? 官方文檔入口 ????
常用參數(shù):

      //創(chuàng)建地圖
      this.graph = new Graph({
        snapline: true, //對(duì)齊線
        history: {
          enabled: true, //歷史記錄
          ignoreChange: true //ignoreChange 是否忽略屬性變化
        },
        panning: true, //支持平移拖拽
        container: document.getElementById('container'),
        width: window.innerWidth,
        height: window.innerHeight,
        background: {
          color: '#fffbe6' // 設(shè)置畫布背景顏色
        },
        grid: {
          size: 10, // 網(wǎng)格大小 10px
          visible: true // 渲染網(wǎng)格背景
        },
        connecting: {
          allowPort: true, //是否允許邊鏈接到鏈接樁
          allowEdge: false, //是否允許邊鏈接到另一個(gè)邊
          allowNode: false, //是否允許邊鏈接到節(jié)點(diǎn)(非節(jié)點(diǎn)上的鏈接樁)
          allowLoop: false, //是否允許創(chuàng)建循環(huán)連線拟烫,即邊的起始節(jié)點(diǎn)和終止節(jié)點(diǎn)為同一節(jié)點(diǎn)
          allowMulti: false, //是否允許在相同的起始節(jié)點(diǎn)和終止之間創(chuàng)建多條邊
          allowBlank: false, //是否允許連接到畫布空白位置的點(diǎn)
          // 自動(dòng)吸附
          snap: {
            radius: 20
          }
        }
      })
//加載地圖數(shù)據(jù)
this.graph.fromJSON(this.data)

右上角工具欄:

image.png

自己寫的该编,主要包含 撤銷重做,創(chuàng)建節(jié)點(diǎn),圖形放大,圖形縮小,重置視圖,數(shù)據(jù)導(dǎo)出构灸;

//撤銷操作
this.graph.undo()
//重做
this.graph.redo()
//創(chuàng)建節(jié)點(diǎn)
 const rect = this.graph.addNode({
  shape: 'rect', // 指定使用何種圖形上渴,默認(rèn)值為 'rect'
  ...
})
//地圖放大縮小
 this.graph.zoom(0.1)
 this.graph.zoom(-0.1)
//重置視圖
this.graph.centerContent() //畫布居中
this.graph.zoom(0)
//序列化/反序列化 數(shù)據(jù)格式
// https://antv-x6.gitee.io/zh/docs/tutorial/intermediate/serialization
// graph.toJSON() 方法來(lái)導(dǎo)出圖中的節(jié)點(diǎn)和邊
this.graph.toJSON()
選中和工具Tools:

2021-04-16 11-13-07.gif

使用工具 Tools文檔
節(jié)點(diǎn)和邊選中代碼示例
節(jié)點(diǎn)和邊選中——事件屬性參考文檔

//在創(chuàng)建節(jié)點(diǎn)的時(shí)候,直接加入刪除按鈕tools
  const rect = this.graph.addNode({
          shape: 'rect', // 指定使用何種圖形喜颁,默認(rèn)值為 'rect'
          ...
          attrs: {
          ...
          },
         //使用工具
          tools: [
            {
              name: 'button-remove', // 工具名稱
              args: { x: 5, y: 5 } // 工具對(duì)應(yīng)的參數(shù)
            }
          ]})
案例代碼:
<template>
  <div>
    <span class="toolbar">
      <div class="tool" title="撤銷" @click="toUndo()">
        <i class="el-icon-refresh-left"></i>
      </div>
      <div class="tool" title="重做" @click="toRedo()">
        <i class="el-icon-refresh-right"></i>
      </div>
      <div class="tool" title="創(chuàng)建節(jié)點(diǎn)" @click="createNodes('rect')">
        <div class="rect"></div>
      </div>
      <div class="tool" @click="mapZoom('+')" title="放大視圖">
        <i class="el-icon-zoom-in"></i>
      </div>
      <div class="tool" @click="mapZoom('-')" title="縮小視圖">
        <i class="el-icon-zoom-out"></i>
      </div>
      <div class="tool" @click="mapZoom()" title="重置視圖">
        <i class="el-icon-full-screen"></i>
      </div>
      <div class="tool" @click="save()" title="保存">
        <i class="el-icon-cpu"></i>
      </div>
    </span>
    <div id="container"></div>
  </div>
</template>

<script>
import { Graph } from '@antv/x6'
export default {
  data() {
    return {
      graph_zoom: 0, //地圖縮放比例
      graph: null,
      //   data: {
      // 節(jié)點(diǎn)
      // nodes: [
      //   {
      //     id: 'node1', // String稠氮,可選,節(jié)點(diǎn)的唯一標(biāo)識(shí)
      //     x: 40, // Number半开,必選隔披,節(jié)點(diǎn)位置的 x 值
      //     y: 40, // Number,必選寂拆,節(jié)點(diǎn)位置的 y 值
      //     width: 60, // Number奢米,可選,節(jié)點(diǎn)大小的 width 值
      //     height: 60, // Number纠永,可選鬓长,節(jié)點(diǎn)大小的 height 值
      //     label: 'node1', // String,節(jié)點(diǎn)標(biāo)簽
      //     tools: ['button-remove']
      //   },
      //   {
      //     id: 'node2', // String尝江,節(jié)點(diǎn)的唯一標(biāo)識(shí)
      //     x: 160, // Number涉波,必選,節(jié)點(diǎn)位置的 x 值
      //     y: 180, // Number炭序,必選啤覆,節(jié)點(diǎn)位置的 y 值
      //     width: 60, // Number,可選惭聂,節(jié)點(diǎn)大小的 width 值
      //     height: 60, // Number窗声,可選,節(jié)點(diǎn)大小的 height 值
      //     label: 'node2' // String辜纲,節(jié)點(diǎn)標(biāo)簽
      //   },
      //   {
      //     id: 'node3', // String笨觅,節(jié)點(diǎn)的唯一標(biāo)識(shí)
      //     x: 40, // Number,必選侨歉,節(jié)點(diǎn)位置的 x 值
      //     y: 180, // Number屋摇,必選,節(jié)點(diǎn)位置的 y 值
      //     width: 60, // Number幽邓,可選炮温,節(jié)點(diǎn)大小的 width 值
      //     height: 60, // Number,可選牵舵,節(jié)點(diǎn)大小的 height 值
      //     label: 'node3' // String柒啤,節(jié)點(diǎn)標(biāo)簽
      //   }
      // ],
      // 邊
      // edges: [
      //   {
      //     source: 'node1', // String倦挂,必須,起始節(jié)點(diǎn) id
      //     target: 'node2' // String担巩,必須方援,目標(biāo)節(jié)點(diǎn) id
      //   }
      // ],
      //   }
      data: []
    }
  },
  mounted() {
    this.init()
  },
  methods: {
    //初始化地圖
    init() {
      //創(chuàng)建地圖
      this.graph = new Graph({
        snapline: true, //對(duì)齊線
        history: {
          enabled: true, //歷史記錄
          ignoreChange: true //ignoreChange 是否忽略屬性變化
        },
        panning: true, //支持平移拖拽
        container: document.getElementById('container'),
        width: window.innerWidth,
        height: window.innerHeight,
        background: {
          color: '#fffbe6' // 設(shè)置畫布背景顏色
        },
        grid: {
          size: 10, // 網(wǎng)格大小 10px
          visible: true // 渲染網(wǎng)格背景
        },
        connecting: {
          allowPort: true, //是否允許邊鏈接到鏈接樁
          allowEdge: false, //是否允許邊鏈接到另一個(gè)邊
          allowNode: false, //是否允許邊鏈接到節(jié)點(diǎn)(非節(jié)點(diǎn)上的鏈接樁)
          allowLoop: false, //是否允許創(chuàng)建循環(huán)連線,即邊的起始節(jié)點(diǎn)和終止節(jié)點(diǎn)為同一節(jié)點(diǎn)
          allowMulti: false, //是否允許在相同的起始節(jié)點(diǎn)和終止之間創(chuàng)建多條邊
          allowBlank: false, //是否允許連接到畫布空白位置的點(diǎn)
          // 自動(dòng)吸附
          snap: {
            radius: 20
          },
          // createEdge() {
          //   //創(chuàng)建動(dòng)畫虛線邊
          //   return new Shape.Edge({
          //     attrs: {
          //       line: {
          //         stroke: '#1890ff',
          //         strokeDasharray: 5,
          //         targetMarker: 'classic',
          //         style: {
          //           animation: 'ant-line 30s infinite linear'
          //         }
          //       }
          //     }
          //   })
          // }
        }
      })
      //加載地圖數(shù)據(jù)
      this.graph.fromJSON(this.data)
      this.graph.centerContent() //畫布居中
      //節(jié)點(diǎn)點(diǎn)擊事件
      this.graph.on('node:click', ({ e, x, y, node, view }) => {
        // console.log(node)
        this.selectReset()
        node.attr('body/stroke', 'orange')
      })
      //邊點(diǎn)擊事件
      this.graph.on('edge:click', ({ e, x, y, edge, view }) => {
        // console.log(edge)
        this.selectReset()
        edge.attr('line/stroke', 'orange')
        edge.prop('labels/0', {
          attrs: {
            body: {
              stroke: 'orange'
            }
          }
        })
      }),
        //節(jié)點(diǎn)雙擊事件
        this.graph.on('node:dblclick', ({ e, x, y, node, view }) => {
          alert('節(jié)點(diǎn)ID:' + node.id)
          console.log(node)
        })
      //邊雙擊事件
      this.graph.on('edge:dblclick', ({ e, x, y, edge, view }) => {
        console.log(edge)
        alert(
          `邊ID:${edge.id}, 起始節(jié)點(diǎn): ${edge.source.cell},目標(biāo)節(jié)點(diǎn): ${edge.target.cell}`
        )
      })
    },
    //保存涛癌,獲取節(jié)點(diǎn)等數(shù)據(jù)
    save() {
      //序列化/反序列化 數(shù)據(jù)格式
      // https://antv-x6.gitee.io/zh/docs/tutorial/intermediate/serialization
      // graph.toJSON() 方法來(lái)導(dǎo)出圖中的節(jié)點(diǎn)和邊
      console.log(this.graph.toJSON())
    },
    //撤銷操作
    toUndo() {
      this.graph.undo()
    },
    //重做
    toRedo() {
      this.graph.redo()
      //   if (this.graph.isHistoryEnabled()) {
      //     this.graph.disableHistory()
      //   } else {
      //     this.graph.enableHistory()
      //   }
    },
    //地圖放大縮小
    mapZoom(type) {
      if (type == '+') {
        this.graph.zoom(0.1)
        this.graph_zoom += 0.1
      } else if (type == '-') {
        this.graph.zoom(-0.1)
        this.graph_zoom -= 0.1
      } else {
        this.graph.zoom(
          this.graph_zoom <= 0 ? Math.abs(this.graph_zoom) : -this.graph_zoom
        )
        this.graph.centerContent() //畫布居中
        this.graph_zoom = 0
      }
    },
    //創(chuàng)建節(jié)點(diǎn)
    createNodes(type) {
      if (type == 'rect') {
        const rect = this.graph.addNode({
          shape: 'rect', // 指定使用何種圖形犯戏,默認(rèn)值為 'rect'
          x: 0,
          y: 0,
          width: 60,
          height: 60,
          angle: 0,
          attrs: {
            body: {
              fill: '#fff', // 背景顏色
              stroke: '#000' // 邊框顏色
            },
            label: {
              text: 'Node', // 文本
              fill: '#333', // 文字顏色
              fontSize: 13 // 文字大小
            }
          },
          tools: [
            {
              name: 'button-remove', // 工具名稱
              args: { x: 5, y: 5 } // 工具對(duì)應(yīng)的參數(shù)
            }
          ],
          //連接樁
          //   ports: [
          // {
          //   id: 'port1',
          //   attrs: {
          //     circle: {
          //       r: 6,
          //       //    magnet: true 這個(gè)特殊屬性,使鏈接樁在連線交互時(shí)可以被連接上
          //       magnet: true,
          //       stroke: '#31d0c6',
          //       strokeWidth: 2,
          //       fill: '#fff'
          //     }
          //   }
          // },
          // {
          //   id: 'port2',
          //   attrs: {
          //     circle: {
          //       r: 6,
          //       magnet: true,
          //       stroke: '#31d0c6',
          //       strokeWidth: 2,
          //       fill: '#fff'
          //     }
          //   }
          // }
          //   ]
          ports: {
            groups: {
              // 輸入鏈接樁群組定義
              left: {
                position: 'left',
                attrs: {
                  circle: {
                    r: 4,
                    magnet: true,
                    stroke: '#31d0c6',
                    strokeWidth: 2,
                    fill: '#fff'
                  }
                }
              },
              right: {
                position: 'right',
                attrs: {
                  circle: {
                    r: 4,
                    magnet: true,
                    stroke: '#31d0c6',
                    strokeWidth: 2,
                    fill: '#fff'
                  }
                }
              },
              top: {
                position: 'top',
                attrs: {
                  circle: {
                    r: 4,
                    magnet: true,
                    stroke: '#31d0c6',
                    strokeWidth: 2,
                    fill: '#fff'
                  }
                }
              },
              bottom: {
                position: 'bottom',
                attrs: {
                  circle: {
                    r: 4,
                    magnet: true,
                    stroke: '#31d0c6',
                    strokeWidth: 2,
                    fill: '#fff'
                  }
                }
              }
            },
            items: [
              {
                id: 'port1-1',
                group: 'left'
              },
              {
                id: 'port1-2',
                group: 'left',
                attrs: {
                  circle: {
                    stroke: '#e9352f'
                  }
                }
              },
              {
                id: 'port2-1',
                group: 'right',
                attrs: {
                  circle: {
                    stroke: '#e9352f'
                  }
                }
              },
              {
                id: 'port2-2',
                group: 'right'
              },
              {
                id: 'port3-1',
                group: 'top',
                attrs: {
                  circle: {
                    stroke: '#e9352f'
                  }
                }
              },
              {
                id: 'port3-2',
                group: 'top'
              },
              {
                id: 'port4-1',
                group: 'bottom'
              },
              {
                id: 'port4-2',
                group: 'bottom',
                attrs: {
                  circle: {
                    stroke: '#e9352f'
                  }
                }
              }
            ]
          }
        })
      }
      //   console.log(this.graph)
    },
    //創(chuàng)建邊
    createEdges(type) {
      const rect = this.graph.addEdge({
        shape: 'edge', // 指定使用何種圖形拳话,默認(rèn)值為 'edge'
        source: 'node1',
        target: 'node3'
      })
    },
    //選擇節(jié)點(diǎn)先匪,邊時(shí)重置顏色
    selectReset() {
      //   this.graph.drawBackground({ color: '#fff' })
      const nodes = this.graph.getNodes()
      const edges = this.graph.getEdges()

      nodes.forEach(node => {
        node.attr('body/stroke', '#000')
      })

      edges.forEach(edge => {
        edge.attr('line/stroke', 'black')
        edge.prop('labels/0', {
          attrs: {
            body: {
              stroke: 'black'
            }
          }
        })
      })
    }
  }
}
</script>

<style lang="scss" scoped>
.toolbar {
  //   padding: 0 20px;
  box-sizing: border-box;
  height: 40px;
  background: white;
  box-shadow: 0 2px 6px #e1e1e1;
  position: fixed;
  right: 0;
  z-index: 999;
  display: flex;
  align-items: center;
  .tool {
    cursor: pointer;
    width: 40px;
    height: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
    &:hover {
      background: #f1f1f1;
    }
  }
}
.rect {
  width: 10px;
  height: 10px;
  border: 1px solid #5a5a5a;
  //   color: #e9352f;
}
</style>

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市弃衍,隨后出現(xiàn)的幾起案子呀非,更是在濱河造成了極大的恐慌,老刑警劉巖镜盯,帶你破解...
    沈念sama閱讀 206,968評(píng)論 6 482
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件慢睡,死亡現(xiàn)場(chǎng)離奇詭異负拟,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)期贫,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,601評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門蚓再,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)塌衰,“玉大人微王,你說(shuō)我怎么就攤上這事累提。” “怎么了倦踢?”我有些...
    開封第一講書人閱讀 153,220評(píng)論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)侠草。 經(jīng)常有香客問(wèn)我辱挥,道長(zhǎng),這世上最難降的妖魔是什么边涕? 我笑而不...
    開封第一講書人閱讀 55,416評(píng)論 1 279
  • 正文 為了忘掉前任晤碘,我火速辦了婚禮,結(jié)果婚禮上功蜓,老公的妹妹穿的比我還像新娘园爷。我一直安慰自己,他們只是感情好式撼,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,425評(píng)論 5 374
  • 文/花漫 我一把揭開白布童社。 她就那樣靜靜地躺著,像睡著了一般著隆。 火紅的嫁衣襯著肌膚如雪扰楼。 梳的紋絲不亂的頭發(fā)上呀癣,一...
    開封第一講書人閱讀 49,144評(píng)論 1 285
  • 那天,我揣著相機(jī)與錄音弦赖,去河邊找鬼项栏。 笑死,一個(gè)胖子當(dāng)著我的面吹牛蹬竖,可吹牛的內(nèi)容都是我干的沼沈。 我是一名探鬼主播,決...
    沈念sama閱讀 38,432評(píng)論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼币厕,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼列另!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起劈榨,我...
    開封第一講書人閱讀 37,088評(píng)論 0 261
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤访递,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后同辣,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體拷姿,經(jīng)...
    沈念sama閱讀 43,586評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,028評(píng)論 2 325
  • 正文 我和宋清朗相戀三年旱函,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了响巢。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,137評(píng)論 1 334
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡棒妨,死狀恐怖踪古,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情券腔,我是刑警寧澤伏穆,帶...
    沈念sama閱讀 33,783評(píng)論 4 324
  • 正文 年R本政府宣布,位于F島的核電站纷纫,受9級(jí)特大地震影響枕扫,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜辱魁,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,343評(píng)論 3 307
  • 文/蒙蒙 一烟瞧、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧染簇,春花似錦参滴、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,333評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春过蹂,著一層夾襖步出監(jiān)牢的瞬間十绑,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,559評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工酷勺, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留本橙,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 45,595評(píng)論 2 355
  • 正文 我出身青樓脆诉,卻偏偏與公主長(zhǎng)得像甚亭,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子击胜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,901評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容