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