需求:在canvas画布上绘制多个圆形,并添加连接线,图形移动,线自动跟随移动。类似于拓扑关系。

查看fabic文档,Stickman火柴人demo完美适应。官网具体代码如下:

(function() {
  var canvas = this.__canvas = new fabric.Canvas('c', { selection: false });
  fabric.Object.prototype.originX = fabric.Object.prototype.originY = 'center';

  function makeCircle(left, top, line1, line2, line3, line4) {
    var c = new fabric.Circle({
      left: left,
      top: top,
      strokeWidth: 5,
      radius: 12,
      fill: '#fff',
      stroke: '#666'
    });
    c.hasControls = c.hasBorders = false;

    c.line1 = line1;
    c.line2 = line2;
    c.line3 = line3;
    c.line4 = line4;

    return c;
  }

  function makeLine(coords) {
    return new fabric.Line(coords, {
      fill: 'red',
      stroke: 'red',
      strokeWidth: 5,
      selectable: false,
      evented: false,
    });
  }

  var line = makeLine([ 250, 125, 250, 175 ]),
      line2 = makeLine([ 250, 175, 250, 250 ]),
      line3 = makeLine([ 250, 250, 300, 350]),
      line4 = makeLine([ 250, 250, 200, 350]),
      line5 = makeLine([ 250, 175, 175, 225 ]),
      line6 = makeLine([ 250, 175, 325, 225 ]);

  canvas.add(line, line2, line3, line4, line5, line6);

  canvas.add(
    makeCircle(line.get('x1'), line.get('y1'), null, line),
    makeCircle(line.get('x2'), line.get('y2'), line, line2, line5, line6),
    makeCircle(line2.get('x2'), line2.get('y2'), line2, line3, line4),
    makeCircle(line3.get('x2'), line3.get('y2'), line3),
    makeCircle(line4.get('x2'), line4.get('y2'), line4),
    makeCircle(line5.get('x2'), line5.get('y2'), line5),
    makeCircle(line6.get('x2'), line6.get('y2'), line6)
  );

  canvas.on('object:moving', function(e) {
    var p = e.target;
    p.line1 && p.line1.set({ 'x2': p.left, 'y2': p.top });
    p.line2 && p.line2.set({ 'x1': p.left, 'y1': p.top });
    p.line3 && p.line3.set({ 'x1': p.left, 'y1': p.top });
    p.line4 && p.line4.set({ 'x1': p.left, 'y1': p.top });
    canvas.renderAll();
  });
})();

我在vue项目中使用,变形如下:

<template>
  <div class="dashboard-container" @contextmenu.prevent>
    <canvas id="editorCanvas" ref="canvas" style="margin-top: 10px;" />
  </div>
</template>

<script>
import { mapGetters } from 'vuex'
import { getMapList } from '@/api/table'
// import fabric from 'fabric'
let canvas = ''
export default {
  name: 'Dashboard',
  computed: {
    ...mapGetters([
      'name'
    ])
  },
  data () {
    return {
      width: window.innerWidth,
      height: window.innerHeight,
      activeEl: {}// 获取当前点击元素
    }
  },
  mounted () {
    canvas = new fabric.Canvas('editorCanvas', {
      width: this.width,
      height: this.height,
      selection: false,
      backgroundColor: '#ffffff',
      transparentCorners: false,
      fireRightClick: true, // 启用右键,button的数字为3
      stopContextMenu: true // 禁止默认右键菜单
    })

    var line = this.makeLine([250, 125, 250, 175])
    var line2 = this.makeLine([250, 175, 250, 250])
    var line3 = this.makeLine([250, 250, 300, 350])
    var line4 = this.makeLine([250, 250, 200, 350])
    var line5 = this.makeLine([250, 175, 175, 225])
    var line6 = this.makeLine([250, 175, 325, 225])

    canvas.add(line, line2, line3, line4, line5, line6)

    canvas.add(
      this.makeCircle(line.get('x1'), line.get('y1'), null, line),
      this.makeCircle(line.get('x2'), line.get('y2'), line, line2, line5, line6),
      this.makeCircle(line2.get('x2'), line2.get('y2'), line2, line3, line4),
      this.makeCircle(line3.get('x2'), line3.get('y2'), line3),
      this.makeCircle(line4.get('x2'), line4.get('y2'), line4),
      this.makeCircle(line5.get('x2'), line5.get('y2'), line5),
      this.makeCircle(line6.get('x2'), line6.get('y2'), line6)
    )

    canvas.on('object:moving', function (e) {
      var p = e.target
      p.line1 && p.line1.set({ 'x2': p.left, 'y2': p.top })
      p.line2 && p.line2.set({ 'x1': p.left, 'y1': p.top })
      p.line3 && p.line3.set({ 'x1': p.left, 'y1': p.top })
      p.line4 && p.line4.set({ 'x1': p.left, 'y1': p.top })
      canvas.renderAll()
    })
  },
  methods: {
    makeCircle (left, top, line1, line2, line3, line4) {
      var c = new fabric.Circle({
        left: left,
        top: top,
        strokeWidth: 5,
        radius: 12,
        fill: '#fff',
        stroke: '#666',
        originX: 'center',
        originY: 'center'
      })
      c.hasControls = c.hasBorders = false

      c.line1 = line1
      c.line2 = line2
      c.line3 = line3
      c.line4 = line4

      return c
    },
    makeLine (coords) {
      return new fabric.Line(coords, {
        fill: 'red',
        stroke: 'red',
        strokeWidth: 5,
        selectable: false,
        evented: false, originX: 'center',
        originY: 'center'
      })
    }

  }
}
</script>

<style lang="scss" scoped>

</style>

效果如图(随便拖动一点移动,关联线自动跟随):

分析:最核心代码为set({ 'x2': p.left, 'y2': p.top }),通过为canvas添加元素移动判定canvas.on('object:moving',根据移动重新设定线段的绘制坐标。

变形使用:(仅为逻辑,请自行编写判定逻辑)

      canvas.on('object:moving', function (e) {
        var p = e.target
        if (....) { /* 判定我移动的元素是点 ,即是移动会改变关联的元素*/
          canvas.forEachObject(function (object) {
           if(....){ /* 定位到关联的线 */
                object.set({ 'x1': p.left, 'y1': p.top })
            }
          })
        }
      })

Logo

助力广东及东莞地区开发者,代码托管、在线学习与竞赛、技术交流与分享、资源共享、职业发展,成为松山湖开发者首选的工作与学习平台

更多推荐