1fb726d48Sopenharmony_ci/*
2fb726d48Sopenharmony_ci * Copyright (C) 2023 Huawei Device Co., Ltd.
3fb726d48Sopenharmony_ci * Licensed under the Apache License, Version 2.0 (the "License");
4fb726d48Sopenharmony_ci * you may not use this file except in compliance with the License.
5fb726d48Sopenharmony_ci * You may obtain a copy of the License at
6fb726d48Sopenharmony_ci *
7fb726d48Sopenharmony_ci *     http://www.apache.org/licenses/LICENSE-2.0
8fb726d48Sopenharmony_ci *
9fb726d48Sopenharmony_ci * Unless required by applicable law or agreed to in writing, software
10fb726d48Sopenharmony_ci * distributed under the License is distributed on an "AS IS" BASIS,
11fb726d48Sopenharmony_ci * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12fb726d48Sopenharmony_ci * See the License for the specific language governing permissions and
13fb726d48Sopenharmony_ci * limitations under the License.
14fb726d48Sopenharmony_ci */
15fb726d48Sopenharmony_ci
16fb726d48Sopenharmony_ciimport { resizeCanvas } from '../helper';
17fb726d48Sopenharmony_ciimport { BaseElement, element } from '../../BaseElement';
18fb726d48Sopenharmony_ciimport { LitChartScatterConfig } from './LitChartScatterConfig';
19fb726d48Sopenharmony_ci
20fb726d48Sopenharmony_ci@element('lit-chart-scatter')
21fb726d48Sopenharmony_ciexport class LitChartScatter extends BaseElement {
22fb726d48Sopenharmony_ci  private scatterTipEL: HTMLDivElement | null | undefined;
23fb726d48Sopenharmony_ci  private labelsEL: HTMLDivElement | null | undefined;
24fb726d48Sopenharmony_ci  canvas: HTMLCanvasElement | undefined | null;
25fb726d48Sopenharmony_ci  canvas2: HTMLCanvasElement | undefined | null;
26fb726d48Sopenharmony_ci  ctx: CanvasRenderingContext2D | undefined | null;
27fb726d48Sopenharmony_ci  originX: number = 0;
28fb726d48Sopenharmony_ci  finalX: number = 0;
29fb726d48Sopenharmony_ci  originY: number = 0;
30fb726d48Sopenharmony_ci  finalY: number = 0;
31fb726d48Sopenharmony_ci  options: LitChartScatterConfig | undefined;
32fb726d48Sopenharmony_ci
33fb726d48Sopenharmony_ci  set config(LitChartScatterConfig: LitChartScatterConfig) {
34fb726d48Sopenharmony_ci    this.options = LitChartScatterConfig;
35fb726d48Sopenharmony_ci    this.init();
36fb726d48Sopenharmony_ci  }
37fb726d48Sopenharmony_ci  init(): void {
38fb726d48Sopenharmony_ci    if (this.options) {
39fb726d48Sopenharmony_ci      // 清楚上一次绘制的数据
40fb726d48Sopenharmony_ci      this.ctx?.clearRect(0, 0, this.clientWidth, this.clientHeight);
41fb726d48Sopenharmony_ci      this.drawBackground();
42fb726d48Sopenharmony_ci      this.drawScatterChart(this.options);
43fb726d48Sopenharmony_ci      //使用off-screen-canvas保存绘制的像素点
44fb726d48Sopenharmony_ci      this.setOffScreen();
45fb726d48Sopenharmony_ci      this.labelsEL!.innerText = this.options.title;
46fb726d48Sopenharmony_ci    }
47fb726d48Sopenharmony_ci  }
48fb726d48Sopenharmony_ci  // 使用离屏技术保存绘制的像素点
49fb726d48Sopenharmony_ci  setOffScreen(): void {
50fb726d48Sopenharmony_ci    this.canvas2 = document.createElement('canvas');
51fb726d48Sopenharmony_ci    this.canvas2.height = this.clientHeight;
52fb726d48Sopenharmony_ci    this.canvas2.width = this.clientWidth;
53fb726d48Sopenharmony_ci    let context2: CanvasRenderingContext2D | null = this.canvas2.getContext('2d');
54fb726d48Sopenharmony_ci    if (this.canvas?.width !== 0 && this.canvas?.height !== 0) {
55fb726d48Sopenharmony_ci      context2!.drawImage(this.canvas!, 0, 0);
56fb726d48Sopenharmony_ci    }
57fb726d48Sopenharmony_ci  }
58fb726d48Sopenharmony_ci  /*绘制渐变色背景*/
59fb726d48Sopenharmony_ci  drawBackground(): void {
60fb726d48Sopenharmony_ci    let w: number = this.clientWidth;
61fb726d48Sopenharmony_ci    let h: number = this.clientHeight;
62fb726d48Sopenharmony_ci    let color: CanvasGradient = this.ctx?.createRadialGradient(w / 2, h / 2, 0.2 * w, w / 2, h / 2, 0.5 * w)!;
63fb726d48Sopenharmony_ci    color?.addColorStop(0, '#eaeaea');
64fb726d48Sopenharmony_ci    color?.addColorStop(1, '#ccc');
65fb726d48Sopenharmony_ci    if (this.options) {
66fb726d48Sopenharmony_ci      this.options!.globalGradient = color;
67fb726d48Sopenharmony_ci    }
68fb726d48Sopenharmony_ci    this.ctx?.save();
69fb726d48Sopenharmony_ci    this.ctx!.fillStyle = color;
70fb726d48Sopenharmony_ci    this.ctx?.fillRect(0, 0, w, h);
71fb726d48Sopenharmony_ci    this.ctx?.restore();
72fb726d48Sopenharmony_ci  }
73fb726d48Sopenharmony_ci  /**
74fb726d48Sopenharmony_ci   * 绘制散点图
75fb726d48Sopenharmony_ci   */
76fb726d48Sopenharmony_ci  drawScatterChart(options: LitChartScatterConfig): void {
77fb726d48Sopenharmony_ci    this.drawAxis(options); //绘制坐标轴
78fb726d48Sopenharmony_ci    this.drawYLabels(options); //绘制y轴坐标
79fb726d48Sopenharmony_ci    this.drawXLabels(options); //绘制x轴坐标
80fb726d48Sopenharmony_ci    let drawload: boolean = false;
81fb726d48Sopenharmony_ci    if (options) {
82fb726d48Sopenharmony_ci      drawload = options.drawload;
83fb726d48Sopenharmony_ci    }
84fb726d48Sopenharmony_ci    if (drawload) {
85fb726d48Sopenharmony_ci      let load: Array<number> = [];
86fb726d48Sopenharmony_ci      if (options) {
87fb726d48Sopenharmony_ci        load = options.load;
88fb726d48Sopenharmony_ci        this.drawBalanceLine(load); //绘制均衡线
89fb726d48Sopenharmony_ci        this.drawLoadLine(load); //绘制最大负载线
90fb726d48Sopenharmony_ci      }
91fb726d48Sopenharmony_ci    }
92fb726d48Sopenharmony_ci    this.drawData(options); //绘制散点图
93fb726d48Sopenharmony_ci  }
94fb726d48Sopenharmony_ci  /**
95fb726d48Sopenharmony_ci   * 绘制坐标轴
96fb726d48Sopenharmony_ci   */
97fb726d48Sopenharmony_ci  drawAxis(options: LitChartScatterConfig): void {
98fb726d48Sopenharmony_ci    let text: Array<string> = new Array();
99fb726d48Sopenharmony_ci    if (options) {
100fb726d48Sopenharmony_ci      text = options.axisLabel;
101fb726d48Sopenharmony_ci    }
102fb726d48Sopenharmony_ci    this.ctx!.font = '10px KATTI';
103fb726d48Sopenharmony_ci    this.ctx!.fillStyle = '#000000';
104fb726d48Sopenharmony_ci    this.ctx!.strokeStyle = '#000000';
105fb726d48Sopenharmony_ci    // 画x轴
106fb726d48Sopenharmony_ci    this.ctx?.beginPath();
107fb726d48Sopenharmony_ci    this.ctx?.moveTo(this.originX, this.originY);
108fb726d48Sopenharmony_ci    this.ctx?.lineTo(this.finalX, this.originY);
109fb726d48Sopenharmony_ci    this.ctx?.fillText(text[0], this.finalX, this.originY);
110fb726d48Sopenharmony_ci    this.ctx?.stroke();
111fb726d48Sopenharmony_ci    // 画Y轴
112fb726d48Sopenharmony_ci    this.ctx?.beginPath();
113fb726d48Sopenharmony_ci    this.ctx?.moveTo(this.originX, this.originY);
114fb726d48Sopenharmony_ci    this.ctx?.lineTo(this.originX, this.finalY);
115fb726d48Sopenharmony_ci    this.ctx?.fillText(text[1], this.originX - 20, this.finalY - 10);
116fb726d48Sopenharmony_ci    this.ctx?.stroke();
117fb726d48Sopenharmony_ci  }
118fb726d48Sopenharmony_ci  /**
119fb726d48Sopenharmony_ci   * 绘制y轴坐标
120fb726d48Sopenharmony_ci   */
121fb726d48Sopenharmony_ci  drawYLabels(options: LitChartScatterConfig): void {
122fb726d48Sopenharmony_ci    const AXAIS_DELTA: number = 5;
123fb726d48Sopenharmony_ci    const QUYU: number = 100;
124fb726d48Sopenharmony_ci    // 添加原点刻度
125fb726d48Sopenharmony_ci    this.ctx!.font = '12px KATTI';
126fb726d48Sopenharmony_ci    this.ctx!.fillStyle = '#000000';
127fb726d48Sopenharmony_ci    this.ctx!.strokeStyle = '#000000';
128fb726d48Sopenharmony_ci    this.ctx?.fillText('0', this.originX - AXAIS_DELTA, this.originY + AXAIS_DELTA * 2);
129fb726d48Sopenharmony_ci    let yAxis: Array<number> = [];
130fb726d48Sopenharmony_ci    if (options) {
131fb726d48Sopenharmony_ci      yAxis = options.yAxisLabel;
132fb726d48Sopenharmony_ci    }
133fb726d48Sopenharmony_ci    // 画Y轴坐标尺
134fb726d48Sopenharmony_ci    for (let i = 0; i < yAxis.length; i++) {
135fb726d48Sopenharmony_ci      let length1: number =
136fb726d48Sopenharmony_ci        (this.originY - this.finalY - ((this.originY - this.finalY) % QUYU)) * (yAxis[i] / yAxis[yAxis.length - 1]);
137fb726d48Sopenharmony_ci      let length2: number = this.originY - length1;
138fb726d48Sopenharmony_ci      let text: string = yAxis[i].toString();
139fb726d48Sopenharmony_ci      let x: number = this.originX - this.ctx?.measureText(text).width! - AXAIS_DELTA;
140fb726d48Sopenharmony_ci      this.ctx?.beginPath();
141fb726d48Sopenharmony_ci      this.ctx?.moveTo(this.originX, length2);
142fb726d48Sopenharmony_ci      this.ctx?.lineTo(this.originX + AXAIS_DELTA, length2);
143fb726d48Sopenharmony_ci      this.ctx?.fillText(text, x, length2 + AXAIS_DELTA);
144fb726d48Sopenharmony_ci      this.ctx?.stroke();
145fb726d48Sopenharmony_ci    }
146fb726d48Sopenharmony_ci  }
147fb726d48Sopenharmony_ci  /**
148fb726d48Sopenharmony_ci   * 绘制x轴坐标
149fb726d48Sopenharmony_ci   */
150fb726d48Sopenharmony_ci  drawXLabels(options: LitChartScatterConfig): void {
151fb726d48Sopenharmony_ci    // 画X轴坐标尺
152fb726d48Sopenharmony_ci    this.ctx!.fillStyle = '#000000';
153fb726d48Sopenharmony_ci    this.ctx!.strokeStyle = '#000000';
154fb726d48Sopenharmony_ci    const QUYU: number = 100;
155fb726d48Sopenharmony_ci    const DELTA: number = 5;
156fb726d48Sopenharmony_ci    let xAxis: Array<number> = [];
157fb726d48Sopenharmony_ci    if (options) {
158fb726d48Sopenharmony_ci      xAxis = options.xAxisLabel;
159fb726d48Sopenharmony_ci    }
160fb726d48Sopenharmony_ci    for (let i = 0; i < xAxis.length; i++) {
161fb726d48Sopenharmony_ci      let length3: number =
162fb726d48Sopenharmony_ci        (this.finalX - this.originX - ((this.finalX - this.originX) % QUYU)) * (xAxis[i] / xAxis[xAxis.length - 1]);
163fb726d48Sopenharmony_ci      let length4: number = this.originX + length3;
164fb726d48Sopenharmony_ci      this.ctx?.beginPath();
165fb726d48Sopenharmony_ci      this.ctx?.moveTo(length4, this.originY);
166fb726d48Sopenharmony_ci      this.ctx?.lineTo(length4, this.originY - DELTA);
167fb726d48Sopenharmony_ci      this.ctx?.fillText(xAxis[i].toString(), length4 - DELTA * 3, this.originY + DELTA * 2);
168fb726d48Sopenharmony_ci      this.ctx?.stroke();
169fb726d48Sopenharmony_ci    }
170fb726d48Sopenharmony_ci  }
171fb726d48Sopenharmony_ci
172fb726d48Sopenharmony_ci  /**
173fb726d48Sopenharmony_ci   * 绘制数据
174fb726d48Sopenharmony_ci   */
175fb726d48Sopenharmony_ci  drawData(options: LitChartScatterConfig): void {
176fb726d48Sopenharmony_ci    let data: Array<Array<Array<number>>> = [];
177fb726d48Sopenharmony_ci    let yAxis: Array<number> = [];
178fb726d48Sopenharmony_ci    let xAxis: Array<number> = [];
179fb726d48Sopenharmony_ci    let colorPool: Array<string> = new Array();
180fb726d48Sopenharmony_ci    let colorPoolText: Array<string> = new Array();
181fb726d48Sopenharmony_ci    let rectY: number = this.clientHeight * 0.05;
182fb726d48Sopenharmony_ci    const QUYU: number = 100;
183fb726d48Sopenharmony_ci    const WIDTH_DELTA: number = 70;
184fb726d48Sopenharmony_ci    if (options) {
185fb726d48Sopenharmony_ci      data = options.data;
186fb726d48Sopenharmony_ci      yAxis = options.yAxisLabel;
187fb726d48Sopenharmony_ci      xAxis = options.xAxisLabel;
188fb726d48Sopenharmony_ci      colorPool = options.colorPool();
189fb726d48Sopenharmony_ci      colorPoolText = options.colorPoolText();
190fb726d48Sopenharmony_ci      options.paintingData = [];
191fb726d48Sopenharmony_ci    }
192fb726d48Sopenharmony_ci    let xLength: number = this.finalX - this.originX - ((this.finalX - this.originX) % QUYU);
193fb726d48Sopenharmony_ci    let yLength: number = this.originY - this.finalY - ((this.originY - this.finalY) % QUYU);
194fb726d48Sopenharmony_ci    for (let i = 0; i < data.length; i++) {
195fb726d48Sopenharmony_ci      for (let j = 0; j < data[i].length; j++) {
196fb726d48Sopenharmony_ci        // 打点x坐标
197fb726d48Sopenharmony_ci        let x: number = this.originX + (data[i][j][0] / xAxis[xAxis.length - 1]) * xLength;
198fb726d48Sopenharmony_ci        // 打点y坐标
199fb726d48Sopenharmony_ci        let y: number = this.originY - (data[i][j][1] / yAxis[yAxis.length - 1]) * yLength;
200fb726d48Sopenharmony_ci        let r: number = 6;
201fb726d48Sopenharmony_ci        if (i > 0) {
202fb726d48Sopenharmony_ci          options.paintingData[data[i][j][2] - 1] = {
203fb726d48Sopenharmony_ci            x,
204fb726d48Sopenharmony_ci            y,
205fb726d48Sopenharmony_ci            r,
206fb726d48Sopenharmony_ci            c: data[i][j],
207fb726d48Sopenharmony_ci            color: colorPool[i],
208fb726d48Sopenharmony_ci          };
209fb726d48Sopenharmony_ci        } else {
210fb726d48Sopenharmony_ci          options.paintingData.push({
211fb726d48Sopenharmony_ci            x,
212fb726d48Sopenharmony_ci            y,
213fb726d48Sopenharmony_ci            r,
214fb726d48Sopenharmony_ci            c: data[i][j],
215fb726d48Sopenharmony_ci            color: colorPool[i],
216fb726d48Sopenharmony_ci          });
217fb726d48Sopenharmony_ci        }
218fb726d48Sopenharmony_ci        this.drawCycle(x, y, r, 0.8, colorPool[i]);
219fb726d48Sopenharmony_ci      }
220fb726d48Sopenharmony_ci      if (data[i].length) {
221fb726d48Sopenharmony_ci        rectY = rectY + 20;
222fb726d48Sopenharmony_ci        this.ctx?.fillText(colorPoolText[i] + ': ', this.clientWidth - WIDTH_DELTA, rectY + 4);
223fb726d48Sopenharmony_ci        this.drawCycle(this.clientWidth - QUYU / 5, rectY, 7.5, 0.8, colorPool[i]);
224fb726d48Sopenharmony_ci      }
225fb726d48Sopenharmony_ci    }
226fb726d48Sopenharmony_ci  }
227fb726d48Sopenharmony_ci  /**
228fb726d48Sopenharmony_ci   * 画圆点
229fb726d48Sopenharmony_ci   */
230fb726d48Sopenharmony_ci  drawCycle(x: number, y: number, r: number, transparency: number, color: string): void {
231fb726d48Sopenharmony_ci    this.ctx!.fillStyle = color;
232fb726d48Sopenharmony_ci    this.ctx?.beginPath();
233fb726d48Sopenharmony_ci    this.ctx!.globalAlpha = transparency;
234fb726d48Sopenharmony_ci    this.ctx?.arc(x, y, r, 0, Math.PI * 2, true);
235fb726d48Sopenharmony_ci    this.ctx?.closePath();
236fb726d48Sopenharmony_ci    this.ctx?.fill();
237fb726d48Sopenharmony_ci  }
238fb726d48Sopenharmony_ci
239fb726d48Sopenharmony_ci  /**
240fb726d48Sopenharmony_ci   * 绘制最大负载线
241fb726d48Sopenharmony_ci   */
242fb726d48Sopenharmony_ci  drawLoadLine(data: Array<number>): void {
243fb726d48Sopenharmony_ci    let maxXAxis: number = 1;
244fb726d48Sopenharmony_ci    const QUYU: number = 100;
245fb726d48Sopenharmony_ci    const FOR_VALUE = 60;
246fb726d48Sopenharmony_ci    if (this.options) {
247fb726d48Sopenharmony_ci      maxXAxis = this.options.xAxisLabel[this.options.xAxisLabel.length - 1];
248fb726d48Sopenharmony_ci    }
249fb726d48Sopenharmony_ci    // data[1]用来标注n Hz负载线
250fb726d48Sopenharmony_ci    let addr1: number =
251fb726d48Sopenharmony_ci      this.originX + (this.finalX - this.originX - ((this.finalX - this.originX) % QUYU)) * (data[0] / maxXAxis);
252fb726d48Sopenharmony_ci    let addr2: number = (this.originY - this.finalY - ((this.originY - this.finalY) % QUYU)) / FOR_VALUE;
253fb726d48Sopenharmony_ci    let y: number = this.originY;
254fb726d48Sopenharmony_ci    this.ctx!.strokeStyle = '#ff0000';
255fb726d48Sopenharmony_ci    for (let i = 0; i < FOR_VALUE; i++) {
256fb726d48Sopenharmony_ci      this.ctx?.beginPath();
257fb726d48Sopenharmony_ci      this.ctx?.moveTo(addr1, y);
258fb726d48Sopenharmony_ci      y -= addr2;
259fb726d48Sopenharmony_ci      this.ctx?.lineTo(addr1, y);
260fb726d48Sopenharmony_ci      if (i % 2 !== 0) {
261fb726d48Sopenharmony_ci        this.ctx?.stroke();
262fb726d48Sopenharmony_ci      }
263fb726d48Sopenharmony_ci    }
264fb726d48Sopenharmony_ci    this.ctx!.font = '10px KATTI';
265fb726d48Sopenharmony_ci    this.ctx!.fillStyle = '#ff0000';
266fb726d48Sopenharmony_ci    this.ctx?.fillText(
267fb726d48Sopenharmony_ci      data[1] + 'Hz最大负载线',
268fb726d48Sopenharmony_ci      addr1 - FOR_VALUE / 3,
269fb726d48Sopenharmony_ci      this.originY - addr2 * FOR_VALUE - FOR_VALUE / 4
270fb726d48Sopenharmony_ci    );
271fb726d48Sopenharmony_ci    this.ctx!.fillStyle = '#000000';
272fb726d48Sopenharmony_ci    this.ctx?.fillText('过供给区', addr1 / 2, y + FOR_VALUE / 2);
273fb726d48Sopenharmony_ci    this.ctx?.fillText('欠供给区', addr1 / 2, this.originY - this.finalY);
274fb726d48Sopenharmony_ci    this.ctx?.fillText('超负载区', addr1 + FOR_VALUE / 3, (this.finalY + this.originY) / 2);
275fb726d48Sopenharmony_ci  }
276fb726d48Sopenharmony_ci
277fb726d48Sopenharmony_ci  /**
278fb726d48Sopenharmony_ci   * 绘制均衡线
279fb726d48Sopenharmony_ci   */
280fb726d48Sopenharmony_ci  drawBalanceLine(data: Array<number>): void {
281fb726d48Sopenharmony_ci    let maxXAxis: number = 1;
282fb726d48Sopenharmony_ci    const QUYU: number = 100;
283fb726d48Sopenharmony_ci    const FOR_VALUE = 60;
284fb726d48Sopenharmony_ci    if (this.options) {
285fb726d48Sopenharmony_ci      maxXAxis = this.options.xAxisLabel[this.options.xAxisLabel.length - 1];
286fb726d48Sopenharmony_ci    }
287fb726d48Sopenharmony_ci    // data[1]用来标注n Hz均衡线
288fb726d48Sopenharmony_ci    let addr1: number =
289fb726d48Sopenharmony_ci      ((this.finalX - this.originX - ((this.finalX - this.originX) % QUYU)) * (data[0] / maxXAxis)) / FOR_VALUE;
290fb726d48Sopenharmony_ci    let addr2: number = (this.originY - this.finalY - ((this.originY - this.finalY) % QUYU)) / FOR_VALUE;
291fb726d48Sopenharmony_ci    let x: number = this.originX;
292fb726d48Sopenharmony_ci    let y: number = this.originY;
293fb726d48Sopenharmony_ci    this.ctx!.strokeStyle = '#00ff00';
294fb726d48Sopenharmony_ci    for (let i = 0; i < FOR_VALUE; i++) {
295fb726d48Sopenharmony_ci      this.ctx?.beginPath();
296fb726d48Sopenharmony_ci      this.ctx?.moveTo(x, y);
297fb726d48Sopenharmony_ci      x += addr1;
298fb726d48Sopenharmony_ci      y -= addr2;
299fb726d48Sopenharmony_ci      this.ctx?.lineTo(x, y);
300fb726d48Sopenharmony_ci      if (i % 2 === 0) {
301fb726d48Sopenharmony_ci        this.ctx?.stroke();
302fb726d48Sopenharmony_ci      }
303fb726d48Sopenharmony_ci    }
304fb726d48Sopenharmony_ci    this.ctx?.save();
305fb726d48Sopenharmony_ci    this.ctx?.translate(addr1 * 25 + this.originX, addr2 * 40 + this.finalY);
306fb726d48Sopenharmony_ci    this.ctx!.font = '10px KATTI';
307fb726d48Sopenharmony_ci    this.ctx!.fillStyle = '#ff0f00';
308fb726d48Sopenharmony_ci    this.ctx?.rotate(-Math.atan(addr2 / addr1));
309fb726d48Sopenharmony_ci    this.ctx?.fillText(data[1] + 'Hz均衡线', 0, 0);
310fb726d48Sopenharmony_ci    this.ctx?.restore();
311fb726d48Sopenharmony_ci  }
312fb726d48Sopenharmony_ci
313fb726d48Sopenharmony_ci  /*检测是否hover在散点之上*/
314fb726d48Sopenharmony_ci  checkHover(options: LitChartScatterConfig | undefined, pos: Object): Object | boolean {
315fb726d48Sopenharmony_ci    let data: Array<Object> = [];
316fb726d48Sopenharmony_ci    if (options) {
317fb726d48Sopenharmony_ci      data = options.paintingData;
318fb726d48Sopenharmony_ci    }
319fb726d48Sopenharmony_ci    let found: boolean | Object = false;
320fb726d48Sopenharmony_ci    for (let i = 0; i < data.length; i++) {
321fb726d48Sopenharmony_ci      found = false;
322fb726d48Sopenharmony_ci      // @ts-ignore
323fb726d48Sopenharmony_ci      if (
324fb726d48Sopenharmony_ci        Math.sqrt(
325fb726d48Sopenharmony_ci          // @ts-ignore
326fb726d48Sopenharmony_ci          Math.pow(pos.x - data[i].x, 2) + Math.pow(pos.y - data[i].y, 2)
327fb726d48Sopenharmony_ci          // @ts-ignore
328fb726d48Sopenharmony_ci        ) < data[i].r
329fb726d48Sopenharmony_ci      ) {
330fb726d48Sopenharmony_ci        found = data[i];
331fb726d48Sopenharmony_ci        break;
332fb726d48Sopenharmony_ci      }
333fb726d48Sopenharmony_ci    }
334fb726d48Sopenharmony_ci    return found;
335fb726d48Sopenharmony_ci  }
336fb726d48Sopenharmony_ci
337fb726d48Sopenharmony_ci  /*绘制hover状态*/
338fb726d48Sopenharmony_ci  paintHover(): void {
339fb726d48Sopenharmony_ci    let obj: Object | null = this.options!.hoverData;
340fb726d48Sopenharmony_ci    // @ts-ignore
341fb726d48Sopenharmony_ci    let x: number = obj?.x;
342fb726d48Sopenharmony_ci    // @ts-ignore
343fb726d48Sopenharmony_ci    let y: number = obj?.y;
344fb726d48Sopenharmony_ci    // @ts-ignore
345fb726d48Sopenharmony_ci    let r: number = obj?.r;
346fb726d48Sopenharmony_ci    // @ts-ignore
347fb726d48Sopenharmony_ci    let c: string = obj?.color;
348fb726d48Sopenharmony_ci    let step: number = 0.5;
349fb726d48Sopenharmony_ci    this.ctx!.globalAlpha = 1;
350fb726d48Sopenharmony_ci    this.ctx!.fillStyle = c;
351fb726d48Sopenharmony_ci    for (let i = 0; i < 10; i++) {
352fb726d48Sopenharmony_ci      this.ctx?.beginPath();
353fb726d48Sopenharmony_ci      this.ctx?.arc(x, y, r + i * step, 0, 2 * Math.PI, false);
354fb726d48Sopenharmony_ci      this.ctx?.fill();
355fb726d48Sopenharmony_ci      this.ctx?.closePath();
356fb726d48Sopenharmony_ci    }
357fb726d48Sopenharmony_ci  }
358fb726d48Sopenharmony_ci  //利用离屏canvas恢复hover前的状态
359fb726d48Sopenharmony_ci  resetHoverWithOffScreen(): void {
360fb726d48Sopenharmony_ci    let obj: Object | null = null;
361fb726d48Sopenharmony_ci    const STEP_VALUE: number = 12;
362fb726d48Sopenharmony_ci    const OUT_CYCLE: number = 2;
363fb726d48Sopenharmony_ci    if (this.options) {
364fb726d48Sopenharmony_ci      obj = this.options.hoverData;
365fb726d48Sopenharmony_ci    }
366fb726d48Sopenharmony_ci    if (!obj) {
367fb726d48Sopenharmony_ci      return;
368fb726d48Sopenharmony_ci    }
369fb726d48Sopenharmony_ci    // @ts-ignore
370fb726d48Sopenharmony_ci    let { x, y, r, c, color } = obj;
371fb726d48Sopenharmony_ci    let step = 0.5;
372fb726d48Sopenharmony_ci    this.ctx!.globalAlpha = 1;
373fb726d48Sopenharmony_ci    for (let i = 10; i > 0; i--) {
374fb726d48Sopenharmony_ci      this.ctx?.save();
375fb726d48Sopenharmony_ci      //绘制外圆范围
376fb726d48Sopenharmony_ci      this.ctx?.drawImage(
377fb726d48Sopenharmony_ci        this.canvas2!,
378fb726d48Sopenharmony_ci        x - r - STEP_VALUE * step,
379fb726d48Sopenharmony_ci        y - r - STEP_VALUE * step,
380fb726d48Sopenharmony_ci        OUT_CYCLE * (r + STEP_VALUE * step),
381fb726d48Sopenharmony_ci        OUT_CYCLE * (r + STEP_VALUE * step),
382fb726d48Sopenharmony_ci        x - r - STEP_VALUE * step,
383fb726d48Sopenharmony_ci        y - r - STEP_VALUE * step,
384fb726d48Sopenharmony_ci        OUT_CYCLE * (r + STEP_VALUE * step),
385fb726d48Sopenharmony_ci        OUT_CYCLE * (r + STEP_VALUE * step)
386fb726d48Sopenharmony_ci      );
387fb726d48Sopenharmony_ci      //绘制内圆
388fb726d48Sopenharmony_ci      this.ctx?.beginPath();
389fb726d48Sopenharmony_ci      this.ctx?.arc(x, y, r + i * step, 0, OUT_CYCLE * Math.PI, false);
390fb726d48Sopenharmony_ci      this.ctx?.closePath();
391fb726d48Sopenharmony_ci      this.ctx!.fillStyle = color;
392fb726d48Sopenharmony_ci      this.ctx!.globalAlpha = 0.8;
393fb726d48Sopenharmony_ci      //填充内圆
394fb726d48Sopenharmony_ci      this.ctx?.fill();
395fb726d48Sopenharmony_ci      this.ctx?.restore();
396fb726d48Sopenharmony_ci    }
397fb726d48Sopenharmony_ci    this.options!.hoverData = null;
398fb726d48Sopenharmony_ci  }
399fb726d48Sopenharmony_ci  /**
400fb726d48Sopenharmony_ci   * 显示提示框
401fb726d48Sopenharmony_ci   */
402fb726d48Sopenharmony_ci  showTip(data: any): void {
403fb726d48Sopenharmony_ci    const minWidth: number = 160;
404fb726d48Sopenharmony_ci    const miniHeight: number = 70;
405fb726d48Sopenharmony_ci    const canvasWidth: number = Number(this.canvas?.style.width.replace('px', ''));
406fb726d48Sopenharmony_ci    const canvasHeight: number = Number(this.canvas?.style.height.replace('px', ''));
407fb726d48Sopenharmony_ci    this.scatterTipEL!.style.display = 'flex';
408fb726d48Sopenharmony_ci    if (canvasWidth - data.x < minWidth && canvasHeight - data.y >= miniHeight) {
409fb726d48Sopenharmony_ci      this.scatterTipEL!.style.top = `${data.y}px`;
410fb726d48Sopenharmony_ci      this.scatterTipEL!.style.left = `${data.x - minWidth}px`;
411fb726d48Sopenharmony_ci    } else if (canvasHeight - data.y < miniHeight && canvasWidth - data.x > minWidth) {
412fb726d48Sopenharmony_ci      this.scatterTipEL!.style.top = `${data.y - miniHeight}px`;
413fb726d48Sopenharmony_ci      this.scatterTipEL!.style.left = `${data.x}px`;
414fb726d48Sopenharmony_ci    } else if (canvasWidth - data.x < minWidth && canvasHeight - data.y < miniHeight) {
415fb726d48Sopenharmony_ci      this.scatterTipEL!.style.top = `${data.y - miniHeight}px`;
416fb726d48Sopenharmony_ci      this.scatterTipEL!.style.left = `${data.x - minWidth}px`;
417fb726d48Sopenharmony_ci    } else {
418fb726d48Sopenharmony_ci      this.scatterTipEL!.style.top = `${data.y}px`;
419fb726d48Sopenharmony_ci      this.scatterTipEL!.style.left = `${data.x}px`;
420fb726d48Sopenharmony_ci    }
421fb726d48Sopenharmony_ci    this.scatterTipEL!.innerHTML = this.options!.tip(data);
422fb726d48Sopenharmony_ci    // @ts-ignore
423fb726d48Sopenharmony_ci    this.options!.hoverEvent('CPU-FREQ', true, data.c[2] - 1);
424fb726d48Sopenharmony_ci  }
425fb726d48Sopenharmony_ci  /**
426fb726d48Sopenharmony_ci   * 隐藏提示框
427fb726d48Sopenharmony_ci   */
428fb726d48Sopenharmony_ci  hideTip(): void {
429fb726d48Sopenharmony_ci    this.scatterTipEL!.style.display = 'none';
430fb726d48Sopenharmony_ci    if (this.options) {
431fb726d48Sopenharmony_ci      // @ts-ignore
432fb726d48Sopenharmony_ci      this.options!.hoverEvent('CPU-FREQ', false);
433fb726d48Sopenharmony_ci    }
434fb726d48Sopenharmony_ci  }
435fb726d48Sopenharmony_ci
436fb726d48Sopenharmony_ci  connectedCallback(): void {
437fb726d48Sopenharmony_ci    super.connectedCallback();
438fb726d48Sopenharmony_ci    this.canvas = this.shadowRoot!.querySelector<HTMLCanvasElement>('#canvas');
439fb726d48Sopenharmony_ci    this.scatterTipEL = this.shadowRoot!.querySelector<HTMLDivElement>('#tip');
440fb726d48Sopenharmony_ci    this.ctx = this.canvas!.getContext('2d', { alpha: true });
441fb726d48Sopenharmony_ci    this.labelsEL = this.shadowRoot!.querySelector<HTMLDivElement>('#shape');
442fb726d48Sopenharmony_ci    resizeCanvas(this.canvas!);
443fb726d48Sopenharmony_ci    this.originX = this.clientWidth * 0.1;
444fb726d48Sopenharmony_ci    this.originY = this.clientHeight * 0.9;
445fb726d48Sopenharmony_ci    this.finalX = this.clientWidth;
446fb726d48Sopenharmony_ci    this.finalY = this.clientHeight * 0.1;
447fb726d48Sopenharmony_ci    /*hover效果*/
448fb726d48Sopenharmony_ci    this.canvas!.onmousemove = (event) => {
449fb726d48Sopenharmony_ci      let pos: Object = {
450fb726d48Sopenharmony_ci        x: event.offsetX,
451fb726d48Sopenharmony_ci        y: event.offsetY,
452fb726d48Sopenharmony_ci      };
453fb726d48Sopenharmony_ci      let hoverPoint: Object | boolean = this.checkHover(this.options, pos);
454fb726d48Sopenharmony_ci      /**
455fb726d48Sopenharmony_ci       * 如果当前有聚焦点
456fb726d48Sopenharmony_ci       */
457fb726d48Sopenharmony_ci      if (hoverPoint) {
458fb726d48Sopenharmony_ci        this.showTip(hoverPoint);
459fb726d48Sopenharmony_ci        let samePoint: boolean = this.options!.hoverData === hoverPoint ? true : false;
460fb726d48Sopenharmony_ci        if (!samePoint) {
461fb726d48Sopenharmony_ci          this.resetHoverWithOffScreen();
462fb726d48Sopenharmony_ci          this.options!.hoverData = hoverPoint;
463fb726d48Sopenharmony_ci        }
464fb726d48Sopenharmony_ci        this.paintHover();
465fb726d48Sopenharmony_ci      } else {
466fb726d48Sopenharmony_ci        //使用离屏canvas恢复
467fb726d48Sopenharmony_ci        this.resetHoverWithOffScreen();
468fb726d48Sopenharmony_ci        this.hideTip();
469fb726d48Sopenharmony_ci      }
470fb726d48Sopenharmony_ci    };
471fb726d48Sopenharmony_ci  }
472fb726d48Sopenharmony_ci
473fb726d48Sopenharmony_ci  initElements(): void {
474fb726d48Sopenharmony_ci    new ResizeObserver((entries, observer) => {
475fb726d48Sopenharmony_ci      entries.forEach((it) => {
476fb726d48Sopenharmony_ci        resizeCanvas(this.canvas!);
477fb726d48Sopenharmony_ci        this.originX = this.clientWidth * 0.1;
478fb726d48Sopenharmony_ci        this.originY = this.clientHeight * 0.95;
479fb726d48Sopenharmony_ci        this.finalX = this.clientWidth * 0.9;
480fb726d48Sopenharmony_ci        this.finalY = this.clientHeight * 0.1;
481fb726d48Sopenharmony_ci        this.labelsEL!.innerText = '';
482fb726d48Sopenharmony_ci        this.init();
483fb726d48Sopenharmony_ci      });
484fb726d48Sopenharmony_ci    }).observe(this);
485fb726d48Sopenharmony_ci  }
486fb726d48Sopenharmony_ci
487fb726d48Sopenharmony_ci  initHtml(): string {
488fb726d48Sopenharmony_ci    return (
489fb726d48Sopenharmony_ci      `
490fb726d48Sopenharmony_ci            <style>   
491fb726d48Sopenharmony_ci            :host {
492fb726d48Sopenharmony_ci                display: flex;
493fb726d48Sopenharmony_ci                flex-direction: column;
494fb726d48Sopenharmony_ci                overflow: hidden;
495fb726d48Sopenharmony_ci                width: 100%;
496fb726d48Sopenharmony_ci                height: 100%;
497fb726d48Sopenharmony_ci            }
498fb726d48Sopenharmony_ci            .shape.active {
499fb726d48Sopenharmony_ci                display: block;
500fb726d48Sopenharmony_ci                position: absolute;
501fb726d48Sopenharmony_ci                left: 35%;    
502fb726d48Sopenharmony_ci                z-index: 99;
503fb726d48Sopenharmony_ci            }
504fb726d48Sopenharmony_ci            #tip{
505fb726d48Sopenharmony_ci                background-color: #f5f5f4;
506fb726d48Sopenharmony_ci                border: 1px solid #fff;
507fb726d48Sopenharmony_ci                border-radius: 5px;
508fb726d48Sopenharmony_ci                color: #333322;
509fb726d48Sopenharmony_ci                font-size: 8pt;
510fb726d48Sopenharmony_ci                position: absolute;
511fb726d48Sopenharmony_ci                display: none;
512fb726d48Sopenharmony_ci                top: 0;
513fb726d48Sopenharmony_ci                left: 0;
514fb726d48Sopenharmony_ci                z-index: 99;
515fb726d48Sopenharmony_ci                pointer-events: none;
516fb726d48Sopenharmony_ci                user-select: none;
517fb726d48Sopenharmony_ci                padding: 5px 10px;
518fb726d48Sopenharmony_ci                box-shadow: 0 0 10px #22ffffff;
519fb726d48Sopenharmony_ci            }
520fb726d48Sopenharmony_ci            #root{
521fb726d48Sopenharmony_ci                position:relative;
522fb726d48Sopenharmony_ci            }
523fb726d48Sopenharmony_ci            .bg_nodata{
524fb726d48Sopenharmony_ci                background-repeat:no-repeat;
525fb726d48Sopenharmony_ci                background-position:center;
526fb726d48Sopenharmony_ci                background-image: url("img/pie_chart_no_data.png");
527fb726d48Sopenharmony_ci            }
528fb726d48Sopenharmony_ci            .bg_hasdata{
529fb726d48Sopenharmony_ci                background-repeat:no-repeat;
530fb726d48Sopenharmony_ci                background-position:center;
531fb726d48Sopenharmony_ci            }
532fb726d48Sopenharmony_ci            ` + this.dismantlingHtml()
533fb726d48Sopenharmony_ci    );
534fb726d48Sopenharmony_ci  }
535fb726d48Sopenharmony_ci
536fb726d48Sopenharmony_ci  /**
537fb726d48Sopenharmony_ci   * 拆解initHtml大函数块
538fb726d48Sopenharmony_ci   * @returns html
539fb726d48Sopenharmony_ci   */
540fb726d48Sopenharmony_ci  dismantlingHtml(): string {
541fb726d48Sopenharmony_ci    return `
542fb726d48Sopenharmony_ci      #labels{
543fb726d48Sopenharmony_ci        display: grid;
544fb726d48Sopenharmony_ci        grid-template-columns: auto auto auto auto auto;
545fb726d48Sopenharmony_ci        width: 100%;
546fb726d48Sopenharmony_ci        height: 25%;
547fb726d48Sopenharmony_ci        box-sizing: border-box;
548fb726d48Sopenharmony_ci        position: absolute;
549fb726d48Sopenharmony_ci        bottom: 0px;
550fb726d48Sopenharmony_ci        left: 0;
551fb726d48Sopenharmony_ci        padding-left: 10px;
552fb726d48Sopenharmony_ci        padding-right: 10px;
553fb726d48Sopenharmony_ci        pointer-events: none;
554fb726d48Sopenharmony_ci      }
555fb726d48Sopenharmony_ci      .name{
556fb726d48Sopenharmony_ci        flex: 1;
557fb726d48Sopenharmony_ci        font-size: 9pt;
558fb726d48Sopenharmony_ci        overflow: hidden;
559fb726d48Sopenharmony_ci        white-space: nowrap;
560fb726d48Sopenharmony_ci        text-overflow: ellipsis;
561fb726d48Sopenharmony_ci        color: var(--dark-color1,#252525);
562fb726d48Sopenharmony_ci        pointer-events: painted;
563fb726d48Sopenharmony_ci      }
564fb726d48Sopenharmony_ci      .label{
565fb726d48Sopenharmony_ci        display: flex;
566fb726d48Sopenharmony_ci        align-items: center;
567fb726d48Sopenharmony_ci        max-lines: 1;
568fb726d48Sopenharmony_ci        white-space: nowrap;
569fb726d48Sopenharmony_ci        overflow: hidden;
570fb726d48Sopenharmony_ci        padding-right: 5px;
571fb726d48Sopenharmony_ci      }
572fb726d48Sopenharmony_ci      .tag{
573fb726d48Sopenharmony_ci        display: flex;
574fb726d48Sopenharmony_ci        align-items: center;
575fb726d48Sopenharmony_ci        justify-content: center;
576fb726d48Sopenharmony_ci        width: 10px;
577fb726d48Sopenharmony_ci        height: 10px;
578fb726d48Sopenharmony_ci        border-radius: 5px;
579fb726d48Sopenharmony_ci        margin-right: 5px;
580fb726d48Sopenharmony_ci      }
581fb726d48Sopenharmony_ci      </style>
582fb726d48Sopenharmony_ci      <div id="root">
583fb726d48Sopenharmony_ci          <div id="shape" class="shape active"></div>
584fb726d48Sopenharmony_ci          <canvas id="canvas" style="top: 0;left: 0;z-index: 21;position: absolute"></canvas>
585fb726d48Sopenharmony_ci          <div id="tip"></div>
586fb726d48Sopenharmony_ci          <div id="labels"></div>
587fb726d48Sopenharmony_ci      </div>`;
588fb726d48Sopenharmony_ci  }
589fb726d48Sopenharmony_ci}
590