• 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 + 75 / 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 + 75);
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  pressFrameIdF: number = -1;
102  pressFrameIdW: number = -1;
103  pressFrameIdS: number = -1;
104  pressFrameIdA: number = -1;
105  pressFrameIdD: number = -1;
106  pressFrameIdFlagIntoView: 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: unknown = null;
123  private rulerW = 0;
124  _cpuCountData: number | undefined;
125
126  //缩放级别
127  private scales: Array<number> = [
128    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,
129    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,
130    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,
131    1_000_000_000_000, 2_000_000_000_000, 5_000_000_000_000,
132  ];
133  private _cpuUsage: Array<{ cpu: number; ro: number; rate: number }> = [];
134
135  constructor(timerShaftEL: TimerShaftElement, frame: Rect, range: TimeRange, notifyHandler: (r: TimeRange) => void) {
136    super(timerShaftEL.canvas, timerShaftEL.ctx!, frame);
137    this.range = range;
138    this.notifyHandler = notifyHandler;
139    this.markAObj = new Mark(
140      timerShaftEL.canvas,
141      'A',
142      timerShaftEL.ctx!,
143      new Rect(range.startX, frame.y, 1, frame.height)
144    );
145    this.markBObj = new Mark(
146      timerShaftEL.canvas,
147      'B',
148      timerShaftEL.ctx!,
149      new Rect(range.endX, frame.y, 1, frame.height)
150    );
151    this.rangeRect = new Rect(range.startX, frame.y, range.endX - range.startX, frame.height);
152    this.currentSlicesTime = new CurrentSlicesTime();
153  }
154
155  set cpuUsage(value: Array<{ cpu: number; ro: number; rate: number }>) {
156    this._cpuUsage = value;
157    this.draw();
158  }
159
160  get cpuCountData(): number | undefined {
161    return this._cpuCountData;
162  }
163
164  set cpuCountData(value: number | undefined) {
165    this._cpuCountData = value;
166  }
167
168  drawCpuUsage(): void {
169    this.context2D.clearRect(this.frame.x, this.frame.y, this.frame.width, this.frame.height);
170    let miniHeight = Math.round(this.frame.height / CpuStruct.cpuCount); //每格高度
171    let miniWidth = Math.ceil(this.frame.width / 100); //每格宽度
172    this._cpuCountData = CpuStruct.cpuCount;
173    if (sessionStorage.getItem('expand') === 'true') {
174      //展开
175      miniHeight = Math.round(this.frame.height / CpuStruct.cpuCount);
176    } else if (sessionStorage.getItem('expand') === 'false') {
177      miniHeight = Math.round(this.frame.height / 2);
178    }
179    for (let index = 0; index < this._cpuUsage.length; index++) {
180      let cpuUsageItem = this._cpuUsage[index];
181      const color = interpolateColorBrightness(
182        ColorUtils.FUNC_COLOR_B[cpuUsageItem.cpu % ColorUtils.FUNC_COLOR_B.length],
183        cpuUsageItem.rate
184      );
185      this.context2D.fillStyle = `rgb(${color[0]}, ${color[1]}, ${color[2]})`;
186      this.context2D.globalAlpha = cpuUsageItem.rate;
187      this.context2D.fillRect(
188        this.frame.x + miniWidth * cpuUsageItem.ro,
189        this.frame.y + cpuUsageItem.cpu * miniHeight,
190        miniWidth,
191        miniHeight
192      );
193    }
194  }
195
196  draw(discardNotify: boolean = false): void {
197    this.context2D.clearRect(
198      this.frame.x - MarkPadding,
199      this.frame.y,
200      this.frame.width + MarkPadding * 2,
201      this.frame.height
202    );
203    this.context2D.beginPath();
204    if (this._cpuUsage.length > 0) {
205      this.drawCpuUsage();
206      this.context2D.globalAlpha = 0;
207    } else {
208      this.context2D.globalAlpha = 1;
209    }
210    //绘制选中区域
211    if (this.drawMark) {
212      this.drawSelectionRange();
213    }
214    if (this.notifyHandler) {
215      this.range.startX = this.rangeRect.x;
216      this.range.endX = this.rangeRect.x + this.rangeRect.width;
217      this.range.startNS = (this.range.startX * this.range.totalNS) / (this.canvas?.clientWidth || 0);
218      this.range.endNS = (this.range.endX * this.range.totalNS) / (this.canvas?.clientWidth || 0);
219      this.calculateScale();
220      this.updateRange();
221      if (!discardNotify) {
222        this.notifyHandler(this.range);
223      }
224    }
225  }
226
227  private updateRange(): void {
228    let tempNs = 0;
229    let rangeStartX = 0;
230    let rangeYu = this.range.startNS % this.scale;
231    let rangeRealW = (this.scale * this.frame.width) / (this.range.endNS - this.range.startNS);
232    if (this.range.xsTxt) {
233      this.range.xsTxt.length = 0;
234    } else {
235      this.range.xsTxt = [];
236    }
237    if (this.range.xs) {
238      this.range.xs.length = 0;
239    } else {
240      this.range.xs = [];
241    }
242    this.range.scale = this.scale;
243    if (rangeYu !== 0) {
244      let firstNodeWidth = ((this.scale - rangeYu) / this.scale) * rangeRealW;
245      rangeStartX += firstNodeWidth;
246      tempNs += this.scale - rangeYu;
247      this.range.xs.push(rangeStartX);
248      this.range.xsTxt.push(ns2UnitS(tempNs + this.range.startNS, this.scale));
249    }
250    while (tempNs < this.range.endNS - this.range.startNS) {
251      rangeStartX += rangeRealW;
252      tempNs += this.scale;
253      this.range.xs.push(rangeStartX);
254      this.range.xsTxt.push(ns2UnitS(tempNs + this.range.startNS, this.scale));
255    }
256  }
257
258  private calculateScale(): void {
259    let l20 = (this.range.endNS - this.range.startNS) / 20;
260    let minScale = 0;
261    let maxScale = 0;
262    let weight = 0;
263    for (let scalesIndex = 0; scalesIndex < this.scales.length; scalesIndex++) {
264      if (this.scales[scalesIndex] > l20) {
265        if (scalesIndex > 0) {
266          minScale = this.scales[scalesIndex - 1];
267        } else {
268          minScale = 0;
269        }
270        maxScale = this.scales[scalesIndex];
271        weight = ((l20 - minScale) * 1.0) / (maxScale - minScale);
272        if (weight > 0.243) {
273          this.scale = maxScale;
274        } else {
275          this.scale = minScale;
276        }
277        break;
278      }
279    }
280    if (this.scale === 0) {
281      this.scale = this.scales[0];
282    }
283  }
284
285  private drawSelectionRange(): void {
286    this.context2D.fillStyle = window.getComputedStyle(this.canvas!, null).getPropertyValue('background-color');
287    this.rangeRect.x = this.markAObj.frame.x < this.markBObj.frame.x ? this.markAObj.frame.x : this.markBObj.frame.x;
288    this.rangeRect.width = Math.abs(this.markBObj.frame.x - this.markAObj.frame.x);
289    this.context2D.fillRect(this.rangeRect.x, this.rangeRect.y, this.rangeRect.width, this.rangeRect.height);
290    this.context2D.globalAlpha = 1;
291    this.context2D.globalAlpha = 0.5;
292    this.context2D.fillStyle = '#999999';
293    // -----------------------绘制选择的阴影高度---------------------
294    if (sessionStorage.getItem('expand') === 'true') {
295      //展开
296      this.context2D.fillRect(
297        this.frame.x,
298        this.frame.y,
299        this.rangeRect.x,
300        this.rangeRect.height + Number(sessionStorage.getItem('foldHeight'))
301      );
302      this.context2D.fillRect(
303        this.rangeRect.x + this.rangeRect.width,
304        this.frame.y,
305        this.frame.width - this.rangeRect.width,
306        this.rangeRect.height + Number(sessionStorage.getItem('foldHeight'))
307      );
308    } else if (sessionStorage.getItem('expand') === 'false') {
309      this.context2D.fillRect(this.frame.x, this.frame.y, this.rangeRect.x, this.rangeRect.height);
310      this.context2D.fillRect(
311        this.rangeRect.x + this.rangeRect.width,
312        this.frame.y,
313        this.frame.width - this.rangeRect.width,
314        this.rangeRect.height
315      );
316    }
317    this.context2D.globalAlpha = 1;
318    this.context2D.closePath();
319    this.markAObj.draw();
320    this.markBObj.draw();
321  }
322
323  getScale(): number {
324    return this.scale;
325  }
326
327  mouseDown(mouseEventDown: MouseEvent): void {
328    let mouseDownX = mouseEventDown.offsetX - (this.canvas?.offsetLeft || 0);
329    let mouseDownY = mouseEventDown.offsetY - (this.canvas?.offsetTop || 0);
330    this.isMouseDown = true;
331    this.mouseDownOffsetX = mouseDownX;
332    if (this.markAObj.isHover) {
333      this.movingMark = this.markAObj;
334      this.mouseDownMovingMarkX = this.movingMark.frame.x || 0;
335    } else if (this.markBObj.isHover) {
336      this.movingMark = this.markBObj;
337      this.mouseDownMovingMarkX = this.movingMark.frame.x || 0;
338    } else {
339      this.movingMark = null;
340    }
341    if (this.rangeRect.containsWithPadding(mouseDownX, mouseDownY, 5, 0)) {
342      this.isMovingRange = true;
343      this.markAX = this.markAObj.frame.x;
344      this.markBX = this.markBObj.frame.x;
345      document.body.style.cursor = 'move';
346    } else if (
347      this.frame.containsWithMargin(mouseDownX, mouseDownY, 20, 0, 0, 0) &&
348      !this.rangeRect.containsWithMargin(mouseDownX, mouseDownY, 0, MarkPadding, 0, MarkPadding)
349    ) {
350      this.isNewRange = true;
351    }
352  }
353
354  mouseUp(ev: MouseEvent): void {
355    this.isMouseDown = false;
356    this.isMovingRange = false;
357    this.isNewRange = false;
358    this.movingMark = null;
359  }
360
361  mouseMove(ev: MouseEvent, trace: SpSystemTrace): void {
362    this.range.refresh = false;
363    let moveX = ev.offsetX - (this.canvas?.offsetLeft || 0);
364    let moveY = ev.offsetY - (this.canvas?.offsetTop || 0);
365    this.centerXPercentage = moveX / (this.canvas?.clientWidth || 0);
366    if (this.centerXPercentage <= 0) {
367      this.centerXPercentage = 0;
368    } else if (this.centerXPercentage >= 1) {
369      this.centerXPercentage = 1;
370    }
371    let maxX = this.canvas?.clientWidth || 0;
372    if (this.markAObj.inspectionFrame.contains(moveX, moveY)) {
373      this.markAObj.isHover = true;
374    } else if (this.markBObj.inspectionFrame.contains(moveX, moveY)) {
375      this.markBObj.isHover = true;
376    } else {
377      this.markAObj.isHover = false;
378      this.markBObj.isHover = false;
379    }
380    this.handleMovingMark(moveX, moveY, maxX, trace);
381    this.handleMovingFresh(moveX, maxX);
382  }
383
384  private handleMovingFresh(moveX: number, maxX: number): void {
385    if (this.isMovingRange && this.isMouseDown) {
386      let result = moveX - this.mouseDownOffsetX;
387      let mA = result + this.markAX;
388      let mB = result + this.markBX;
389      if (mA >= 0 && mA <= maxX) {
390        this.markAObj.frame.x = mA;
391      } else if (mA < 0) {
392        this.markAObj.frame.x = 0;
393      } else {
394        this.markAObj.frame.x = maxX;
395      }
396      this.markAObj.inspectionFrame.x = this.markAObj.frame.x - MarkPadding;
397      if (mB >= 0 && mB <= maxX) {
398        this.markBObj.frame.x = mB;
399      } else if (mB < 0) {
400        this.markBObj.frame.x = 0;
401      } else {
402        this.markBObj.frame.x = maxX;
403      }
404      this.markBObj.inspectionFrame.x = this.markBObj.frame.x - MarkPadding;
405      this.recordMovingS();
406      requestAnimationFrame(() => {
407        this.draw();
408        this.range.refresh = false;
409        this.delayDraw();
410      });
411    } else if (this.isNewRange) {
412      this.markAObj.frame.x = this.mouseDownOffsetX;
413      this.markAObj.inspectionFrame.x = this.mouseDownOffsetX - MarkPadding;
414      if (moveX >= 0 && moveX <= maxX) {
415        this.markBObj.frame.x = moveX;
416      } else if (moveX < 0) {
417        this.markBObj.frame.x = 0;
418      } else {
419        this.markBObj.frame.x = maxX;
420      }
421      this.markBObj.inspectionFrame.x = this.markBObj.frame.x - MarkPadding;
422      this.recordMovingS();
423      requestAnimationFrame(() => {
424        this.draw();
425        this.range.refresh = false;
426        this.delayDraw();
427      });
428    }
429  }
430
431  private handleMovingMark(moveX: number, moveY: number, maxX: number, trace: SpSystemTrace): void {
432    if (this.movingMark) {
433      let result = moveX - this.mouseDownOffsetX + this.mouseDownMovingMarkX;
434      if (result >= 0 && result <= maxX) {
435        this.movingMark.frame.x = result;
436      } else if (result < 0) {
437        this.movingMark.frame.x = 0;
438      } else {
439        this.movingMark.frame.x = maxX;
440      }
441      this.movingMark.inspectionFrame.x = this.movingMark.frame.x - MarkPadding;
442      this.recordMovingS();
443      requestAnimationFrame(() => {
444        this.draw();
445        this.range.refresh = false;
446        this.delayDraw();
447      });
448    } else if (this.rangeRect.containsWithPadding(moveX, moveY, MarkPadding, 0)) {
449      trace.style.cursor = 'move';
450      document.body.style.cursor = 'move';
451    } else if (
452      this.frame.containsWithMargin(moveX, moveY, 20, 0, 0, 0) &&
453      !this.rangeRect.containsWithMargin(moveX, moveY, 0, MarkPadding, 0, MarkPadding)
454    ) {
455      trace.style.cursor = 'crosshair';
456      document.body.style.cursor = 'crosshair';
457    }
458  }
459
460  recordMovingS(): void {
461    if (this.animaStartTime === undefined) {
462      let dat = new Date();
463      dat.setTime(dat.getTime() - 400);
464      this.animaStartTime = dat.getTime();
465    }
466    this.currentDuration = new Date().getTime() - this.animaStartTime;
467    this.setCacheInterval();
468    this.range.refresh = this.cacheInterval.flag;
469  }
470
471  setCacheInterval(): void {
472    if (Math.trunc(this.currentDuration / this.cacheInterval.interval) !== this.cacheInterval.value) {
473      this.cacheInterval.flag = true;
474      this.cacheInterval.value = Math.trunc(this.currentDuration / this.cacheInterval.interval);
475    } else {
476      this.cacheInterval.flag = false;
477    }
478  }
479
480  delayDraw(): void {
481    if (this.delayTimer) {
482      // @ts-ignore
483      clearTimeout(this.delayTimer);
484    }
485    this.delayTimer = setTimeout(() => {
486      this.range.refresh = true;
487      this.draw();
488      this.range.refresh = false;
489      this.animaStartTime = undefined;
490    }, this.cacheInterval.interval + 50);
491  }
492
493  mouseOut(ev: MouseEvent): void {
494    this.movingMark = null;
495  }
496
497  fillX(): void {
498    if (this.range.endNS < 0) {
499      this.range.endNS = 0;
500    }
501    if (this.range.startNS < 0) {
502      this.range.startNS = 0;
503    }
504    if (this.range.endNS > this.range.totalNS) {
505      this.range.endNS = this.range.totalNS;
506    }
507    if (this.range.startNS > this.range.totalNS) {
508      this.range.startNS = this.range.totalNS;
509    }
510    this.range.startX = (this.range.startNS * (this.canvas?.clientWidth || 0)) / this.range.totalNS;
511    this.range.endX = (this.range.endNS * (this.canvas?.clientWidth || 0)) / this.range.totalNS;
512    this.markAObj.frame.x = this.range.startX;
513    this.markAObj.inspectionFrame.x = this.markAObj.frame.x - MarkPadding;
514    this.markBObj.frame.x = this.range.endX;
515    this.markBObj.inspectionFrame.x = this.markBObj.frame.x - MarkPadding;
516  }
517
518  setRangeNS(startNS: number, endNS: number): void {
519    this.range.startNS = startNS;
520    this.range.endNS = endNS;
521    this.fillX();
522    this.draw();
523  }
524
525  getRange(): TimeRange {
526    return this.range;
527  }
528
529  cancelPressFrame(): void {
530    if (this.pressFrameIdA !== -1) {
531      cancelAnimationFrame(this.pressFrameIdA);
532    }
533    if (this.pressFrameIdD !== -1) {
534      cancelAnimationFrame(this.pressFrameIdD);
535    }
536    if (this.pressFrameIdW !== -1) {
537      cancelAnimationFrame(this.pressFrameIdW);
538    }
539    if (this.pressFrameIdS !== -1) {
540      cancelAnimationFrame(this.pressFrameIdS);
541    }
542    if (this.pressFrameIdF !== -1) {
543      cancelAnimationFrame(this.pressFrameIdF);
544    }
545    if (this.pressFrameIdFlagIntoView !== -1) {
546      cancelAnimationFrame(this.pressFrameIdFlagIntoView);
547    }
548  }
549
550  cancelUpFrame(): void {
551    if (this.upFrameIdA !== -1) {
552      cancelAnimationFrame(this.upFrameIdA);
553    }
554    if (this.upFrameIdD !== -1) {
555      cancelAnimationFrame(this.upFrameIdD);
556    }
557    if (this.upFrameIdW !== -1) {
558      cancelAnimationFrame(this.upFrameIdW);
559    }
560    if (this.upFrameIdS !== -1) {
561      cancelAnimationFrame(this.upFrameIdS);
562    }
563  }
564
565  cancelTimeOut: unknown = undefined;
566  isKeyPress: boolean = false;
567
568  keyPress(keyboardEvent: KeyboardEvent, currentSlicesTime?: CurrentSlicesTime): void {
569    //第一个按键或者最后一个按下的和当前按键不一致
570    if (
571      this.pressedKeys.length === 0 ||
572      this.pressedKeys[this.pressedKeys.length - 1] !== keyboardEvent.key.toLocaleLowerCase()
573    ) {
574      this.pressedKeys = this.pressedKeys.filter((v) => {
575        return v !== keyboardEvent.key.toLocaleLowerCase();
576      });
577      this.setCacheInterval();
578      this.range.refresh = this.cacheInterval.flag;
579      if (currentSlicesTime) {
580        this.currentSlicesTime = currentSlicesTime;
581      }
582      this.isMouseDown = false;
583      this.isMovingRange = false;
584      this.isNewRange = false;
585      this.movingMark = null;
586      this.cancelPressFrame();
587      this.cancelUpFrame();
588      this.pressedKeys.push(keyboardEvent.key.toLocaleLowerCase());
589      this.animaStartTime = new Date().getTime(); //记录按下的时间
590      // @ts-ignore
591      this.keyboardKeyPressMap[this.pressedKeys[this.pressedKeys.length - 1]]?.bind(this)();
592    }
593  }
594
595  scrollFlagIntoView(): void {
596    let animFlagIntoView = (): void => {
597      let clientWidth: number = this.canvas?.clientWidth || 0;
598      let flagTime: number = this.currentSlicesTime.startTime!;
599      let unitValue: number = Number(((this.range.endNS - this.range.startNS) / clientWidth).toFixed(2));
600      if (flagTime > this.range.endNS) {
601        let offsetNs = 20 * unitValue + flagTime - this.range.endNS;
602        this.range.startNS += offsetNs;
603        this.range.endNS += offsetNs;
604      } else if (flagTime < this.range.startNS) {
605        let offsetNs = this.range.startNS - flagTime + 20 * unitValue;
606        this.range.startNS -= offsetNs;
607        this.range.endNS -= offsetNs;
608      }
609      this.fillX();
610      this.draw();
611      this.range.refresh = false;
612      this.pressFrameIdFlagIntoView = requestAnimationFrame(animFlagIntoView);
613    };
614    this.pressFrameIdFlagIntoView = requestAnimationFrame(animFlagIntoView);
615  }
616
617  keyPressF(): void {
618    let animF = (): void => {
619      let clientWidth = this.canvas?.clientWidth || 0;
620      let midX = Math.round(clientWidth / 2);
621      let startTime = 0;
622      let endTime = 0;
623      this.rulerW = this.canvas!.offsetWidth;
624      if (this.currentSlicesTime.startTime) {
625        startTime = this.currentSlicesTime.startTime;
626      } else {
627        return;
628      }
629      if (this.currentSlicesTime.endTime) {
630        endTime = this.currentSlicesTime.endTime;
631      } else {
632        return;
633      }
634      if (startTime === endTime) {
635        let midNs = (this.range.endNS - this.range.startNS) / 2;
636        if (startTime > midNs && startTime - midNs < this.range.totalNS - this.range.endNS) {
637          this.range.startNS += startTime - midNs;
638          this.range.endNS += startTime - midNs;
639        } else if (startTime < midNs && midNs - startTime < this.range.startNS) {
640          this.range.startNS -= midNs - startTime;
641          this.range.endNS -= midNs - startTime;
642        } else if (startTime > midNs && startTime - midNs > this.range.totalNS - this.range.endNS) {
643          this.range.startNS = 2 * startTime - this.range.totalNS;
644          this.range.endNS = this.range.totalNS;
645        } else if (startTime < midNs && midNs - startTime > this.range.startNS) {
646          this.range.startNS = 0;
647          this.range.endNS = 2 * startTime;
648        }
649      } else {
650        let startX = midX - 150;
651        let endX = midX + 150;
652        this.range.startNS = (endX * startTime - startX * endTime) / (endX - startX);
653        this.range.endNS = (this.rulerW * (endTime - this.range.startNS) + this.range.startNS * endX) / endX;
654      }
655      this.fillX();
656      this.draw();
657      this.range.refresh = false;
658      this.pressFrameIdF = requestAnimationFrame(animF);
659    };
660    this.pressFrameIdF = requestAnimationFrame(animF);
661  }
662
663  fixReg = 76; //速度上线
664  f = 11; //加速度系数,值越小加速度越大
665
666  keyPressW(): void {
667    let animW = (): void => {
668      if (this.scale === 50) {
669        this.fillX();
670        this.range.refresh = true;
671        this.notifyHandler(this.range);
672        this.range.refresh = false;
673        return;
674      }
675      this.animaStartTime = this.animaStartTime || Date.now();
676      this.currentDuration = (Date.now() - this.animaStartTime!) / this.f; //reg
677      if (this.currentDuration >= this.fixReg) {
678        this.currentDuration = this.fixReg;
679      }
680      let bb = Math.tan((Math.PI / 180) * this.currentDuration);
681      this.range.startNS += this.centerXPercentage * bb * this.scale;
682      this.range.endNS -= (1 - this.centerXPercentage) * bb * this.scale;
683      this.fillX();
684      this.draw();
685      this.range.refresh = false;
686      this.pressFrameIdW = requestAnimationFrame(animW);
687    };
688    this.pressFrameIdW = requestAnimationFrame(animW);
689  }
690
691  keyPressS(): void {
692    let animS = (): void => {
693      if (this.range.startNS <= 0 && this.range.endNS >= this.range.totalNS) {
694        this.fillX();
695        this.range.refresh = true;
696        this.notifyHandler(this.range);
697        this.range.refresh = false;
698        return;
699      }
700      this.animaStartTime = this.animaStartTime || Date.now();
701      this.currentDuration = (Date.now() - this.animaStartTime!) / this.f;
702      if (this.currentDuration >= this.fixReg) {
703        this.currentDuration = this.fixReg;
704      }
705      let bb = Math.tan((Math.PI / 180) * this.currentDuration);
706      this.range.startNS -= this.centerXPercentage * bb * this.scale;
707      this.range.endNS += (1 - this.centerXPercentage) * bb * this.scale;
708      this.fillX();
709      this.draw();
710      this.range.refresh = false;
711      this.pressFrameIdS = requestAnimationFrame(animS);
712    };
713    this.pressFrameIdS = requestAnimationFrame(animS);
714  }
715
716  keyPressA(): void {
717    let animA = (): void => {
718      if (this.range.startNS <= 0) {
719        this.fillX();
720        this.range.refresh = true;
721        this.notifyHandler(this.range);
722        this.range.refresh = false;
723        return;
724      }
725      this.animaStartTime = this.animaStartTime || Date.now();
726      this.currentDuration = (Date.now() - this.animaStartTime!) / this.f;
727      if (this.currentDuration >= this.fixReg) {
728        this.currentDuration = this.fixReg;
729      }
730      let bb = Math.tan((Math.PI / 180) * this.currentDuration);
731      let s = this.scale * bb;
732      this.range.startNS -= s;
733      this.range.endNS -= s;
734      this.fillX();
735      this.draw();
736      this.range.refresh = false;
737      this.pressFrameIdA = requestAnimationFrame(animA);
738    };
739    this.pressFrameIdA = requestAnimationFrame(animA);
740  }
741
742  keyPressD(): void {
743    let animD = (): void => {
744      if (this.range.endNS >= this.range.totalNS) {
745        this.fillX();
746        this.range.refresh = true;
747        this.notifyHandler(this.range);
748        this.range.refresh = false;
749        return;
750      }
751      this.animaStartTime = this.animaStartTime || Date.now();
752      this.currentDuration = (Date.now() - this.animaStartTime!) / this.f;
753      if (this.currentDuration >= this.fixReg) {
754        this.currentDuration = this.fixReg;
755      }
756      let bb = Math.tan((Math.PI / 180) * this.currentDuration);
757      let s = this.scale * bb;
758      this.range.startNS += s;
759      this.range.endNS += s;
760      this.fillX();
761      this.draw();
762      this.range.refresh = false;
763      this.pressFrameIdD = requestAnimationFrame(animD);
764    };
765    this.pressFrameIdD = requestAnimationFrame(animD);
766  }
767
768  keyboardKeyPressMap: unknown = {
769    w: this.keyPressW,
770    s: this.keyPressS,
771    a: this.keyPressA,
772    d: this.keyPressD,
773    f: this.keyPressF,
774    ']': this.keyPressF,
775    '[': this.keyPressF,
776    '.': this.scrollFlagIntoView,
777    ',': this.scrollFlagIntoView,
778  };
779
780  keyboardKeyUpMap: unknown = {
781    w: this.keyUpW,
782    s: this.keyUpS,
783    a: this.keyUpA,
784    d: this.keyUpD,
785  };
786
787  keyUp(ev: KeyboardEvent): void {
788    this.cacheInterval.value = 0;
789    if (this.pressedKeys.length > 0) {
790      let number = this.pressedKeys.findIndex((value) => value === ev.key.toLocaleLowerCase());
791      if (number === this.pressedKeys.length - 1) {
792        this.animaStartTime = undefined;
793        this.cancelPressFrame();
794        // @ts-ignore
795        this.keyboardKeyUpMap[ev.key]?.bind(this)();
796      }
797      if (number !== -1) {
798        this.pressedKeys.splice(number, 1);
799      }
800    }
801  }
802
803  keyUpW(): void {
804    let startTime = new Date().getTime();
805    let animW = (): void => {
806      if (this.scale === 50) {
807        this.fillX();
808        this.keyUpEnd();
809        return;
810      }
811      let dur = new Date().getTime() - startTime;
812      if (dur > 150) {
813        dur = 150;
814      }
815      let offset = Math.tan((Math.PI / 180) * (150 - dur) * 0.2) * this.scale;
816      this.range.startNS += this.centerXPercentage * offset;
817      this.range.endNS -= (1 - this.centerXPercentage) * offset;
818      this.fillX();
819      this.draw();
820      this.range.refresh = false;
821      if (dur < 150) {
822        this.upFrameIdW = requestAnimationFrame(animW);
823      } else {
824        this.keyUpEnd();
825      }
826    };
827    this.upFrameIdW = requestAnimationFrame(animW);
828  }
829
830  keyUpS(): void {
831    let startTime = new Date().getTime();
832    let animS = (): void => {
833      if (this.range.startNS <= 0 && this.range.endNS >= this.range.totalNS) {
834        this.fillX();
835        this.keyUpEnd();
836        return;
837      }
838      let dur = new Date().getTime() - startTime;
839      if (dur > 150) {
840        dur = 150;
841      }
842      let offset = Math.tan((Math.PI / 180) * (150 - dur) * 0.2) * this.scale;
843      this.range.startNS -= this.centerXPercentage * offset;
844      this.range.endNS += (1 - this.centerXPercentage) * offset;
845      this.fillX();
846      this.draw();
847      this.range.refresh = false;
848      if (dur < 150) {
849        this.upFrameIdS = requestAnimationFrame(animS);
850      } else {
851        this.keyUpEnd();
852      }
853    };
854    this.upFrameIdS = requestAnimationFrame(animS);
855  }
856
857  keyUpA(): void {
858    let startTime = new Date().getTime();
859    let animA = (): void => {
860      if (this.range.startNS <= 0) {
861        this.fillX();
862        this.keyUpEnd();
863        return;
864      }
865      let dur = new Date().getTime() - startTime;
866      if (dur > 150) {
867        dur = 150;
868      }
869      let offset = Math.tan((Math.PI / 180) * (150 - dur) * 0.15) * this.scale;
870      this.range.startNS -= offset;
871      this.range.endNS -= offset;
872      this.fillX();
873      this.draw();
874      this.range.refresh = false;
875      if (dur < 150) {
876        this.upFrameIdA = requestAnimationFrame(animA);
877      } else {
878        this.keyUpEnd();
879      }
880    };
881    this.upFrameIdA = requestAnimationFrame(animA);
882  }
883
884  keyUpEnd(): void {
885    this.range.refresh = true;
886    window.isLastFrame = true;
887    this.notifyHandler(this.range);
888    this.range.refresh = false;
889    window.isLastFrame = false;
890  }
891
892  keyUpD(): void {
893    let startTime = new Date().getTime();
894    let animD = (): void => {
895      if (this.range.endNS >= this.range.totalNS) {
896        this.keyUpEnd();
897        return;
898      }
899      let dur = new Date().getTime() - startTime;
900      let offset = Math.tan((Math.PI / 180) * (150 - dur) * 0.15) * this.scale;
901      this.range.startNS += offset;
902      this.range.endNS += offset;
903      this.fillX();
904      this.draw();
905      this.range.refresh = false;
906      if (dur < 150) {
907        this.upFrameIdD = requestAnimationFrame(animD);
908      } else {
909        this.keyUpEnd();
910      }
911    };
912    this.upFrameIdD = requestAnimationFrame(animD);
913  }
914
915  translate(distance: number): void {
916    const rangeDur = this.range.endNS - this.range.startNS;
917    const time = (distance / this.canvas!.width) * rangeDur;
918    if (
919      this.range.startNS < 0 ||
920      this.range.endNS < 0 ||
921      this.range.startNS > this.range.totalNS ||
922      this.range.endNS > this.range.totalNS
923    ) {
924      return;
925    }
926    this.range.startNS -= time;
927    this.range.endNS -= time;
928    this.fillX();
929    this.draw();
930    this.range.refresh = true;
931    this.notifyHandler(this.range);
932    this.range.refresh = false;
933  }
934}
935