小程序开发定制使用 auto-drawing 画一个鱼骨图

使用 auto-drawing 小程序开发定制画一个鱼骨图

基于 vue 环境

安装

yarn add  auto-drawing
  • 1

代码

fishbone.vue

<template>  <div class="container">    <div ref="mainElementRef" class="main-element" />  </div></template><script lang="ts" setup>  // 鱼骨图  import { ElMessage } from 'element-plus'  import {    onMounted,    onBeforeUnmount,    shallowReactive,    ref,    reactive  } from 'vue'  import type { ZRenderType, ZRenderGroup, ShapeCoreType } from 'auto-drawing'  import { createCanvas, createGroup, renderCanvas } from 'auto-drawing'  import type { Params } from './utils'  import { getCircle, getLine, getText } from './utils'  interface IState {    zr: Nullable<ZRenderType>    group: Nullable<ZRenderGroup>    clickGroup: Nullable<ZRenderGroup>  }  type Direction = 'left' | 'right'  const props = defineProps({    /**     * 画布宽     */    width: {      type: Number,      default: 300    },    /**     * 画布高     */    height: {      type: Number,      default: 300    }  })  const state = shallowReactive<IState>({    zr: null,    group: null,    clickGroup: null  })  const mainElementRef = ref<any>(null)  // 基本配置  const baseOptions = { x: props.width / 2, y: props.height / 2 }  onMounted(() => {    state.zr = createCanvas(      mainElementRef.value as HTMLDivElement    ) as ZRenderType    state.group = createGroup(baseOptions) as ZRenderGroup    state.clickGroup = createGroup(baseOptions) as ZRenderGroup    state.zr.setBackgroundColor('#fff')    // 原数据    const originData: string[] = [...new Array(10)].map((_, index) =>      String(index)    )    // 小程序开发定制画布两边留白    const gutter = 40    // 小程序开发定制鱼刺往后斜的距离    const angleLength = props.width / 20    // 鱼刺方向    const direction: Direction = 'left'    // 鱼刺长度    const fishboneLength = (props.height / 2 - gutter) / 2    // 主轴的基本坐标    const base = props.width / 2 - gutter    // 主轴数据    const main = originData.slice(0, 2)    // 鱼刺数据    const body = originData.slice(2)    // 主轴上面鱼刺数据    const bodyTop = body.slice(0, Math.ceil(body.length / 2))    // 主轴下面鱼刺数据    const bodyBottom = body.slice(Math.ceil(body.length / 2))    // 主抽的点数量    const pointCount = Math.max(bodyTop.length, bodyBottom.length)    // 主抽每个点之间的间距    const pointStep = (props.width - gutter * 2) / (pointCount + 1)    // 生成主轴点数据    const point = [...new Array(pointCount)].map((item, index) => {      return [-base + (index + 1) * pointStep, 0]    })    // 生成鱼刺方法    const getData = (item: string, index: number, type: string) => {      const mark = type === 'top' ? -1 : 1      const directionMark = direction === 'left' ? -1 : 1      const [baseX, baseY] = point[index]      const params: Params = {        start: point[index],        end: [baseX + angleLength * directionMark, fishboneLength * mark],        title: item,        pointType: 'basicPoint',        tag: 'start'      }      // 生成主轴基点圆图形数据      const baseCircle = getCircle(baseX, baseY, params)      const [x1, y1] = baseCircle.params.start as number[]      const [x2, y2] = baseCircle.params.end as number[]      // 生成鱼刺圆点图形数据      const bodyTop = getCircle(x2, y2, {        ...baseCircle.params,        pointType: 'endpoint',        tag: 'end'      })      // 生成鱼刺直线图形数据      const line = getLine(x1, y1, x2, y2)      // 生成鱼刺文字图形数据      const offset = mark === -1 ? -24 : 14      const text = getText(x2 - 10, y2 + offset, baseCircle.params.title)      return { type: 'group', data: [baseCircle, bodyTop, line, text], params }    }    /** 上鱼刺数据处理 */    const bodyTopData = bodyTop.map((item, index) =>      getData(item, index, 'top')    )    /** 下鱼刺数据处理 */    const bodyBottomData = bodyBottom.map((item, index) =>      getData(item, index, 'bottom')    )    /** 主抽数据处理 */    const mainCircleData = main.map((item, index) => {      const cx = index === 0 ? -base : base      const cy = 0      const title = item      const params: Params = {        title: item,        cx,        cy,        pointType: 'endpoint',        point: [cx, cy]      }      // 主抽圆点图数据      const circle = getCircle(cx, 0, params)      // 主抽文字图数据      const x = cx - 6      const y = cy - 24      const text = getText(x, y, title)      return { type: 'group', data: [circle, text], params }    })    const mainLineMap = mainCircleData.map(      item => item.params.point as number[]    )    const [[x1, y1], [x2, y2]] = mainLineMap    // 主抽直线图数据    const mainLineData = getLine(x1, y1, x2, y2)    // 主抽整体图数据    const mainData = [...mainCircleData, mainLineData]    // 所有数据    const data = [      ...mainData,      ...bodyTopData,      ...bodyBottomData    ] as ShapeCoreType[]    renderCanvas(state.zr, state.group, data, { scale: true, translate: true })    renderCanvas(state.zr, state.clickGroup, [], {      scale: true,      translate: true    })    state.zr.on('click', (e: any) => {      console.log(e?.target)      const { shape, type } = e?.target || {}      const params = (e?.target?.params as Params) || {}      if (!shape || type !== 'circle' || params.pointType !== 'endpoint') return      const data: ShapeCoreType = {        type: 'circle',        ...shape,        stroke: 'red',        fill: 'red',        zlevel: 2      }      // 移除之前的图形      state.clickGroup?.removeAll()      renderCanvas(        state.zr as ZRenderType,        state.clickGroup as ZRenderGroup,        [data],        {          scale: true,          translate: true        }      )      setTimeout(() => {        ElMessage.success('点了我:' + params.title)      })    })  })  onBeforeUnmount(() => {    // 销毁画布    state.zr && state.zr.dispose()  })  const containerCSS = reactive({    width: props.width + 'px',    height: props.height + 'px'  })</script><style lang="scss" scoped>  .container {    height: v-bind('containerCSS.height');    width: v-bind('containerCSS.width');    overflow: hidden;    padding: 0;    .main-element {      padding: 0;    }  }</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 188
  • 189
  • 190
  • 191
  • 192
  • 193
  • 194
  • 195
  • 196
  • 197
  • 198
  • 199
  • 200
  • 201
  • 202
  • 203
  • 204
  • 205
  • 206
  • 207
  • 208
  • 209
  • 210
  • 211
  • 212
  • 213
  • 214
  • 215
  • 216
  • 217
  • 218
  • 219
  • 220
  • 221
  • 222
  • 223
  • 224
  • 225
  • 226
  • 227
  • 228
  • 229

utils.ts

export type Params = {  /**   * 开始坐标   */  start?: number[]  /**   * 结束坐标   */  end?: number[]  /**   * 点坐标   */  point?: number[]  /**   * 标题   */  title: string  /**   * 圆点的类型  basicPoint:基点 不可点击   endpoint:端点 可点击   */  pointType: 'basicPoint' | 'endpoint' | ''  /**   * 圆的圆心x坐标   */  cx?: number  /**   * 圆的圆心y坐标   */  cy?: number  /**   * 当前图形标记   */  tag?: string}/** * 生成圆 * @param cx * @param cy * @param params * @returns */export const getCircle = (  cx: number,  cy: number,  params: Params = { title: '', pointType: '' }) => ({  type: 'circle',  cx: cx,  cy: cy,  r: 8,  fill: '#fa8423',  stroke: '#fa8423',  zlevel: 1,  params: { ...params }})/** * 生成直线 * @param x1 * @param y1 * @param x2 * @param y2 * @returns */export const getLine = (x1: number, y1: number, x2: number, y2: number) => ({  type: 'line',  x1,  y1,  x2,  y2,  stroke: '#000',  fill: '#000'})/** * 生成文字 * @param x * @param y * @param text * @returns */export const getText = (x: number, y: number, text: string) => ({  type: 'text',  x,  y,  text: text,  fontSize: 14,  fontWeight: 400,  stroke: '#000',  fill: '#000',  zlevel: 10})
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94

效果

官方文档

网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发