slqmock数据库工具是如何实现表ER图导出

文章讲述了在接到老师要求在论文中加入子ER图后,作者利用编程技能,通过canvas来绘制ER图的过程。作者通过计算角度和线长,确保椭圆间不重叠,并编写了一个工具,能从数据库表结构自动生成ER图,最后还分享了去除canvas多余空白的技巧和项目源码链接。

写论文交初稿后,老师说论文中要有子ER图,我就画了个系统ER图,我的项目中表还是不少的,20多张,这要画到什么时候啊!正好之前写的数据库填充小工具,有表导出功能,借此实现表ER图导出功能。

用canvas画ER图

表ER图是由一个矩形和多个椭圆组成,首先给这些形状确定大小,其次就要解决椭圆之间不重叠的问题。这就要涉及到高中知识了三角函数的问题了,为此我还百度回顾下,都快忘完了。
在canvas中画图就是要确定每个形状的坐标。
有个容易出错的地方,Math.sin、cos、tan中的是弧长,不是度数
我们将椭圆看成已长径为半径的圆。如何保证圆不重叠,就要看图吧
在这里插入图片描述

计算矩形与椭圆中心线长

 const attSize = table.table_attributes.length
  //属性间隔度数
  const degree = 360 / attSize
  //计算线长
  let lineLength = Math.abs(shapeWidth/1.5 / Math.tan(((2 * Math.PI) / 360) *(degree / 2)))

开始画图

一个原则,画线,圆椭圆,画文字,画完后,画矩形,上代码

 const centreLength = lineLength + shapeWidth

  const erEList: erElement[] = []

  let offsetDegree = 0,
    temDeg: number,
    centerX: number,
    centerY: number
  table.table_attributes.forEach((item) => {
    temDeg = ((2 * Math.PI) / 360) * offsetDegree
    ;(centerX = centreLength + Math.cos(temDeg) * lineLength),
      (centerY = centreLength + Math.sin(temDeg) * lineLength * -1),
      erEList.push({
        text: item.Comment != '' ? item.Comment : item.Field,
        shape: 'oval',
        centerX,
        centerY,
        x: centerX - shapeWidth / 2,
        y: centerY - shapeHeight / 2
      })
    offsetDegree += degree
  })
  erEList.push({
    text:
      table.table_info.table_comment != ''
        ? table.table_info.table_comment
        : table.table_info.table_name,
    shape: 'rectangle',
    centerX: centreLength,
    centerY: centreLength,
    x: centreLength - shapeWidth / 2,
    y: centreLength - shapeHeight / 2
  })
  console.log(erEList)
  //计算椭圆位置
  // const canvas = document.createElement('canvas')
  canvas.height = centreLength * 2
  canvas.width = centreLength * 2

  // canvas.style.width=centreLength*2+'px'
  // canvas.style.height=centreLength*2+'px'
  const ctx = canvas.getContext('2d')!

  ctx.clearRect(0, 0, canvas.width, canvas.height)

  erEList.forEach((shape) => {
    //画线
    ctx.beginPath()
    ctx.lineWidth = lineWidth
    ctx.strokeStyle = lineColor
    ctx.moveTo(shape.centerX, shape.centerY)
    ctx.lineTo(centreLength, centreLength)
    ctx.stroke()
    if (shape.shape == 'oval') {
      drawEllipse(ctx, shape.centerX, shape.centerY, shapeWidth, shapeHeight)
    } else {
      ctx.beginPath() //开始新的路径
      ctx.rect(shape.x, shape.y, shapeWidth, shapeHeight) //生成矩形路径,起点坐标(前两个参数)矩形大小(后两个参数)
      ctx.fillStyle = bgColor
      ctx.strokeStyle = lineColor
      ctx.fill()
      ctx.stroke() //绘制路径
    }
    // 画文字
    drawText(ctx, shape.text, shape.x, shape.y)
  })
  return getMinCanvas(canvas, ctx)

去除多余空白

const getMinCanvas = (
  canvas: HTMLCanvasElement,
  context: CanvasRenderingContext2D
): HTMLCanvasElement => {
  let imgData = context.getImageData(0, 0, canvas.width, canvas.height)
  const data = imgData.data
  //图形边界位置
  let left = canvas.width
  let right = 0
  let top = canvas.height
  let bottom = 0
  for (let i = 0; i < data.length; i += 4) {
  	//计算该像素在canvas的位置
    const x = (i / 4) % canvas.width
    const y = Math.floor(i / (4 * canvas.width))
    //获取颜色透明度
    const alpha = data[i + 3]
    //确定该像素是否可见
    if (alpha > 0) {
      //计算可视边界
      left = Math.min(left, x)
      right = Math.max(right, x)
      top = Math.min(top, y)
      bottom = Math.max(bottom, y)
    }
  }
  const width = right - left
  const height = bottom - top
  const canvas1 = document.createElement('canvas')
  canvas1.width = width
  canvas1.height = height
  context = canvas1.getContext('2d')!
  //填充白色背景
  context.beginPath()
  context.fillStyle = bgColor
  context.fillRect(0, 0, canvas.width, canvas.height)
  context.stroke()
  //裁剪
  context.drawImage(canvas, left, top, width, height, 0, 0, width, height)
  return canvas1
}

这里使用了上面提到的方法去除多余空白,将实际内容绘制到了一个新的canvas中,并将其替换原来的canvas。

项目地址:

gitee:https://gitee.com/baymaxsjj/sqlmock

github:https://github.com/baymaxsjj/sqlmock
本文代码实现在:src\renderer\src\db\doc-export\er-export.ts

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值