写给自己的Topology使用

819 阅读2分钟

Topology地址

Github地址: github.com/le5le-com/t…
官方文档 :www.yuque.com/alsmile/top…

起步

  • 首先需要安装核心库(核心绘图引擎)
npm install @topology/core (注意查看topology版本更新内容)

版本更新内容

  • 初始化
<template>
    <div class="tools">
      <div v-for="(item, index) in tools" :key="index">
        <div class="title">{{ item.group }}</div>
        <div class="buttons">
          <a
            v-for="(btn, i) in item.children"
            :key="i"
            :title="btn.name"
            :draggable="btn.data"
            @dragstart="onDrag($event, btn)"
          >
            <img :src="btn.data.image" alt="" srcset="" v-if="btn.data.image" />
            <i v-else :class="`iconfont ${btn.icon}`"></i>
          </a>
        </div>
      </div>
    </div>
  <div id="topology-wrapper"></div>
  <!-- 需要在template中先创建一个topology组件的容器(父节点) -->
</template>
<script>
  // 在页面中引入Topology
  import { Topology } from '@topology/core'
  export default {
    data(){
        return {
        topologyInstance:null
      }
    },
    mounted(){
        this.initTopology()
    },
    methods:{
        initTopology(){
        /**
        * 生成一个topology实例,第一个参数为template中容器(父节点)的id,第
        * 二个参数为topology的配置项,具体参数参考官方文档
        */
        this.topologyInstance = new Topology('topology-wrapper',{})
      },
      // 拖拽事件
       onDrag (event, node) {
          event.dataTransfer.setData('Text', JSON.stringify(node.data))
       },
       // topology消息通信
       onMessage (event, data) {
              if (event === 'addNode' || event === 'addLine') {
                console.log('onMessage', event, data)
              }
        // 右侧输入框编辑状态时点击编辑区域其他元素,onMessage执行后才执行onUpdateProps方法,通过
              setTimeout让onUpdateProps先执行
          setTimeout(() => {
            switch (event) {
              case 'node':
                break
              case 'addNode':
                break
              case 'line':
              case 'addLine':
                break
              case 'multi':
                break
              case 'space':
                break
              case 'moveOut':
                break
              case 'moveNodes':
                break
              case 'resizeNodes':
                break
              case 'resize':
                break
              case 'scale':
                break
              case 'locked':
                break
            }
          }, 0)
    },
    }
  }
</script>

问题

  • 基本上的绘制 都基于canvas 因此 官方文档没有提供的api 都可以去看下canvas提供的熟悉 去源码查下
  • Node(节点) Line (线) Pen (画笔) 官方文档写了一句话 继承于画笔的成员变量这里省略(重要) 有些熟悉在节点和node找不到的去查看画笔
  • Pen 的 type 属性 画笔类型:是节点还是连线 拖拽是设置name为line 拖拽时返回的还是node节点 记得设置type : 1
  • 目前线不支持设置线性渐变但支持设置背景图。strokeImage属性 设置线的背景图
  • 修改源码设置线的线性渐变
  Line.prototype.drawBkLinearGradient = function (ctx) {
    if (!this.gradientFromColor || !this.gradientToColor) {
      return
    }
    var from = new Point(this.rect.x, this.rect.center.y)
    var to = new Point(this.rect.ex, this.rect.center.y)
    if (this.gradientAngle % 90 === 0 && this.gradientAngle % 180) {
      if (this.gradientAngle % 270) {
        from.x = this.rect.center.x
        from.y = this.rect.y
        to.x = this.rect.center.x
        to.y = this.rect.ey
      } else {
        from.x = this.rect.center.x
        from.y = this.rect.ey
        to.x = this.rect.center.x
        to.y = this.rect.y
      }
    } else if (this.gradientAngle) {
      from.rotate(this.gradientAngle, this.rect.center)
      to.rotate(this.gradientAngle, this.rect.center)
    }
    // contributor: https://github.com/sunnyguohua/topology
    var grd = ctx.createLinearGradient( from.x, from.y, to.x, to.y )
    grd.addColorStop(0, this.gradientFromColor)
    grd.addColorStop(1, this.gradientToColor)
    ctx.strokeStyle = grd
  }
  Line.prototype.drawBkRadialGradient = function (ctx) {
    if (!this.gradientFromColor || !this.gradientToColor) {
      return
    }
    var r = this.rect.width
    if (r < this.rect.height) {
      r = this.rect.height
    }
    r *= 0.5
    var grd = ctx.createRadialGradient(
      this.rect.center.x,
      this.rect.center.y,
      r * this.gradientRadius,
      this.rect.center.x,
      this.rect.center.y,
      r
    )
    grd.addColorStop(0, this.gradientFromColor)
    grd.addColorStop(1, this.gradientToColor)
    ctx.strokeStyle = grd
  }
draw 方法的时候去调用一下 也可以设置 选择线性渐变或者径向渐变
switch (this.bkType) {
  case 1:
    this.drawBkLinearGradient(ctx)
    break
  case 2:
    this.drawBkRadialGradient(ctx)
    break
}
1 - 线性渐变,配合设置下面属性:  

     gradientFromColor

     gradientToColor

     gradientAngle

2 - 径向渐变,配合设置下面属性    

     gradientFromColor       

     gradientToColor

     gradientRadius
  • 设置线的边缘lineCap 设置线宽的时候可以看到
round: 圆角
butt: 平直
square: 正方形
  • 开启网格背景
grid        boolean        是否显示背景网格

gridColor   string         背景网格颜色

gridSize    number         背景网格格子大小

rule        boolean        是否显示背景标尺

rule        Colorstring    标尺颜色
  • canvas.delete(param?: string | Pen[], force?: boolean) 方法
为空时,删除选中元素,为string时,删除id或tag的元素为数组时,删除数组中的元素
params stringstring时,删除id或tag的元素为数组时,删除数组中的元素
force  boolean  是否强制删除,否则locked下无法删除

节点添加子节点设置 rectInParent的bug

Node.prototype.scale = function (scale, center) {
        if (!center) {
            center = this.rect.center;
        }
        this['oldRect'] = this.rect.clone();
        this.rect.x = center.x - (center.x - this.rect.x) * scale;
        this.rect.y = center.y - (center.y - this.rect.y) * scale;
        this.textOffsetX *= scale;
        this.textOffsetY *= scale;
        this.z *= scale;
        this.rect.width *= scale;
        this.rect.height *= scale;
        if (this.name === 'rectangle') {
          console.log(this.rect.width, this.rect.height, scale, '执行scale');
        }
        this.rect.ex = this.rect.x + this.rect.width;
        this.rect.ey = this.rect.y + this.rect.height;
        this.lineWidth *= scale;
        if (this.imageWidth) {
            this.imageWidth *= scale;
        }
        if (this.imageHeight) {
            this.imageHeight *= scale;
        }
        this.lastImage = null;
        this.fontSize *= scale;
        this.iconSize *= scale;
        if (typeof this.paddingLeft === 'number') {
            this.paddingLeft *= scale;
        }
        if (typeof this.paddingTop === 'number') {
            this.paddingTop *= scale;
        }
        if (typeof this.paddingRight === 'number') {
            this.paddingRight *= scale;
        }
        if (typeof this.paddingBottom === 'number') {
            this.paddingBottom *= scale;
        }
        if (this.rectInParent) {
            if (typeof this.rectInParent.x === 'number') {
                this.rectInParent.x *= scale;
            }
            if (typeof this.rectInParent.y === 'number') {
                this.rectInParent.y *= scale;
            }
            if (typeof this.rectInParent.width === 'number') {
                console.log(this.rectInParent.width, scale, '老值');
                ## bug rectInParent 初始值不能 * scale 直接取node.rect  / scale 的值 重新初始化
                ## 重新 / scale 是因为 初始化时会根据rectInParent重新 * scale  
                // this.rectInParent.width *= scale; (源代码)
                this.rectInParent.width = this.rect.width / scale;
                console.log('新值', this.rectInParent.width, scale);
            }
            if (typeof this.rectInParent.height === 'number') {
                // this.rectInParent.height *= scale;
                this.rectInParent.height = this.rect.height  / scale;
            }
            if (typeof this.rectInParent.marginLeft === 'number') {
                this.rectInParent.marginLeft *= scale;
            }
            if (typeof this.rectInParent.marginTop === 'number') {
                this.rectInParent.marginTop *= scale;
            }
            if (typeof this.rectInParent.marginRight === 'number') {
                this.rectInParent.marginRight *= scale;
            }
            if (typeof this.rectInParent.marginBottom === 'number') {
                this.rectInParent.marginBottom *= scale;
            }
        }
        this.rect.calcCenter();
        if (this.animateFrames && this.animateFrames.length) {
            for (var _i = 0, _a = this.animateFrames; _i < _a.length; _i++) {
                var item = _a[_i];
                if (item.initState) {
                    if (!item.initState.scale) {
                        item.initState = new Node(item.initState);
                    }
                    item.initState.scale(scale, center);
                }
                if (item.state) {
                    if (!item.state.scale) {
                        item.state = new Node(item.state);
                    }
                    item.state.scale(scale, center);
                }
                if (item.initState && item.state) {
                    item.state.fontSize = item.initState.fontSize;
                }
            }
        }
        this.scalePoints(scale, scale);
        this.elementRendered = false;
        this.init();
        if (this.children) {
            for (var _b = 0, _c = this.children; _b < _c.length; _b++) {
                var item = _c[_b];
                item.scale(scale, center);
            }
        }
        if (this.animateReady && this.animateReady.scale) {
            this.animateReady.scale(scale, center);
        }
    };