• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1/*
2 * Copyright (C) 2022 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 *     http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16import { Graph } from './Graph';
17import { Rect } from './Rect';
18import { ns2UnitS, TimerShaftElement } from '../TimerShaftElement';
19import { ColorUtils, interpolateColorBrightness } from '../base/ColorUtils';
20import { CpuStruct } from '../../../database/ui-worker/cpu/ProcedureWorkerCPU';
21import { CurrentSlicesTime, SpSystemTrace } from '../../SpSystemTrace';
22
23const MarkPadding = 5;
24const FIT_TOTALX_MIN: number = 280;
25const FIT_TOTALX_MAX: number = 300;
26const MID_OFFSET: number = 10;
27
28export class Mark extends Graph {
29  name: string | undefined;
30  inspectionFrame: Rect;
31  private _isHover: boolean = false;
32
33  constructor(canvas: HTMLCanvasElement | undefined | null, name: string, c: CanvasRenderingContext2D, frame: Rect) {
34    super(canvas, c, frame);
35    this.name = name;
36    this.inspectionFrame = new Rect(frame.x - MarkPadding, frame.y, frame.width + MarkPadding * 2, frame.height);
37  }
38
39  get isHover(): boolean {
40    return this._isHover;
41  }
42
43  set isHover(value: boolean) {
44    this._isHover = value;
45    if (value) {
46      document.body.style.cursor = 'ew-resize';
47    } else {
48      document.body.style.cursor = 'default';
49    }
50  }
51
52  draw(): void {
53    this.context2D.beginPath();
54    this.context2D.strokeStyle = '#999999';
55    this.context2D.lineWidth = 7;
56    this.context2D.moveTo(this.frame.x, this.frame.y);
57    this.context2D.lineTo(this.frame.x, this.frame.y + this.frame.height / 3);
58    this.context2D.stroke();
59    this.context2D.strokeStyle = '#999999';
60    this.context2D.lineWidth = 1;
61    this.context2D.moveTo(this.frame.x, this.frame.y);
62    this.context2D.lineTo(this.frame.x, this.frame.y + this.frame.height);
63    this.context2D.stroke();
64    this.context2D.closePath();
65  }
66}
67
68export interface TimeRange {
69  slicesTime: {
70    color: string | null | undefined;
71    startTime: number | null | undefined;
72    endTime: number | null | undefined;
73  };
74  scale: number;
75  totalNS: number;
76  startX: number;
77  endX: number;
78  startNS: number;
79  endNS: number;
80  xs: Array<number>;
81  refresh: boolean;
82  xsTxt: Array<string>;
83}
84
85export class RangeRuler extends Graph {
86  public rangeRect: Rect;
87  public currentSlicesTime: CurrentSlicesTime;
88  public markAObj: Mark;
89  public markBObj: Mark;
90  public drawMark: boolean = false;
91  public range: TimeRange;
92  private pressedKeys: Array<string> = [];
93  mouseDownOffsetX = 0;
94  mouseDownMovingMarkX = 0;
95  movingMark: Mark | undefined | null;
96  isMouseDown: boolean = false;
97  isMovingRange: boolean = false;
98  isNewRange: boolean = false;
99  markAX: number = 0;
100  markBX: number = 0;
101  isPress: boolean = false;
102  pressFrameIdF: number = -1;
103  pressFrameIdW: number = -1;
104  pressFrameIdS: number = -1;
105  pressFrameIdA: number = -1;
106  pressFrameIdD: number = -1;
107  upFrameIdW: number = -1;
108  upFrameIdS: number = -1;
109  upFrameIdA: number = -1;
110  upFrameIdD: number = -1;
111  currentDuration: number = 0;
112  cacheInterval: { interval: number; value: number; flag: boolean } = {
113    interval: 200,
114    value: 0,
115    flag: false,
116  };
117  centerXPercentage: number = 0;
118  animaStartTime: number | undefined;
119  p: number = 1000;
120  private readonly notifyHandler: (r: TimeRange) => void;
121  private scale: number = 0;
122  private delayTimer: any = null;
123  private rulerW = 0;
124
125  //缩放级别
126  private scales: Array<number> = [
127    50, 100, 200, 500, 1_000, 2_000, 5_000, 10_000, 20_000, 50_000, 100_000, 200_000, 500_000, 1_000_000, 2_000_000,
128    5_000_000, 10_000_000, 20_000_000, 50_000_000, 100_000_000, 200_000_000, 500_000_000, 1_000_000_000, 2_000_000_000,
129    5_000_000_000, 10_000_000_000, 20_000_000_000, 50_000_000_000, 100_000_000_000, 200_000_000_000, 500_000_000_000,
130    1_000_000_000_000, 2_000_000_000_000, 5_000_000_000_000,
131  ];
132  private _cpuUsage: Array<{ cpu: number; ro: number; rate: number }> = [];
133
134  constructor(timerShaftEL: TimerShaftElement, frame: Rect, range: TimeRange, notifyHandler: (r: TimeRange) => void) {
135    super(timerShaftEL.canvas, timerShaftEL.ctx!, frame);
136    this.range = range;
137    this.notifyHandler = notifyHandler;
138    this.markAObj = new Mark(
139      timerShaftEL.canvas,
140      'A',
141      timerShaftEL.ctx!,
142      new Rect(range.startX, frame.y, 1, frame.height)
143    );
144    this.markBObj = new Mark(
145      timerShaftEL.canvas,
146      'B',
147      timerShaftEL.ctx!,
148      new Rect(range.endX, frame.y, 1, frame.height)
149    );
150    this.rangeRect = new Rect(range.startX, frame.y, range.endX - range.startX, frame.height);
151    this.currentSlicesTime = new CurrentSlicesTime();
152  }
153
154  set cpuUsage(value: Array<{ cpu: number; ro: number; rate: number }>) {
155    this._cpuUsage = value;
156    this.draw();
157  }
158
159  drawCpuUsage(): void {
160    this.context2D.clearRect(this.frame.x, this.frame.y, this.frame.width, this.frame.height);
161    let miniHeight = Math.round(this.frame.height / CpuStruct.cpuCount); //每格高度
162    let miniWidth = Math.ceil(this.frame.width / 100); //每格宽度
163    for (let index = 0; index < this._cpuUsage.length; index++) {
164      let cpuUsageItem = this._cpuUsage[index];
165      const color = interpolateColorBrightness(ColorUtils.MD_PALETTE[cpuUsageItem.cpu], cpuUsageItem.rate);
166      this.context2D.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
167      this.context2D.globalAlpha = cpuUsageItem.rate;
168      this.context2D.fillRect(
169        this.frame.x + miniWidth * cpuUsageItem.ro,
170        this.frame.y + cpuUsageItem.cpu * miniHeight,
171        miniWidth,
172        miniHeight
173      );
174    }
175  }
176
177  draw(discardNotify: boolean = false): void {
178    this.context2D.clearRect(
179      this.frame.x - MarkPadding,
180      this.frame.y,
181      this.frame.width + MarkPadding * 2,
182      this.frame.height
183    );
184    this.context2D.beginPath();
185    if (this._cpuUsage.length > 0) {
186      this.drawCpuUsage();
187      this.context2D.globalAlpha = 0;
188    } else {
189      this.context2D.globalAlpha = 1;
190    }
191    //绘制选中区域
192    if (this.drawMark) {
193      this.drawSelectionRange();
194    }
195    if (this.notifyHandler) {
196      this.range.startX = this.rangeRect.x;
197      this.range.endX = this.rangeRect.x + this.rangeRect.width;
198      this.range.startNS = (this.range.startX * this.range.totalNS) / (this.canvas?.clientWidth || 0);
199      this.range.endNS = (this.range.endX * this.range.totalNS) / (this.canvas?.clientWidth || 0);
200      this.calculateScale();
201      this.updateRange();
202      if (!discardNotify) {
203        this.notifyHandler(this.range);
204      }
205    }
206  }
207
208  private updateRange(): void {
209    let tempNs = 0;
210    let rangeStartX = 0;
211    let rangeYu = this.range.startNS % this.scale;
212    let rangeRealW = (this.scale * this.frame.width) / (this.range.endNS - this.range.startNS);
213    if (this.range.xsTxt) {
214      this.range.xsTxt.length = 0;
215    } else {
216      this.range.xsTxt = [];
217    }
218    if (this.range.xs) {
219      this.range.xs.length = 0;
220    } else {
221      this.range.xs = [];
222    }
223    this.range.scale = this.scale;
224    if (rangeYu !== 0) {
225      let first_NodeWidth = ((this.scale - rangeYu) / this.scale) * rangeRealW;
226      rangeStartX += first_NodeWidth;
227      tempNs += this.scale - rangeYu;
228      this.range.xs.push(rangeStartX);
229      this.range.xsTxt.push(ns2UnitS(tempNs + this.range.startNS, this.scale));
230    }
231    while (tempNs < this.range.endNS - this.range.startNS) {
232      rangeStartX += rangeRealW;
233      tempNs += this.scale;
234      this.range.xs.push(rangeStartX);
235      this.range.xsTxt.push(ns2UnitS(tempNs + this.range.startNS, this.scale));
236    }
237  }
238
239  private calculateScale(): void {
240    let l20 = (this.range.endNS - this.range.startNS) / 20;
241    let minScale = 0;
242    let maxScale = 0;
243    let weight = 0;
244    for (let scalesIndex = 0; scalesIndex < this.scales.length; scalesIndex++) {
245      if (this.scales[scalesIndex] > l20) {
246        if (scalesIndex > 0) {
247          minScale = this.scales[scalesIndex - 1];
248        } else {
249          minScale = 0;
250        }
251        maxScale = this.scales[scalesIndex];
252        weight = ((l20 - minScale) * 1.0) / (maxScale - minScale);
253        if (weight > 0.243) {
254          this.scale = maxScale;
255        } else {
256          this.scale = minScale;
257        }
258        break;
259      }
260    }
261    if (this.scale === 0) {
262      this.scale = this.scales[0];
263    }
264  }
265
266  private drawSelectionRange(): void {
267    this.context2D.fillStyle = window.getComputedStyle(this.canvas!, null).getPropertyValue('background-color');
268    this.rangeRect.x = this.markAObj.frame.x < this.markBObj.frame.x ? this.markAObj.frame.x : this.markBObj.frame.x;
269    this.rangeRect.width = Math.abs(this.markBObj.frame.x - this.markAObj.frame.x);
270    this.context2D.fillRect(this.rangeRect.x, this.rangeRect.y, this.rangeRect.width, this.rangeRect.height);
271    this.context2D.globalAlpha = 1;
272    this.context2D.globalAlpha = 0.5;
273    this.context2D.fillStyle = '#999999';
274    this.context2D.fillRect(this.frame.x, this.frame.y, this.rangeRect.x, this.rangeRect.height);
275    this.context2D.fillRect(
276      this.rangeRect.x + this.rangeRect.width,
277      this.frame.y,
278      this.frame.width - this.rangeRect.width,
279      this.rangeRect.height
280    );
281    this.context2D.globalAlpha = 1;
282    this.context2D.closePath();
283    this.markAObj.draw();
284    this.markBObj.draw();
285  }
286
287  getScale(): number {
288    return this.scale;
289  }
290
291  mouseDown(mouseEventDown: MouseEvent): void {
292    let mouseDown_x = mouseEventDown.offsetX - (this.canvas?.offsetLeft || 0);
293    let mouseDown_y = mouseEventDown.offsetY - (this.canvas?.offsetTop || 0);
294    this.isMouseDown = true;
295    this.mouseDownOffsetX = mouseDown_x;
296    if (this.markAObj.isHover) {
297      this.movingMark = this.markAObj;
298      this.mouseDownMovingMarkX = this.movingMark.frame.x || 0;
299    } else if (this.markBObj.isHover) {
300      this.movingMark = this.markBObj;
301      this.mouseDownMovingMarkX = this.movingMark.frame.x || 0;
302    } else {
303      this.movingMark = null;
304    }
305    if (this.rangeRect.containsWithPadding(mouseDown_x, mouseDown_y, 5, 0)) {
306      this.isMovingRange = true;
307      this.markAX = this.markAObj.frame.x;
308      this.markBX = this.markBObj.frame.x;
309      document.body.style.cursor = 'move';
310    } else if (
311      this.frame.containsWithMargin(mouseDown_x, mouseDown_y, 20, 0, 0, 0) &&
312      !this.rangeRect.containsWithMargin(mouseDown_x, mouseDown_y, 0, MarkPadding, 0, MarkPadding)
313    ) {
314      this.isNewRange = true;
315    }
316  }
317
318  mouseUp(ev: MouseEvent) {
319    this.isMouseDown = false;
320    this.isMovingRange = false;
321    this.isNewRange = false;
322    this.movingMark = null;
323  }
324
325  mouseMove(ev: MouseEvent, trace: SpSystemTrace): void {
326    this.range.refresh = false;
327    let move_x = ev.offsetX - (this.canvas?.offsetLeft || 0);
328    let move_y = ev.offsetY - (this.canvas?.offsetTop || 0);
329    this.centerXPercentage = move_x / (this.canvas?.clientWidth || 0);
330    if (this.centerXPercentage <= 0) {
331      this.centerXPercentage = 0;
332    } else if (this.centerXPercentage >= 1) {
333      this.centerXPercentage = 1;
334    }
335    let maxX = this.canvas?.clientWidth || 0;
336    if (this.markAObj.inspectionFrame.contains(move_x, move_y)) {
337      this.markAObj.isHover = true;
338    } else if (this.markBObj.inspectionFrame.contains(move_x, move_y)) {
339      this.markBObj.isHover = true;
340    } else {
341      this.markAObj.isHover = false;
342      this.markBObj.isHover = false;
343    }
344    this.handleMovingMark(move_x, move_y, maxX, trace);
345    this.handleMovingFresh(move_x, maxX);
346  }
347
348  private handleMovingFresh(move_x: number, maxX: number): void {
349    if (this.isMovingRange && this.isMouseDown) {
350      let result = move_x - this.mouseDownOffsetX;
351      let mA = result + this.markAX;
352      let mB = result + this.markBX;
353      if (mA >= 0 && mA <= maxX) {
354        this.markAObj.frame.x = mA;
355      } else if (mA < 0) {
356        this.markAObj.frame.x = 0;
357      } else {
358        this.markAObj.frame.x = maxX;
359      }
360      this.markAObj.inspectionFrame.x = this.markAObj.frame.x - MarkPadding;
361      if (mB >= 0 && mB <= maxX) {
362        this.markBObj.frame.x = mB;
363      } else if (mB < 0) {
364        this.markBObj.frame.x = 0;
365      } else {
366        this.markBObj.frame.x = maxX;
367      }
368      this.markBObj.inspectionFrame.x = this.markBObj.frame.x - MarkPadding;
369      this.recordMovingS();
370      requestAnimationFrame(() => {
371        this.draw();
372        this.range.refresh = false;
373        this.delayDraw();
374      });
375    } else if (this.isNewRange) {
376      this.markAObj.frame.x = this.mouseDownOffsetX;
377      this.markAObj.inspectionFrame.x = this.mouseDownOffsetX - MarkPadding;
378      if (move_x >= 0 && move_x <= maxX) {
379        this.markBObj.frame.x = move_x;
380      } else if (move_x < 0) {
381        this.markBObj.frame.x = 0;
382      } else {
383        this.markBObj.frame.x = maxX;
384      }
385      this.markBObj.inspectionFrame.x = this.markBObj.frame.x - MarkPadding;
386      this.recordMovingS();
387      requestAnimationFrame(() => {
388        this.draw();
389        this.range.refresh = false;
390        this.delayDraw();
391      });
392    }
393  }
394
395  private handleMovingMark(
396    move_x: number,
397    move_y: number,
398    maxX: number,
399    trace: SpSystemTrace
400  ): void {
401    if (this.movingMark) {
402      let result = move_x - this.mouseDownOffsetX + this.mouseDownMovingMarkX;
403      if (result >= 0 && result <= maxX) {
404        this.movingMark.frame.x = result;
405      } else if (result < 0) {
406        this.movingMark.frame.x = 0;
407      } else {
408        this.movingMark.frame.x = maxX;
409      }
410      this.movingMark.inspectionFrame.x = this.movingMark.frame.x - MarkPadding;
411      this.recordMovingS();
412      requestAnimationFrame(() => {
413        this.draw();
414        this.range.refresh = false;
415        this.delayDraw();
416      });
417    } else if (this.rangeRect.containsWithPadding(move_x, move_y, MarkPadding, 0)) {
418      trace.style.cursor = 'move';
419      document.body.style.cursor = 'move';
420    } else if (
421      this.frame.containsWithMargin(move_x, move_y, 20, 0, 0, 0) &&
422      !this.rangeRect.containsWithMargin(move_x, move_y, 0, MarkPadding, 0, MarkPadding)
423    ) {
424      trace.style.cursor = 'crosshair';
425      document.body.style.cursor = 'crosshair';
426    }
427  }
428
429  recordMovingS() {
430    if (this.animaStartTime == undefined) {
431      let dat = new Date();
432      dat.setTime(dat.getTime() - 400);
433      this.animaStartTime = dat.getTime();
434    }
435    this.currentDuration = new Date().getTime() - this.animaStartTime;
436    this.setCacheInterval();
437    this.range.refresh = this.cacheInterval.flag;
438  }
439
440  setCacheInterval() {
441    if (Math.trunc(this.currentDuration / this.cacheInterval.interval) !== this.cacheInterval.value) {
442      this.cacheInterval.flag = true;
443      this.cacheInterval.value = Math.trunc(this.currentDuration / this.cacheInterval.interval);
444    } else {
445      this.cacheInterval.flag = false;
446    }
447  }
448
449  delayDraw() {
450    if (this.delayTimer) {
451      clearTimeout(this.delayTimer);
452    }
453    this.delayTimer = setTimeout(() => {
454      this.range.refresh = true;
455      this.draw();
456      this.range.refresh = false;
457      this.animaStartTime = undefined;
458    }, this.cacheInterval.interval + 50);
459  }
460
461  mouseOut(ev: MouseEvent): void {
462    this.movingMark = null;
463  }
464
465  fillX() {
466    if (this.range.endNS < 0) this.range.endNS = 0;
467    if (this.range.startNS < 0) this.range.startNS = 0;
468    if (this.range.endNS > this.range.totalNS) this.range.endNS = this.range.totalNS;
469    if (this.range.startNS > this.range.totalNS) this.range.startNS = this.range.totalNS;
470    this.range.startX = (this.range.startNS * (this.canvas?.clientWidth || 0)) / this.range.totalNS;
471    this.range.endX = (this.range.endNS * (this.canvas?.clientWidth || 0)) / this.range.totalNS;
472    this.markAObj.frame.x = this.range.startX;
473    this.markAObj.inspectionFrame.x = this.markAObj.frame.x - MarkPadding;
474    this.markBObj.frame.x = this.range.endX;
475    this.markBObj.inspectionFrame.x = this.markBObj.frame.x - MarkPadding;
476  }
477
478  setRangeNS(startNS: number, endNS: number) {
479    this.range.startNS = startNS;
480    this.range.endNS = endNS;
481    this.fillX();
482    this.draw();
483  }
484
485  getRange(): TimeRange {
486    return this.range;
487  }
488
489  cancelPressFrame(): void {
490    if (this.pressFrameIdA !== -1) cancelAnimationFrame(this.pressFrameIdA);
491    if (this.pressFrameIdD !== -1) cancelAnimationFrame(this.pressFrameIdD);
492    if (this.pressFrameIdW !== -1) cancelAnimationFrame(this.pressFrameIdW);
493    if (this.pressFrameIdS !== -1) cancelAnimationFrame(this.pressFrameIdS);
494    if (this.pressFrameIdF !== -1) cancelAnimationFrame(this.pressFrameIdF);
495  }
496
497  cancelUpFrame(): void {
498    if (this.upFrameIdA !== -1) cancelAnimationFrame(this.upFrameIdA);
499    if (this.upFrameIdD !== -1) cancelAnimationFrame(this.upFrameIdD);
500    if (this.upFrameIdW !== -1) cancelAnimationFrame(this.upFrameIdW);
501    if (this.upFrameIdS !== -1) cancelAnimationFrame(this.upFrameIdS);
502  }
503
504  cancelTimeOut: any = undefined;
505  isKeyPress: boolean = false;
506
507  keyPress(keyboardEvent: KeyboardEvent, currentSlicesTime?: CurrentSlicesTime) {
508    //第一个按键或者最后一个按下的和当前按键不一致
509    if (
510      this.pressedKeys.length == 0 ||
511      this.pressedKeys[this.pressedKeys.length - 1] !== keyboardEvent.key.toLocaleLowerCase()
512    ) {
513      this.setCacheInterval();
514      this.range.refresh = this.cacheInterval.flag;
515      if (currentSlicesTime) {
516        this.currentSlicesTime = currentSlicesTime;
517      }
518      this.cancelPressFrame();
519      this.cancelUpFrame();
520      this.pressedKeys.push(keyboardEvent.key.toLocaleLowerCase());
521      this.animaStartTime = new Date().getTime(); //记录按下的时间
522      this.keyboardKeyPressMap[this.pressedKeys[this.pressedKeys.length - 1]]?.bind(this)();
523    }
524    this.isPress = true;
525  }
526
527  keyPressF(): void {
528    let animF = () => {
529      let clientWidth = this.canvas?.clientWidth || 0;
530      let midX = Math.round(clientWidth / 2);
531      let startTime = 0;
532      let endTime = 0;
533      this.rulerW = this.canvas!.offsetWidth;
534      if (this.currentSlicesTime.startTime) {
535        startTime = this.currentSlicesTime.startTime;
536      } else {
537        return;
538      }
539      if (this.currentSlicesTime.endTime) {
540        endTime = this.currentSlicesTime.endTime;
541      } else {
542        return;
543      }
544      let startX = midX - 150;
545      let endX = midX + 150;
546      this.range.startNS = (endX * startTime - startX * endTime) / (endX - startX);
547      this.range.endNS = ((this.rulerW * (endTime - this.range.startNS)) + this.range.startNS * endX) / endX;
548      this.fillX();
549      this.draw();
550      this.range.refresh = true;
551      this.notifyHandler(this.range);
552      this.range.refresh = false;
553      this.pressFrameIdF = requestAnimationFrame(animF);
554    };
555    this.pressFrameIdF = requestAnimationFrame(animF);
556  }
557
558  fixReg = 76; //速度上线
559  f = 11; //加速度系数,值越小加速度越大
560
561  keyPressW() {
562    let animW = (): void => {
563      if (this.scale === 50) {
564        this.fillX();
565        this.range.refresh = true;
566        this.notifyHandler(this.range);
567        this.range.refresh = false;
568        return;
569      }
570      this.currentDuration = (Date.now() - this.animaStartTime!) / this.f; //reg
571      if (this.currentDuration >= this.fixReg) {
572        this.currentDuration = this.fixReg;
573      }
574      let bb = Math.tan((Math.PI / 180) * this.currentDuration);
575      this.range.startNS += this.centerXPercentage * bb * this.scale;
576      this.range.endNS -= (1 - this.centerXPercentage) * bb * this.scale;
577      this.fillX();
578      this.draw();
579      this.range.refresh = false;
580      this.pressFrameIdW = requestAnimationFrame(animW);
581    };
582    this.pressFrameIdW = requestAnimationFrame(animW);
583  }
584
585  keyPressS(): void {
586    let animS = (): void => {
587      if (this.range.startNS <= 0 && this.range.endNS >= this.range.totalNS) {
588        this.fillX();
589        this.range.refresh = true;
590        this.notifyHandler(this.range);
591        this.range.refresh = false;
592        return;
593      }
594      this.currentDuration = (Date.now() - this.animaStartTime!) / this.f;
595      if (this.currentDuration >= this.fixReg) {
596        this.currentDuration = this.fixReg;
597      }
598      let bb = Math.tan((Math.PI / 180) * this.currentDuration);
599      this.range.startNS -= this.centerXPercentage * bb * this.scale;
600      this.range.endNS += (1 - this.centerXPercentage) * bb * this.scale;
601      this.fillX();
602      this.draw();
603      this.range.refresh = false;
604      this.pressFrameIdS = requestAnimationFrame(animS);
605    };
606    this.pressFrameIdS = requestAnimationFrame(animS);
607  }
608
609  keyPressA(): void {
610    let animA = (): void => {
611      if (this.range.startNS <= 0) {
612        this.fillX();
613        this.range.refresh = true;
614        this.notifyHandler(this.range);
615        this.range.refresh = false;
616        return;
617      }
618      this.currentDuration = (Date.now() - this.animaStartTime!) / this.f;
619      if (this.currentDuration >= this.fixReg) {
620        this.currentDuration = this.fixReg;
621      }
622      let bb = Math.tan((Math.PI / 180) * this.currentDuration);
623      let s = this.scale * bb;
624      this.range.startNS -= s;
625      this.range.endNS -= s;
626      this.fillX();
627      this.draw();
628      this.range.refresh = false;
629      this.pressFrameIdA = requestAnimationFrame(animA);
630    };
631    this.pressFrameIdA = requestAnimationFrame(animA);
632  }
633
634  keyPressD(): void {
635    let animD = (): void => {
636      if (this.range.endNS >= this.range.totalNS) {
637        this.fillX();
638        this.range.refresh = true;
639        this.notifyHandler(this.range);
640        this.range.refresh = false;
641        return;
642      }
643      this.currentDuration = (Date.now() - this.animaStartTime!) / this.f;
644      if (this.currentDuration >= this.fixReg) this.currentDuration = this.fixReg;
645      let bb = Math.tan((Math.PI / 180) * this.currentDuration);
646      let s = this.scale * bb;
647      this.range.startNS += s;
648      this.range.endNS += s;
649      this.fillX();
650      this.draw();
651      this.range.refresh = false;
652      this.pressFrameIdD = requestAnimationFrame(animD);
653    };
654    this.pressFrameIdD = requestAnimationFrame(animD);
655  }
656
657  keyboardKeyPressMap: any = {
658    w: this.keyPressW,
659    s: this.keyPressS,
660    a: this.keyPressA,
661    d: this.keyPressD,
662    f: this.keyPressF,
663  };
664
665  keyboardKeyUpMap: any = {
666    w: this.keyUpW,
667    s: this.keyUpS,
668    a: this.keyUpA,
669    d: this.keyUpD,
670  };
671
672  keyUp(ev: KeyboardEvent): void {
673    this.cacheInterval.value = 0;
674    if (this.pressedKeys.length > 0) {
675      let number = this.pressedKeys.findIndex((value) => value === ev.key.toLocaleLowerCase());
676      if (number == this.pressedKeys.length - 1) {
677        this.animaStartTime = undefined;
678        this.cancelPressFrame();
679        this.keyboardKeyUpMap[ev.key]?.bind(this)();
680      }
681      if (number != -1) {
682        this.pressedKeys.splice(number, 1);
683      }
684    }
685    this.isPress = false;
686  }
687
688  keyUpW(): void {
689    let startTime = new Date().getTime();
690    let animW = () => {
691      if (this.scale === 50) {
692        this.fillX();
693        this.keyUpEnd();
694        return;
695      }
696      let dur = new Date().getTime() - startTime;
697      if (dur > 150) dur = 150;
698      let offset = Math.tan((Math.PI / 180) * (150 - dur) * 0.2) * this.scale;
699      this.range.startNS += this.centerXPercentage * offset;
700      this.range.endNS -= (1 - this.centerXPercentage) * offset;
701      this.fillX();
702      this.draw();
703      this.range.refresh = false;
704      if (dur < 150) {
705        this.upFrameIdW = requestAnimationFrame(animW);
706      } else {
707        this.keyUpEnd();
708      }
709    };
710    this.upFrameIdW = requestAnimationFrame(animW);
711  }
712
713  keyUpS(): void {
714    let startTime = new Date().getTime();
715    let animS = (): void => {
716      if (this.range.startNS <= 0 && this.range.endNS >= this.range.totalNS) {
717        this.fillX();
718        this.keyUpEnd();
719        return;
720      }
721      let dur = new Date().getTime() - startTime;
722      if (dur > 150) dur = 150;
723      let offset = Math.tan((Math.PI / 180) * (150 - dur) * 0.2) * this.scale;
724      this.range.startNS -= this.centerXPercentage * offset;
725      this.range.endNS += (1 - this.centerXPercentage) * offset;
726      this.fillX();
727      this.draw();
728      this.range.refresh = false;
729      if (dur < 150) {
730        this.upFrameIdS = requestAnimationFrame(animS);
731      } else {
732        this.keyUpEnd();
733      }
734    };
735    this.upFrameIdS = requestAnimationFrame(animS);
736  }
737
738  keyUpA(): void {
739    let startTime = new Date().getTime();
740    let animA = (): void => {
741      if (this.range.startNS <= 0) {
742        this.fillX();
743        this.keyUpEnd();
744        return;
745      }
746      let dur = new Date().getTime() - startTime;
747      if (dur > 150) dur = 150;
748      let offset = Math.tan((Math.PI / 180) * (150 - dur) * 0.15) * this.scale;
749      this.range.startNS -= offset;
750      this.range.endNS -= offset;
751      this.fillX();
752      this.draw();
753      this.range.refresh = false;
754      if (dur < 150) {
755        this.upFrameIdA = requestAnimationFrame(animA);
756      } else {
757        this.keyUpEnd();
758      }
759    };
760    this.upFrameIdA = requestAnimationFrame(animA);
761  }
762
763  keyUpEnd(): void {
764    this.range.refresh = true;
765    window.isLastFrame = true;
766    this.notifyHandler(this.range);
767    this.range.refresh = false;
768    window.isLastFrame = false;
769  }
770
771  keyUpD(): void {
772    let startTime = new Date().getTime();
773    let animD = (): void => {
774      if (this.range.endNS >= this.range.totalNS) {
775        this.keyUpEnd();
776        return;
777      }
778      let dur = new Date().getTime() - startTime;
779      let offset = Math.tan((Math.PI / 180) * (150 - dur) * 0.15) * this.scale;
780      this.range.startNS += offset;
781      this.range.endNS += offset;
782      this.fillX();
783      this.draw();
784      this.range.refresh = false;
785      if (dur < 150) {
786        this.upFrameIdD = requestAnimationFrame(animD);
787      } else {
788        this.keyUpEnd();
789      }
790    };
791    this.upFrameIdD = requestAnimationFrame(animD);
792  }
793
794  translate(distance: number): void {
795    const rangeDur = this.range.endNS - this.range.startNS;
796    const time = (distance / this.canvas!.width) * rangeDur;
797    if (
798      this.range.startNS < 0 ||
799      this.range.endNS < 0 ||
800      this.range.startNS > this.range.totalNS ||
801      this.range.endNS > this.range.totalNS
802    ) {
803      return;
804    }
805    this.range.startNS -= time;
806    this.range.endNS -= time;
807    this.fillX();
808    this.draw();
809    this.range.refresh = true;
810    this.notifyHandler(this.range);
811    this.range.refresh = false;
812  }
813}
814