This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money

preface

Recently, the company’s projects tend to display 🥱 on large digital screens, but there is a pyramid diagram 🤔️ in the project prototype sent to me this time. Unfortunately, as we use Echarts for our charts, and Echarts does not support pyramid diagram, as a front-end developer who likes making wheels, although his own technology is not good, However, the spirit of conquering difficult problems is still there 😁, and continuous roll-in is the core competitiveness of our common front-end development 😂, so we have the idea of imitating Echarts to achieve the pyramid.

No more talking about the effect first

Project Address:(Github.com/SHDjason/Py…)

The body of the

Demo is currently based on the Vue2. X framework

The project implementation can be passed in configurations as follows: Body graph position (distance), body graph offset (offset), data sort (sort), graph color (color), data text callback (fontFormatter), ToolTip configuration (ToolTip), Data presentation Style (infoStyle), etc

Initialize canvas basic information and implement resizing

<template>
  <div id="canvas-warpper">
    <div id="canvas-tooltip"></div>
  </div>
</template>
Copy the code

Let’s create the Canvas canvas

This.canvas = document.createElement('canvas') // Add canvas node to el el.appendChild(this.canvas) This. canvasWidth = el.offsetwidth this.canvasHeight = el.offsetheight // Set the canvas element to be the same width as the parent element This.canvas. SetAttribute ('width', this.canvasWidth) // Sets the Canvas element to the same height as the parent element this.canvas. SetAttribute ('height', this.canvasHeight)Copy the code

Get the center point of the canvas for later adaptation and fixing

 this.canvasCenter = [
        Math.round((this.canvasWidth - this.integration.distance[0] * 2) / 2) + this.integration.distance[0],
        Math.round((this.canvasHeight - this.integration.distance[1] * 2) / 2) + this.integration.distance[1]
      ]
Copy the code

Monitor incoming data and calculate the ratio

This is where you write the incoming configuration of data sort

watch: { data: { immediate: true, deep: true, Handler (newValue) {let totalData = 0 newValue. ForEach (Element => {totalData = totalData + Number(element.value) }) this.dataInfo = newValue.map(item => { const accounted = (item.value / totalData) * 100 return {... item, accounted, title: this.integration.title } }) if (this.integration.sort === 'max') { this.dataInfo.sort((a, b) => { return a.value - b.value }) } else if (this.integration.sort === 'min') { this.dataInfo.sort((a, b) => { return b.value - a.value }) } } } },Copy the code

Now we can locate the four basic points of the pyramid

The position of these basic points determines the shape of the pyramid displayed at the back which can be fine-tuned to suit its own aesthetic

If (this.canvas. GetContext) {this.ctx = this.canvas. GetContext ('2d'); - this. CanvasWidth / 13, enclosing integration. The short [1]] this. Point. Left = [this. Integration. Short [0] * 1.5, this.canvasHeight - this.integration.distance[1] - this.canvasHeight / 5 ] this.point.right = [ this.canvasWidth - This. Integration. Short [0] * 1.9, this.canvasHeight - this.integration.distance[1] - this.canvasHeight / 5 ] this.point.bottom = [ this.canvasCenter[0] - this.canvasWidth / 13, this.canvasHeight - this.integration.distance[1] ] this.point.shadow = [ this.integration.distance[0] - this.canvasCenter[0] / 5, This. 1.2 this canvasHeight /. Integration. Short [1]] for (const key in this. Point) {this. Point [key] [0] = this.point[key][0] + this.integration.offset[0] this.point[key][1] = this.point[key][1] + this.integration.offset[1] } } Else {throw 'no getContext method found under Canvas'}Copy the code
  • The complete code
Let el = document.getelementById ('canvas-warpper') // Create canvas element this.canvas = document.createElement('canvas') // AppendChild (this.canvas) this.canvasWidth = el.offsetwidth this.canvasheight = el.offsetheight // SetAttribute ('width', this.canvas. SetAttribute ('width', This.canvaswidth) // Set the canvas element to the same height as the parent element this.canvas.setAttribute('height', this.canvasHeight) this.canvasCenter = [ Math.round((this.canvasWidth - this.integration.distance[0] * 2) / 2) + this.integration.distance[0], Math.round((this.canvasHeight - this.integration.distance[1] * 2) / 2) + this.integration.distance[1] ] if (this.canvas. GetContext) {this.ctx = this.canvas. GetContext ('2d') // Pyramid base position this.point. Top = [this.canvasCenter[0] - Enclosing canvasWidth / 13, this. Integration. Short. [1]] this point. Left = [this. Integration. Short [0] * 1.5, this.canvasHeight - this.integration.distance[1] - this.canvasHeight / 5 ] this.point.right = [ this.canvasWidth - This. Integration. Short [0] * 1.9, this.canvasHeight - this.integration.distance[1] - this.canvasHeight / 5 ] this.point.bottom = [ this.canvasCenter[0] - this.canvasWidth / 13, this.canvasHeight - this.integration.distance[1] ] this.point.shadow = [ this.integration.distance[0] - this.canvasCenter[0] / 5, This. 1.2 this canvasHeight /. Integration. Short [1]] for (const key in this. Point) {this. Point [key] [0] = this.point[key][0] + this.integration.offset[0] this.point[key][1] = this.point[key][1] + this.integration.offset[1] } } TopAngle.LTB = this.angle(this.point.top, this.point.left, this.point.left, this.point.left); this.point.bottom) this.topAngle.RTB = this.angle(this.point.top, this.point.right, This. The point. The bottom) / / calculation of each data point position this. CalculationPointPosition (enclosing dataInfo)},Copy the code

Calculate the Angle of each side of the pyramid

In order to fix each data point later, but alas, the math was so bad that I came up with an idea:

The fixed point range of each piece of data must be on the line of four basic points. Then I will figure out the Angle of the line of each basic point, and then I will figure out the ratio of each piece of data to the line of the current basic point after I flip the Angle to vertical.

Const a = {X: a[0], Y: const a = {X: a[0], Y: const a = {X: a[0], Y: const a = {X: a[0], Y: const a = {X: a[0], Y: const a = {X: a[0], Y: const a = {X: a[0], Y: const a[1] } const B = { X: b[0], Y: b[1] } const C = { X: c[0], Y: c[1] } const AB = Math.sqrt(Math.pow(A.X - B.X, 2) + Math.pow(A.Y - B.Y, 2)) const AC = Math.sqrt(Math.pow(A.X - C.X, 2) + Math.pow(A.Y - C.Y, 2)) const BC = Math.sqrt(Math.pow(B.X - C.X, 2) + Math.pow(B.Y - C.Y, 2)) const cosA = (Math.pow(AB, 2) + Math.pow(AC, 2) - Math.pow(BC, 2)) / (2 * AB * AC) const angleA = Math.round((Math.acos(cosA) * 180) / Math.PI) return angleA }Copy the code

Calculate the position of each data point

  • The next step is to determine the drawing scope of each piece of data

Let’s first position the left side of the pyramid and the points rotated perpendicular to the right

/** * @description: ptSrc * @param {*} ptSrc; * @param {*} ptRotationCenter * @param {*} ptRotationCenter * @param {*} ptRotationCenter Clockwise negative * @return {*} * @author: PtSrc, ptRotationCenter angle) { const a = ptRotationCenter[0] const b = ptRotationCenter[1] const x0 = ptSrc[0] const y0 = ptSrc[1] const rx = a + (x0 - a) * Math.cos((angle * Math.PI) / 180) - (y0 - b) * Math.sin((angle * Math.PI) / 180) const ry = b + (x0 - a) * Math.sin((angle * Math.PI) / 180) + (y0 - b) * Math.cos((angle * Math.PI) / 180) const point = [rx, ry] return point },Copy the code
const LP = this.rotatePoint(this.point.left, this.point.top, this.topAngle.LTB * -1)
      const RP = this.rotatePoint(this.point.right, this.point.top, this.topAngle.RTB)
Copy the code

LP is the position of the point after the LTB Angle is rotated counterclockwise by the edge of TL

RP is the position of the point at which the edge of TR is rotated clockwise by Angle RTB

  • This can determine the length of each data point on the three edges of the complete code

To calculate the length of each point, take the point on the edge of TL as an example: get the length of LP (position after counterclockwise rotation of LTB Angle), calculate the length of the data according to the proportion of total data occupied by the data, and then turn the Angle back to restore the edge to get the position information of the data on the TL edge. const vertical = [ this.point.top[0], (LP[1] – this.point.top[1]) * (item.accounted / 100) + this.point.top[1] ]

/** * @description: calculate the data point position * @param {*} val point ratio * @return {*} * @author: */ calculationPointPosition(val) {const LP = this.rotatePoint(this.point.left, this.point.top, this.topAngle.LTB * -1) const RP = this.rotatePoint(this.point.right, this.point.top, this.topAngle.RTB) let temporary = { left: [ [0, 0], [0, 0], [0, 0] ], right: [ [0, 0], [0, 0], [0, 0] ], middle: [ [0, 0], [0, 0], [0, 0] ] } const dataInfo = val.map((item, Index) => {if (index === 0) {for (const key in temporary) {if (key === 'left') {// Index) => {index) => {if (index === 0) {for (const key in temporary) {if (key === 'left')  = [ this.point.top[0], (LP[1] - this.point.top[1]) * (item.point.top [1]) + this.point.top[1]] // Temporary. this.rotatePoint(vertical, this.point.top, this.topAngle.LTB), Vertical]} else if (key === 'right') {const vertical = [this.point.top[0], (RP[1] - this.point.top[1]) * (item.point.top [1]) + this.point.top[1]] this.point.top, this.rotatePoint(vertical, this.point.top, this.topAngle.RTB * -1), Vertical]} else if (key === 'middle') {temporary. Middle = [this.point.top, [this.point.top[0], (this.point.bottom[1] - this.point.top[1]) * (item.accounted / 100) + this.point.top[1] ], [ this.point.top[0], (this.point.bottom[1] - this.point.top[1]) * (item.accounted / 100) + this.point.top[1] ] ] } } } else { for (const key Parse (json.stringify (temporary[key][2])) if (key === 'left') {// In temporary) {const vertical = json.parse (json.stringify (temporary[key][2])) vertical1 = [this.point.top[0], Vertical [1] + (LP[1] - this.point.top[1]) * (item. left = [this.point.top, three times)] this.rotatePoint(vertical1, this.point.top, this.topAngle.LTB), } else if (key === 'right') {const vertical1 = [this.point.top[0], Vertical [1] + (RP[1] - this.point.top[1]) * (item. right = [this.point.top, three times)] this.rotatePoint(vertical1, this.point.top, this.topAngle.RTB * -1), vertical1 ] } else if (key === 'middle') { temporary.middle = [ this.point.top, [this.point.top[0], (this.point.bottom[1] - this.point.top[1]) * (item.accounted / 100) + vertical[1]], [this.point.top[0], (this.point.bottom[1] - this.point.top[1]) * (item.accounted / 100) + vertical[1]] ] } } } return { ... item, temporary: JSON.parse(JSON.stringify(temporary)) } }) this.dataInfo = dataInfo },Copy the code

This gives you the point position of the length of each piece of data on each edge.

painting

Data layer drawing

We’ve got the points of the length of each piece of data on each edge. So how do you get the length of this line segment on that edge? That’s easy because the position of the first piece of data at the second point of this length is the position of the first point of the second piece of data and now we can go to the next step. Data layer drawing up

/** * @description: Data layer painting * @param {*} * @return {*} * @author: Parse (json.stringify (this.datainfo)) // data.reverse() var index = -1 this.dataInfo = this.dataInfo.map(item => { index++ if (this.integration.color.length === index) { index = 0 } return { . item, color: this.integration.color[index] } }) this.dataInfo = this.dataInfo.map((item, index) => { let drawingPoint = [] this.ctx.fillStyle = item.color this.ctx.beginPath() let point1, point2, point3, point4, point5, point6 if (index === 0) { [point1, point2, point3, point4, point5, point6] = [ item.temporary.left[0], item.temporary.left[1], item.temporary.middle[1], item.temporary.right[1], item.temporary.right[0], item.temporary.middle[0] ] } else { [point1, point2, point3, point4, point5, point6] = [ this.dataInfo[index - 1].temporary.left[1], item.temporary.left[1], item.temporary.middle[1], item.temporary.right[1], this.dataInfo[index - 1].temporary.right[1], this.dataInfo[index - 1].temporary.middle[1] ] } this.ctx.moveTo(... point1) this.ctx.lineTo(... point2) this.ctx.lineTo(... point3) this.ctx.lineTo(... point4) this.ctx.lineTo(... point5) this.ctx.lineTo(... point6) drawingPoint = [point1, point2, point3, point4, point5, point6] if (this.integration.infoStyle.stroke) { this.ctx.shadowOffsetX = 0 this.ctx.shadowOffsetY = 0 this.ctx.shadowBlur = 2 this.ctx.shadowColor = this.integration.infoStyle.strokeColor } this.ctx.fill() return { ... item, drawingPoint } }) }Copy the code

That basically completes the core of the pyramid.

But that’s not enough. To achieve Echarts’ simple functionality, you need more than just diagrams

Painting of words

Font painting is relatively simple, we have the position of each data point, the length of each data point F C points divided by 2 points of the position set as the starting point

* @param {*} * @return {*} * @author: ShuDong winter * / paintingText (lData) {this. CTX. ShadowColor = 'rgba (90,90,90,0)' const color = this. Integration. InfoStyle. Color? this.integration.infoStyle.color : '#fff' const width = this.integration.infoStyle.width ? this.integration.infoStyle.width : 0 const dotSize = this.integration.infoStyle.dotSize ? this.integration.infoStyle.dotSize : 4 const offset = this.integration.infoStyle.offset ? this.integration.infoStyle.offset : [0, 0] let text = '' this.ctx.strokeStyle = color this.ctx.fillStyle = color this.dataInfo.forEach((item, index) => { if (item.drawingPoint) { let line = [ [0, 0], [0, 0] ] this.ctx.font = `normal lighter ${ this.integration.infoStyle.size ? this.integration.infoStyle.size : 14 }px sans-serif ` this.ctx.beginPath() if (lData && index + 1 === lData.l) { line = [ [ lData.obj.drawingPoint[2][0], (lData.obj.drawingPoint[2][1] - lData.obj.drawingPoint[5][1]) / 2 + lData.obj.drawingPoint[5][1] ], [ lData.obj.drawingPoint[2][0] + lData.obj.drawingPoint[2][0] / 2 + width, (lData.obj.drawingPoint[2][1] - lData.obj.drawingPoint[5][1]) / 2 + lData.obj.drawingPoint[5][1] ] ] this.ctx.font = `normal lighter ${ this.integration.infoStyle.size ? this.integration.infoStyle.size + 2 : 16 }px sans-serif ` text = this.integration.fontFormatter(item) ! == 'default' ? this.integration.fontFormatter(item) : lData.obj.value + ' ---- ' + lData.obj.name this.ctx.setLineDash([0, 0]) this.ctx.strokeText( text, line[1][0] + offset[0], line[1][1] + (this.integration.infoStyle.size ? this.integration.infoStyle.size + 2 : 14) / 3 + offset[1] ) } else { line = [ [ item.drawingPoint[2][0], (item.drawingPoint[2][1] - item.drawingPoint[5][1]) / 2 + item.drawingPoint[5][1] ], [ item.drawingPoint[2][0] + item.drawingPoint[2][0] / 2 + width, (item.drawingPoint[2][1] - item.drawingPoint[5][1]) / 2 + item.drawingPoint[5][1] ] ] text = this.integration.fontFormatter(item) ! == 'default' ? this.integration.fontFormatter(item) : item.value + ' ----- ' + item.name this.ctx.setLineDash([0, 0]) this.ctx.strokeText( text, line[1][0] + offset[0], line[1][1] + (this.integration.infoStyle.size ? this.integration.infoStyle.size + 2 : 16) / 3 + offset[1] ) } this.ctx.setLineDash(this.integration.infoStyle.setLineDash) this.ctx.moveTo(... line[0]) this.ctx.lineTo(... line[1]) this.ctx.stroke() this.ctx.arc(... Line [0], dotSize, 0, 360, false) this.ctx.fill() else {throw 'drawingPoint attribute not found'}})},Copy the code

Highlight layer

The highlight layer is nothing more than to monitor the mouse position, determine whether the mouse position exists in the layer, which layer, and then redraw the current layer

/** * @description: mouse event registration * @param {*} * @return {*} * @author: */ eventRegistered() {const canvasWarpper = document.getelementById (' Canvas-warpper ') // Register event canvasWarpper.addEventListener('mousedown', this.doMouseDown, false) canvasWarpper.addEventListener('mouseup', this.doMouseUp, false) canvasWarpper.addEventListener('mousemove', this.doMouseMove, False) / / / / / / registered events this. Canvas. AddEventListener (mousedown, enclosing doMouseDown, false) // this.canvas.addEventListener('mouseup', this.doMouseUp, false) // this.canvas.addEventListener('mousemove', This. DoMouseMove, false)}, /** * @description: mouse move * @param {*} e * @return {*} * @author: */ / eslint-disable-next-line no-unused-vars doMouseMove(e) {const x = e.pagex const y = e.pagey this.highlightCurrentRegion(this.determineDataMouse(this.getLocation(x, y))) if (this.integration.tooltip.show) { this.showTooltip(this.determineDataMouse(this.getLocation(x, y)), This.getlocation (x, y))}}, /** * @description: {@param {*} * @return {*} * @author: DetermineDataMouse (mouseLocation) {let req = false for (let index = 0; index < this.dataInfo.length; index++) { if (this.insidePolygon(this.dataInfo[index].drawingPoint, mouseLocation)) { return (req = { l: index + 1, obj: this.dataInfo[index] }) } } return req }, /** * @description: * @param {*} lData * @return {*} * @author: */ highlightCurrentRegion(lData) {// const width = this.canvas.width; // this.canvas.width = width; this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height) if (! LData) {this.paintdatainfo () this.ctx.shadowcolor = 'rgba(90,90,90,0)' this.paintingbody () this.paintingtext () return} This.ctx. ShadowColor = 'rgba(gba(90,90,90,0)' this.ctx.fillstyle = ldata.obj.color // this.ctx.scale(1.05, 1.05) this.ctx.beginPath() this.ctx.moveto (ldata.obj. drawingPoint[0][0], this.ctx.scale(1.05, 1.05) this.ctx.beginPath() this.ctx.moveto (ldata.obj. DrawingPoint [0], lData.obj.drawingPoint[0][1]) this.ctx.lineTo(lData.obj.drawingPoint[1][0], lData.obj.drawingPoint[1][1]) this.ctx.lineTo(lData.obj.drawingPoint[2][0], lData.obj.drawingPoint[2][1]) this.ctx.lineTo(lData.obj.drawingPoint[3][0], lData.obj.drawingPoint[3][1]) this.ctx.lineTo(lData.obj.drawingPoint[4][0], lData.obj.drawingPoint[4][1]) this.ctx.lineTo(lData.obj.drawingPoint[5][0], lData.obj.drawingPoint[5][1]) this.ctx.shadowOffsetX = 0 this.ctx.shadowOffsetY = 0 this.ctx.shadowBlur = 10 This. CTX. ShadowColor = this. Integration. InfoStyle. HighlightedColor enclosing CTX. The fill () / / shadow map this. CTX. BeginPath () this.ctx.moveTo(lData.obj.drawingPoint[0][0], lData.obj.drawingPoint[0][1]) this.ctx.lineTo(lData.obj.drawingPoint[1][0], lData.obj.drawingPoint[1][1]) this.ctx.lineTo(lData.obj.drawingPoint[2][0], lData.obj.drawingPoint[2][1]) this.ctx.lineTo(lData.obj.drawingPoint[5][0], DrawingPoint [5][1]) this.ctx.fillstyle = 'rgba(120,120,120,.15)' this.ctx.fill() this.paintingtext (lData)}Copy the code

Displays the tooltip location

You can start by defining the render template for tooltip

Then render it on the code

showTooltip(lData, coordinates) { let canvasWarpper = document.getElementById('canvas-warpper') let canvasTooltip = document.getElementById('canvas-tooltip') if (lData) { canvasTooltip.style.zIndex = this.integration.tooltip.z CanvasTooltip. Style. The transition = 'opacity s cubic bezier - 0.2 (0.23, 1, 0.32, 1) 0 s, 0.2 s cubic bezier - visibility (0.23, 1, 0.32, 1) 0 s, 0.15 s' transform. Let HTML = JSON parse (JSON. Stringify (enclosing tooltipDiv)) if (this. Integration. The tooltip. The formatter) { html = this.integration.tooltip.formatter(lData) } else { const searchVal = [ ['$[title]$', lData.obj.title], ['$[name]$', lData.obj.name], ['$[val]$', lData.obj.value], ['$[color]$', lData.obj.color], ['$[fontSize]$', this.integration.tooltip.fontSize], ['$[backgroundColor]$', this.integration.tooltip.backgroundColor], ['$[fontColor]$', this.integration.tooltip.fontColor] ] searchVal.forEach(el => { html = html.replaceAll(... el) }) } canvasTooltip.innerHTML = html canvasWarpper.style.cursor = 'pointer' canvasTooltip.style.visibility = 'visible' canvasTooltip. Style. Opacity = 1 let (x, y) = coordinates x = x + y = y + 20 height / / / / canvas canvasHeight: 0, // canvasWidth // canvasWidth: 0, / / determine whether beyond framework content if (x + canvasTooltip. ClientWidth > enclosing canvasWidth) {x = x - canvasTooltip. ClientWidth - 40} the if (y + canvasTooltip.clientHeight > this.canvasHeight) { y = y - canvasTooltip.clientHeight - 40 } canvasTooltip.style.transform = `translate3d(${x}px, ${y}px, 0px)` } else { canvasWarpper.style.cursor = 'default' canvasTooltip.style.visibility = 'hidden' canvasTooltip.style.opacity = 0 } },Copy the code

And some other configuration function is also relatively simple operation, mainly is too lazy 😂, directly on the complete source code! Source notes are more complete, not very clear can comment, I see will reply!

Complete source code

<template>
  <div id="canvas-warpper">
    <div id="canvas-tooltip"></div>
  </div>
</template>

<script>
export default {
  name: 'Pyramid',
  props: {
    options: {
      type: Object,
      default: () => {
        return {
          title: '',
          // 主体离边框距离
          distance: [0, 0],
          // 主体偏移值 (x,y)
          offset: [0, 0],
          // 排序(max , min)优先
          sort: '',
          // 颜色
          color: ['#80FFA5', '#00DDFF', '#37A2FF', '#FF0087', '#FFBF00'],
          // 格式化字体输出
          fontFormatter: () => {
            return 'default'
          },
          // tooltip信息配置
          tooltip: {
            show: true, // 是否显示
            fontColor: '#000', //  字体内部颜色
            fontSize: 14, // 字体大小
            backgroundColor: '#fff', // tooltip背景
            formatter: null, // 回调方法
            z: 999999 // tooltip z-index层级
          },
          // 样式
          infoStyle: {
            stroke: false, // 是否描边
            strokeColor: '#fff', //描边颜色
            size: null, // 字体大小
            color: null, //颜色
            highlightedColor: '#fff', // 高亮颜色
            setLineDash: [0, 0], // 虚线值
            width: -10, // 设置多少 就会在基础上加上设置的值
            offset: [0, 0], // 字体x,y的偏移度
            dotSize: 4 //点大小
          }
        }
      }
    },

    // 渲染数据
    data: {
      type: Array,
      default: () => {
        return [
          { name: 'name1', value: 11 },
          { name: 'name2', value: 11 },
          { name: 'name3', value: 11 },
          { name: 'name4', value: 77 },
          { name: 'name5', value: 55 },
          { name: 'name6', value: 66 }
        ]
      }
    }
  },
  watch: {
    data: {
      immediate: true,
      deep: true,
      handler(newValue) {
        // 数据总量
        let totalData = 0
        newValue.forEach(element => {
          totalData = totalData + Number(element.value)
        })
        this.dataInfo = newValue.map(item => {
          const accounted = (item.value / totalData) * 100
          return { ...item, accounted, title: this.integration.title }
        })
        if (this.integration.sort === 'max') {
          this.dataInfo.sort((a, b) => {
            return a.value - b.value
          })
        } else if (this.integration.sort === 'min') {
          this.dataInfo.sort((a, b) => {
            return b.value - a.value
          })
        }
      }
    }
  },
  computed: {
    integration() {
      return {
        title: this.options.title ? this.options.title : '',
        // 主体离边框距离
        distance: this.options.distance ? this.options.distance : [0, 0],
        // 主体偏移值 (x,y)
        offset: this.options.offset ? this.options.offset : [0, 0],
        // 排序(max , min)优先
        sort: this.options.sort ? this.options.sort : '',
        // 颜色
        color: this.options.color ? this.options.color : ['#80FFA5', '#00DDFF', '#37A2FF', '#FF0087', '#FFBF00'],
        // 格式化字体输出
        fontFormatter: this.options.fontFormatter
          ? this.options.fontFormatter
          : () => {
              return 'default'
            },
        // tooltip显示
        tooltip: {
          show: this.options.tooltip ? (this.options.tooltip.show ? this.options.tooltip.show : true) : true, // 是否显示
          fontColor: this.options.tooltip
            ? this.options.tooltip.fontColor
              ? this.options.tooltip.fontColor
              : '#000'
            : '#000', //  字体内部颜色
          fontSize: this.options.tooltip ? (this.options.tooltip.fontSize ? this.options.tooltip.fontSize : 14) : 14, // 字体大小
          backgroundColor: this.options.tooltip
            ? this.options.tooltip.backgroundColor
              ? this.options.tooltip.backgroundColor
              : '#fff'
            : '#fff', // tooltip背景
          formatter: this.options.tooltip
            ? this.options.tooltip.formatter
              ? this.options.tooltip.formatter
              : null
            : null, // 返回方法
          z: this.options.tooltip ? (this.options.tooltip.z ? this.options.tooltip.z : 999999) : 999999 // tooltip z-index层级
        },
        // 样式
        infoStyle: {
          stroke: this.options.infoStyle
            ? this.options.infoStyle.stroke
              ? this.options.infoStyle.stroke
              : false
            : false, //是否描边
          strokeColor: this.options.infoStyle
            ? this.options.infoStyle.strokeColor
              ? this.options.infoStyle.strokeColor
              : '#fff'
            : '#fff', // 描边颜色
          size: this.options.infoStyle ? (this.options.infoStyle.size ? this.options.infoStyle.size : null) : null, // 字体大小
          color: this.options.infoStyle ? (this.options.infoStyle.color ? this.options.infoStyle.color : null) : null, //颜色
          width: this.options.infoStyle
            ? this.options.infoStyle.width || this.options.infoStyle.width !== 0
              ? this.options.infoStyle.width
              : -10
            : -10, // 设置多少 就会在基础上加上设置的值
          offset: this.options.infoStyle
            ? this.options.infoStyle.offset
              ? this.options.infoStyle.offset
              : [0, 0]
            : [0, 0], // 字体x,y的偏移度
          setLineDash: this.options.infoStyle
            ? this.options.infoStyle.setLineDash
              ? this.options.infoStyle.setLineDash
              : [0, 0]
            : [0, 0], //虚线值
          highlightedColor: this.options.infoStyle
            ? this.options.infoStyle.highlightedColor
              ? this.options.infoStyle.highlightedColor
              : '#fff'
            : '#fff', //高亮颜色
          dotSize: this.options.infoStyle
            ? this.options.infoStyle.dotSize || this.options.infoStyle.dotSize !== 0
              ? this.options.infoStyle.dotSize
              : 4
            : 4 //点大小
        }
      }
    }
  },
  data() {
    return {
      // canvas 主体
      canvas: null,
      // 图像渲染内容
      ctx: null,
      // 画布高度
      canvasHeight: 0,
      // 画布宽度
      canvasWidth: 0,
      // 画布中心点 [x,y]
      canvasCenter: [0, 0],
      // 金字塔四个点位置
      point: {
        top: [0, 0],
        left: [0, 0],
        right: [0, 0],
        bottom: [0, 0],
        shadow: [0, 0]
      },
      // 数据信息
      dataInfo: [],
      // 金字塔顶端角度信息
      topAngle: {
        LTB: 0,
        RTB: 0
      },
      // tooltip 模板
      tooltipDiv: `<div  style="margin: 0px 0 0; line-height: 1;border-color: $[backgroundColor]$ ;background-color: $[backgroundColor]$;color: $[fontColor]$;
    border-width: 1px;border-radius: 4px;padding: 10px;pointer-events: none;box-shadow: rgb(0 0 0 / 20%) 1px 2px 10px;border-style: solid;white-space: nowrap;">
        <div style="margin: 0px 0 0; line-height: 1">
          <div style="font-size: $[fontSize]$px; color: $[fontColor]$; font-weight: 400; line-height: 1"> $[title]$ </div>
          <div style="margin: 10px 0 0; line-height: 1">
            <div style="margin: 0px 0 0; line-height: 1">
              <div style="margin: 0px 0 0; line-height: 1">
                <span
                  style="
                    display: inline-block;
                    margin-right: 4px;
                    border-radius: 10px;
                    width: 10px;
                    height: 10px;
                    background-color: $[color]$;
                  "
                ></span>
                <span style="font-size: $[fontSize]$px; color: $[fontColor]$; font-weight: 400; margin-left: 2px">$[name]$</span>
                <span style="float: right; margin-left: 20px; font-size: $[fontSize]$px; color: $[fontColor]$; font-weight: 900">$[val]$</span>
                <div style="clear: both"></div>
              </div>
              <div style="clear: both"></div>
            </div>
            <div style="clear: both"></div>
          </div>
          <div style="clear: both"></div>
        </div>
        <div style="clear: both"></div>
      </div>`
    }
  },
  mounted() {
    this.init()
  },
  methods: {
    init() {
      this.initCanvasBaseInfo()
      this.paintDataInfo()
      this.paintingText()
      this.paintingBody()
      this.eventRegistered()
    },
    /**
     * @description: 初始化canvas基本信息
     * @param {*}
     * @return {*}
     * @author: 舒冬冬
     */
    initCanvasBaseInfo() {
      let el = document.getElementById('canvas-warpper')
      // 创建canvas元素
      this.canvas = document.createElement('canvas')
      // 把canvas元素节点添加在el元素下
      el.appendChild(this.canvas)
      this.canvasWidth = el.offsetWidth
      this.canvasHeight = el.offsetHeight
      // 将canvas元素设置与父元素同宽
      this.canvas.setAttribute('width', this.canvasWidth)
      // 将canvas元素设置与父元素同高
      this.canvas.setAttribute('height', this.canvasHeight)
      this.canvasCenter = [
        Math.round((this.canvasWidth - this.integration.distance[0] * 2) / 2) + this.integration.distance[0],
        Math.round((this.canvasHeight - this.integration.distance[1] * 2) / 2) + this.integration.distance[1]
      ]
      if (this.canvas.getContext) {
        this.ctx = this.canvas.getContext('2d')
        // 金字塔基本点位置
        this.point.top = [this.canvasCenter[0] - this.canvasWidth / 13, this.integration.distance[1]]
        this.point.left = [
          this.integration.distance[0] * 1.5,
          this.canvasHeight - this.integration.distance[1] - this.canvasHeight / 5
        ]
        this.point.right = [
          this.canvasWidth - this.integration.distance[0] * 1.9,
          this.canvasHeight - this.integration.distance[1] - this.canvasHeight / 5
        ]
        this.point.bottom = [
          this.canvasCenter[0] - this.canvasWidth / 13,
          this.canvasHeight - this.integration.distance[1]
        ]
        this.point.shadow = [
          this.integration.distance[0] - this.canvasCenter[0] / 5,
          this.canvasHeight / 1.2 - this.integration.distance[1]
        ]
        for (const key in this.point) {
          this.point[key][0] = this.point[key][0] + this.integration.offset[0]
          this.point[key][1] = this.point[key][1] + this.integration.offset[1]
        }
      } else {
        throw 'canvas下未找到 getContext方法'
      }
      this.topAngle.LTB = this.angle(this.point.top, this.point.left, this.point.bottom)
      this.topAngle.RTB = this.angle(this.point.top, this.point.right, this.point.bottom)
      // 计算各数据点位置
      this.calculationPointPosition(this.dataInfo)
    },
    // ======================================事件==========================================
    /**
     * @description: 鼠标事件注册
     * @param {*}
     * @return {*}
     * @author: 舒冬冬
     */
    eventRegistered() {
      const canvasWarpper = document.getElementById('canvas-warpper')
      //注册事件
      canvasWarpper.addEventListener('mousedown', this.doMouseDown, false)
      canvasWarpper.addEventListener('mouseup', this.doMouseUp, false)
      canvasWarpper.addEventListener('mousemove', this.doMouseMove, false)
      // //注册事件
      // this.canvas.addEventListener('mousedown', this.doMouseDown, false)
      // this.canvas.addEventListener('mouseup', this.doMouseUp, false)
      // this.canvas.addEventListener('mousemove', this.doMouseMove, false)
    },
    /**
     * @description: 鼠标按下
     * @param {*} e
     * @return {*}
     * @author: 舒冬冬
     */
    // eslint-disable-next-line no-unused-vars
    doMouseDown(e) {},
    /**
     * @description: 鼠标弹起
     * @param {*} e
     * @return {*}
     * @author: 舒冬冬
     */
    // eslint-disable-next-line no-unused-vars
    doMouseUp(e) {},
    /**
     * @description: 鼠标移动
     * @param {*} e
     * @return {*}
     * @author: 舒冬冬
     */
    // eslint-disable-next-line no-unused-vars
    doMouseMove(e) {
      const x = e.pageX
      const y = e.pageY
      this.highlightCurrentRegion(this.determineDataMouse(this.getLocation(x, y)))
      if (this.integration.tooltip.show) {
        this.showTooltip(this.determineDataMouse(this.getLocation(x, y)), this.getLocation(x, y))
      }
    },

    /**
     *  @description 判断一个点是否在多边形内部
     *  @param points 多边形坐标集合
     *  @param testPoint 测试点坐标
     *  @author: 舒冬冬
     *  返回true为真,false为假
     */
    insidePolygon(points, testPoint) {
      const x = testPoint[0],
        y = testPoint[1]
      let inside = false
      for (let i = 0, j = points.length - 1; i < points.length; j = i++) {
        const xi = points[i][0],
          yi = points[i][1]
        const xj = points[j][0],
          yj = points[j][1]

        const intersect = yi > y !== yj > y && x < ((xj - xi) * (y - yi)) / (yj - yi) + xi
        if (intersect) inside = !inside
      }
      return inside
    },
    /**
     * @description: 获取当前鼠标坐标
     * @param {*}
     * @return {*}
     * @author: 舒冬冬
     */
    getLocation(x, y) {
      const bbox = this.canvas.getBoundingClientRect()
      return [(x - bbox.left) * (this.canvas.width / bbox.width), (y - bbox.top) * (this.canvas.height / bbox.height)]
    },
    // ======================================算法==========================================

    /**
     * @description: 根据A点旋转指定角度后B点的坐标位置
     * @param {*} ptSrc 圆上某点(初始点);
     * @param {*} ptRotationCenter 圆心点
     * @param {*} angle 旋转角度°  -- [angle * M_PI / 180]:将角度换算为弧度
     * 【注意】angle 逆时针为正,顺时针为负
     * @return {*}
     * @author: 舒冬冬
     */
    rotatePoint(ptSrc, ptRotationCenter, angle) {
      const a = ptRotationCenter[0]
      const b = ptRotationCenter[1]
      const x0 = ptSrc[0]
      const y0 = ptSrc[1]
      const rx = a + (x0 - a) * Math.cos((angle * Math.PI) / 180) - (y0 - b) * Math.sin((angle * Math.PI) / 180)
      const ry = b + (x0 - a) * Math.sin((angle * Math.PI) / 180) + (y0 - b) * Math.cos((angle * Math.PI) / 180)
      const point = [rx, ry]
      return point
    },

    /**
     * @description: 求3点之间角度
     * @return {*} 点 a 的角度
     * @author: 舒冬冬
     */
    angle(a, b, c) {
      const A = { X: a[0], Y: a[1] }
      const B = { X: b[0], Y: b[1] }
      const C = { X: c[0], Y: c[1] }
      const AB = Math.sqrt(Math.pow(A.X - B.X, 2) + Math.pow(A.Y - B.Y, 2))
      const AC = Math.sqrt(Math.pow(A.X - C.X, 2) + Math.pow(A.Y - C.Y, 2))
      const BC = Math.sqrt(Math.pow(B.X - C.X, 2) + Math.pow(B.Y - C.Y, 2))
      const cosA = (Math.pow(AB, 2) + Math.pow(AC, 2) - Math.pow(BC, 2)) / (2 * AB * AC)
      const angleA = Math.round((Math.acos(cosA) * 180) / Math.PI)
      return angleA
    },
    /**
     * @description: 计算两点之间距离
     * @return {*}
     * @author: 舒冬冬
     */
    getDistanceBetweenTwoPoints(a, b) {
      const A = a[0] - b[0]
      const B = a[1] - b[1]
      const result = Math.sqrt(Math.pow(A, 2) + Math.pow(B, 2))
      return result
    },
    /**
     * @description: 计算数据的点位置
     * @param {*} val 点占比
     * @return {*}
     * @author: 舒冬冬
     */
    calculationPointPosition(val) {
      const LP = this.rotatePoint(this.point.left, this.point.top, this.topAngle.LTB * -1)
      const RP = this.rotatePoint(this.point.right, this.point.top, this.topAngle.RTB)
      let temporary = {
        left: [
          [0, 0],
          [0, 0],
          [0, 0]
        ],
        right: [
          [0, 0],
          [0, 0],
          [0, 0]
        ],
        middle: [
          [0, 0],
          [0, 0],
          [0, 0]
        ]
      }

      
      const dataInfo = val.map((item, index) => {
        if (index === 0) {
          for (const key in temporary) {
            if (key === 'left') {
              // 垂直后点的位置
              // 垂直后点点距离
              const vertical = [
                this.point.top[0],
                (LP[1] - this.point.top[1]) * (item.accounted / 100) + this.point.top[1]
              ]
              // 还原后点的位置
              temporary.left = [this.point.top, this.rotatePoint(vertical, this.point.top, this.topAngle.LTB), vertical]
            } else if (key === 'right') {
              // 垂直后点点距离
              const vertical = [
                this.point.top[0],
                (RP[1] - this.point.top[1]) * (item.accounted / 100) + this.point.top[1]
              ]
              // 还原后点的位置
              temporary.right = [
                this.point.top,
                this.rotatePoint(vertical, this.point.top, this.topAngle.RTB * -1),
                vertical
              ]
            } else if (key === 'middle') {
              // 垂直后点点距离
              temporary.middle = [
                this.point.top,
                [
                  this.point.top[0],
                  (this.point.bottom[1] - this.point.top[1]) * (item.accounted / 100) + this.point.top[1]
                ],
                [
                  this.point.top[0],
                  (this.point.bottom[1] - this.point.top[1]) * (item.accounted / 100) + this.point.top[1]
                ]
              ]
            }
          }
        } else {
          for (const key in temporary) {
            const vertical = JSON.parse(JSON.stringify(temporary[key][2]))
            if (key === 'left') {
              // 垂直后点点距离
              const vertical1 = [this.point.top[0], vertical[1] + (LP[1] - this.point.top[1]) * (item.accounted / 100)]
              // 还原后点的位置
              temporary.left = [
                this.point.top,
                this.rotatePoint(vertical1, this.point.top, this.topAngle.LTB),
                vertical1
              ]
            } else if (key === 'right') {
              // 垂直后点点距离
              const vertical1 = [this.point.top[0], vertical[1] + (RP[1] - this.point.top[1]) * (item.accounted / 100)]
              // 还原后点的位置
              temporary.right = [
                this.point.top,
                this.rotatePoint(vertical1, this.point.top, this.topAngle.RTB * -1),
                vertical1
              ]
            } else if (key === 'middle') {
              temporary.middle = [
                this.point.top,
                [this.point.top[0], (this.point.bottom[1] - this.point.top[1]) * (item.accounted / 100) + vertical[1]],
                [this.point.top[0], (this.point.bottom[1] - this.point.top[1]) * (item.accounted / 100) + vertical[1]]
              ]
            }
          }
        }

        return { ...item, temporary: JSON.parse(JSON.stringify(temporary)) }
      })
      this.dataInfo = dataInfo
    },
    /**
     * @description: 判断鼠标在哪层位置上
     * @param {*}
     * @return {*}
     * @author: 舒冬冬
     */
    determineDataMouse(mouseLocation) {
      let req = false
      for (let index = 0; index < this.dataInfo.length; index++) {
        if (this.insidePolygon(this.dataInfo[index].drawingPoint, mouseLocation)) {
          return (req = { l: index + 1, obj: this.dataInfo[index] })
        }
      }
      return req
    },
    // ======================================绘图==========================================
    /**
     * @description: 绘画主体
     * @param {*}
     * @return {*}
     * @author: 舒冬冬
     */
    paintingBody() {
      // 左半边金字塔阴影
      this.ctx.fillStyle = 'rgba(120,120,120,.15)'
      this.ctx.beginPath()
      this.ctx.moveTo(...this.point.top)
      this.ctx.lineTo(...this.point.bottom)
      this.ctx.lineTo(...this.point.left)
      this.ctx.fill()

      this.ctx.fill()
    },
    /**
     * @description: 数据图层绘画
     * @param {*}
     * @return {*}
     * @author: 舒冬冬
     */
    paintDataInfo() {
      var index = -1
      this.dataInfo = this.dataInfo.map(item => {
        index++
        if (this.integration.color.length === index) {
          index = 0
        }
        return { ...item, color: this.integration.color[index] }
      })
      this.dataInfo = this.dataInfo.map((item, index) => {
        let drawingPoint = []
        this.ctx.fillStyle = item.color
        this.ctx.beginPath()
        let point1, point2, point3, point4, point5, point6
        if (index === 0) {
          [point1, point2, point3, point4, point5, point6] = [
            item.temporary.left[0],
            item.temporary.left[1],
            item.temporary.middle[1],
            item.temporary.right[1],
            item.temporary.right[0],
            item.temporary.middle[0]
          ]
        } else {
          [point1, point2, point3, point4, point5, point6] = [
            this.dataInfo[index - 1].temporary.left[1],
            item.temporary.left[1],
            item.temporary.middle[1],
            item.temporary.right[1],
            this.dataInfo[index - 1].temporary.right[1],
            this.dataInfo[index - 1].temporary.middle[1]
          ]
        }
        this.ctx.moveTo(...point1)
        this.ctx.lineTo(...point2)
        this.ctx.lineTo(...point3)
        this.ctx.lineTo(...point4)
        this.ctx.lineTo(...point5)
        this.ctx.lineTo(...point6)
        drawingPoint = [point1, point2, point3, point4, point5, point6]
        if (this.integration.infoStyle.stroke) {
          this.ctx.shadowOffsetX = 0
          this.ctx.shadowOffsetY = 0
          this.ctx.shadowBlur = 2
          this.ctx.shadowColor = this.integration.infoStyle.strokeColor
        }
        this.ctx.fill()
        return { ...item, drawingPoint }
      })
    },
    /**
     * @description: 绘画字体
     * 此方法请在 paintDataInfo() 执行后使用
     * @param {*}
     * @return {*}
     * @author: 舒冬冬
     */
    paintingText(lData) {
      this.ctx.shadowColor = 'rgba(90,90,90,0)'
      const color = this.integration.infoStyle.color ? this.integration.infoStyle.color : '#fff'
      const width = this.integration.infoStyle.width ? this.integration.infoStyle.width : 0
      const dotSize = this.integration.infoStyle.dotSize ? this.integration.infoStyle.dotSize : 4
      const offset = this.integration.infoStyle.offset ? this.integration.infoStyle.offset : [0, 0]
      let text = ''
      this.ctx.strokeStyle = color
      this.ctx.fillStyle = color
      this.dataInfo.forEach((item, index) => {
        if (item.drawingPoint) {
          let line = [
            [0, 0],
            [0, 0]
          ]
          this.ctx.font = `normal lighter ${
            this.integration.infoStyle.size ? this.integration.infoStyle.size : 14
          }px sans-serif `

          this.ctx.beginPath()
          if (lData && index + 1 === lData.l) {
            line = [
              [
                lData.obj.drawingPoint[2][0],
                (lData.obj.drawingPoint[2][1] - lData.obj.drawingPoint[5][1]) / 2 + lData.obj.drawingPoint[5][1]
              ],
              [
                lData.obj.drawingPoint[2][0] + lData.obj.drawingPoint[2][0] / 2 + width,
                (lData.obj.drawingPoint[2][1] - lData.obj.drawingPoint[5][1]) / 2 + lData.obj.drawingPoint[5][1]
              ]
            ]

            this.ctx.font = `normal lighter ${
              this.integration.infoStyle.size ? this.integration.infoStyle.size + 2 : 16
            }px sans-serif `
            text =
              this.integration.fontFormatter(item) !== 'default'
                ? this.integration.fontFormatter(item)
                : lData.obj.value + ' ---- ' + lData.obj.name
            this.ctx.setLineDash([0, 0])
            this.ctx.strokeText(
              text,
              line[1][0] + offset[0],
              line[1][1] + (this.integration.infoStyle.size ? this.integration.infoStyle.size + 2 : 14) / 3 + offset[1]
            )
          } else {
            line = [
              [
                item.drawingPoint[2][0],
                (item.drawingPoint[2][1] - item.drawingPoint[5][1]) / 2 + item.drawingPoint[5][1]
              ],
              [
                item.drawingPoint[2][0] + item.drawingPoint[2][0] / 2 + width,
                (item.drawingPoint[2][1] - item.drawingPoint[5][1]) / 2 + item.drawingPoint[5][1]
              ]
            ]
            text =
              this.integration.fontFormatter(item) !== 'default'
                ? this.integration.fontFormatter(item)
                : item.value + ' ----- ' + item.name
            this.ctx.setLineDash([0, 0])
            this.ctx.strokeText(
              text,
              line[1][0] + offset[0],
              line[1][1] + (this.integration.infoStyle.size ? this.integration.infoStyle.size + 2 : 16) / 3 + offset[1]
            )
          }
          this.ctx.setLineDash(this.integration.infoStyle.setLineDash)
          this.ctx.moveTo(...line[0])
          this.ctx.lineTo(...line[1])
          this.ctx.stroke()
          this.ctx.arc(...line[0], dotSize, 0, 360, false)
          this.ctx.fill() //画实心圆
        } else {
          throw '未找到 drawingPoint 属性'
        }
      })
    },
    /**
     * @description: 显示tooltip位置
     * @param {*} lData 当前层级
     * @param {*} coordinates 鼠标位置
     * @return {*}
     * @author: 舒冬冬
     */
    showTooltip(lData, coordinates) {
      let canvasWarpper = document.getElementById('canvas-warpper')
      let canvasTooltip = document.getElementById('canvas-tooltip')
      if (lData) {
        canvasTooltip.style.zIndex = this.integration.tooltip.z
        canvasTooltip.style.transition =
          ' opacity 0.2s cubic-bezier(0.23, 1, 0.32, 1) 0s, visibility 0.2s cubic-bezier(0.23, 1, 0.32, 1) 0s,transform 0.15s'
        let html = JSON.parse(JSON.stringify(this.tooltipDiv))
        if (this.integration.tooltip.formatter) {
          html = this.integration.tooltip.formatter(lData)
        } else {
          const searchVal = [
            ['$[title]$', lData.obj.title],
            ['$[name]$', lData.obj.name],
            ['$[val]$', lData.obj.value],
            ['$[color]$', lData.obj.color],
            ['$[fontSize]$', this.integration.tooltip.fontSize],
            ['$[backgroundColor]$', this.integration.tooltip.backgroundColor],
            ['$[fontColor]$', this.integration.tooltip.fontColor]
          ]
          searchVal.forEach(el => {
            html = html.replaceAll(...el)
          })
        }
        canvasTooltip.innerHTML = html
        canvasWarpper.style.cursor = 'pointer'
        canvasTooltip.style.visibility = 'visible'
        canvasTooltip.style.opacity = 1
        let [x, y] = coordinates
        x = x + 20
        y = y + 20
        // 画布高度
        // canvasHeight: 0,
        // 画布宽度
        // canvasWidth: 0,
        // 判断是否超出框架内容
        if (x + canvasTooltip.clientWidth > this.canvasWidth) {
          x = x - canvasTooltip.clientWidth - 40
        }
        if (y + canvasTooltip.clientHeight > this.canvasHeight) {
          y = y - canvasTooltip.clientHeight - 40
        }
        canvasTooltip.style.transform = `translate3d(${x}px, ${y}px, 0px)`
      } else {
        canvasWarpper.style.cursor = 'default'
        canvasTooltip.style.visibility = 'hidden'
        canvasTooltip.style.opacity = 0
      }
    },
    /**
     * @description: 高亮某一层级
     * @param {*} lData 层级数据
     * @return {*}
     * @author: 舒冬冬
     */
    highlightCurrentRegion(lData) {

      this.ctx.clearRect(0, 0, this.canvas.width, this.canvas.height)
      if (!lData) {
        this.paintDataInfo()
        this.ctx.shadowColor = 'rgba(90,90,90,0)'
        this.paintingBody()
        this.paintingText()
        return
      }
      this.paintDataInfo()
      this.ctx.shadowColor = 'rgba(90,90,90,0)'
      this.paintingBody()
      this.ctx.fillStyle = lData.obj.color
      //  this.ctx.scale(1.05, 1.05)
      this.ctx.beginPath()
      this.ctx.moveTo(lData.obj.drawingPoint[0][0], lData.obj.drawingPoint[0][1])
      this.ctx.lineTo(lData.obj.drawingPoint[1][0], lData.obj.drawingPoint[1][1])
      this.ctx.lineTo(lData.obj.drawingPoint[2][0], lData.obj.drawingPoint[2][1])
      this.ctx.lineTo(lData.obj.drawingPoint[3][0], lData.obj.drawingPoint[3][1])
      this.ctx.lineTo(lData.obj.drawingPoint[4][0], lData.obj.drawingPoint[4][1])
      this.ctx.lineTo(lData.obj.drawingPoint[5][0], lData.obj.drawingPoint[5][1])
      this.ctx.shadowOffsetX = 0
      this.ctx.shadowOffsetY = 0
      this.ctx.shadowBlur = 10
      this.ctx.shadowColor = this.integration.infoStyle.highlightedColor
      this.ctx.fill()
      // 阴影绘制
      this.ctx.beginPath()
      this.ctx.moveTo(lData.obj.drawingPoint[0][0], lData.obj.drawingPoint[0][1])
      this.ctx.lineTo(lData.obj.drawingPoint[1][0], lData.obj.drawingPoint[1][1])
      this.ctx.lineTo(lData.obj.drawingPoint[2][0], lData.obj.drawingPoint[2][1])
      this.ctx.lineTo(lData.obj.drawingPoint[5][0], lData.obj.drawingPoint[5][1])
      this.ctx.fillStyle = 'rgba(120,120,120,.15)'
      this.ctx.fill()
      this.paintingText(lData)
    }
  }
}
</script>


Copy the code

At the end

😂 Chinese is not good, the expression ability is poor, this is my first time to send technology blog what do not understand welcome comment discussion, also welcome the big guy’s correction.

Mutual encouragement!!

Project address :(github.com/SHDjason/Py…)