• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1import {ns2s, Rect} from "./ProcedureWorkerCommon.js";
2import {CpuStruct} from "../bean/CpuStruct.js";
3import {ColorUtils} from "../component/trace/base/ColorUtils.js";
4
5let timeRuler: TimeRuler | undefined;
6let rangeRuler: RangeRuler | undefined;
7let sportRuler: SportRuler | undefined;
8let offsetTop: number = 0;
9let offsetLeft: number = 0;
10
11// @ts-ignore
12export function timeline(canvas: OffscreenCanvas, ctx: OffscreenCanvasRenderingContext2D, startNS: number,
13                         endNS: number, totalNS: number, frame: Rect, keyPressCode: any, keyUpCode: any,
14                         mouseDown: any, mouseUp: any, mouseMove: any, mouseOut: any,
15                         _offsetLeft: number, _offsetTop: number, changeHandler: Function) {
16    offsetLeft = _offsetLeft;
17    offsetTop = _offsetTop;
18    if (timeRuler == undefined) {
19        timeRuler = new TimeRuler(canvas, ctx, new Rect(0, 0, frame.width, 20), totalNS);
20    }
21    if (!sportRuler) {
22        sportRuler = new SportRuler(canvas, ctx, new Rect(0, 100.5, frame.width, frame.height - 100));
23    }
24    if (!rangeRuler) {
25        rangeRuler = new RangeRuler(canvas, ctx!, new Rect(0, 25, frame.width, 75), {
26            startX: 0,
27            endX: frame.width,
28            startNS: 0,
29            endNS: totalNS,
30            totalNS: totalNS,
31            xs: [],
32            xsTxt: []
33        }, (a) => {
34            if (sportRuler) {
35                sportRuler.range = a;
36            }
37            changeHandler(a);
38        });
39    }
40    ;
41    rangeRuler.frame.width = frame.width;
42    sportRuler.frame.width = frame.width;
43    timeRuler.frame.width = frame.width;
44    if (keyPressCode) {
45        rangeRuler.keyPress(keyPressCode);
46    } else if (keyUpCode) {
47        rangeRuler.keyUp(keyUpCode);
48    } else if (mouseDown) {
49        rangeRuler.mouseDown(mouseDown);
50    } else if (mouseUp) {
51        rangeRuler.mouseUp(mouseUp);
52    } else if (mouseMove) {
53        rangeRuler.mouseMove(mouseMove);
54    } else if (mouseOut) {
55        rangeRuler.mouseOut(mouseOut);
56    } else {
57        timeRuler.draw();
58        rangeRuler.draw();
59    }
60}
61
62export abstract class Graph {
63    // @ts-ignore
64    c: OffscreenCanvasRenderingContext2D;
65    // @ts-ignore
66    canvas: OffscreenCanvas | undefined | null;
67    frame: Rect;
68
69    // @ts-ignore
70    protected constructor(canvas: OffscreenCanvas | undefined | null, c: OffscreenCanvasRenderingContext2D, frame: Rect) {
71        this.canvas = canvas;
72        this.frame = frame;
73        this.c = c;
74    }
75
76    abstract draw(): void;
77}
78
79export class TimeRuler extends Graph {
80    totalNS: number
81    private stepSmall: number;
82    private step: number;
83    private stepNS: number;
84
85    // @ts-ignore
86    constructor(canvas: OffscreenCanvas | undefined | null, c: OffscreenCanvasRenderingContext2D, frame: Rect, totalNS: number = 10_000_000_000) {
87        super(canvas, c, frame)
88        this.totalNS = totalNS;
89        this.step = this.frame.width / 10;
90        this.stepSmall = this.frame.width / 100;
91        this.stepNS = this.totalNS / 10;
92    }
93
94    draw() {
95        this.step = this.frame.width / 10;
96        this.stepSmall = this.frame.width / 100;
97        this.stepNS = this.totalNS / 10;
98        this.c.clearRect(this.frame.x, this.frame.y, this.frame.width, this.frame.height)
99        this.c.beginPath();
100        this.c.strokeStyle = "#999"
101        this.c.lineWidth = 1;
102        for (let i = 0; i <= 10; i++) {
103            let x = Math.floor(i * this.step) + this.frame.x;
104            this.c.moveTo(x, 0);
105            this.c.lineTo(x, this.frame.height);
106            if (i == 10) break;
107            for (let j = 1; j < 10; j++) {
108                this.c.moveTo(x + Math.floor(j * this.stepSmall), 0);
109                this.c.lineTo(x + Math.floor(j * this.stepSmall), this.frame.height / 4);
110            }
111            this.c.fillStyle = '#999'
112            this.c.fillText(`${ns2s(i * this.stepNS)}`, x + 5, this.frame.height - 1)
113        }
114        this.c.stroke();
115        this.c.closePath();
116    }
117}
118
119/**
120 * SportRuler
121 */
122export class SportRuler extends Graph {
123    private _range: TimeRange = {} as TimeRange;
124    private rangeFlag = new Flag(0, 0, 0, 0, 0);
125    public flagList: Array<Flag> = [];
126    private ruler_w = 1022;
127    public static rulerFlagObj: Flag | null = null;
128
129    public flagListIdx: number | null = null
130
131    public obj = [{x: 3}, {x: 2}];
132
133    modifyFlagList(type: string, flag: any = {}) {
134        if (type == "amend") {
135            if (flag.text && this.flagListIdx !== null) {
136                this.flagList[this.flagListIdx].text = flag.text
137            }
138            if (flag.color && this.flagListIdx !== null) {
139                this.flagList[this.flagListIdx].color = flag.color
140            }
141        } else if (type == "remove") {
142            if (this.flagListIdx !== null) {
143                this.flagList.splice(this.flagListIdx, 1)
144            }
145        }
146        this.draw()
147    }
148
149    get range(): TimeRange {
150        return this._range;
151    }
152
153    set range(value: TimeRange) {
154        this._range = value;
155        this.draw()
156    }
157
158    lineColor: string | null = null;
159
160    // @ts-ignore
161    constructor(canvas: OffscreenCanvas | undefined | null, c: OffscreenCanvasRenderingContext2D, frame: Rect) {
162        super(canvas, c, frame)
163    }
164
165    draw(): void {
166        this.ruler_w = this.frame.width;
167        this.c.clearRect(this.frame.x, this.frame.y, this.frame.width, this.frame.height)
168        this.c.beginPath();
169        this.lineColor = "#dadada";//window.getComputedStyle(this.canvas!, null).getPropertyValue("color");
170        this.c.strokeStyle = this.lineColor //"#dadada"
171        this.c.lineWidth = 1;
172        this.c.moveTo(this.frame.x, this.frame.y)
173        this.c.lineTo(this.frame.x + this.frame.width, this.frame.y)
174        this.c.stroke();
175        this.c.closePath();
176        this.c.beginPath();
177        this.c.lineWidth = 3;
178        this.c.strokeStyle = "#999999"
179        this.c.moveTo(this.frame.x, this.frame.y)
180        this.c.lineTo(this.frame.x, this.frame.y + this.frame.height)
181        this.c.stroke();
182        this.c.closePath();
183        this.c.beginPath();
184        this.c.lineWidth = 1;
185        this.c.strokeStyle = this.lineColor;//"#999999"
186        this.c.fillStyle = '#999999'
187        this.c.font = '8px sans-serif'
188        this.range.xs?.forEach((it, i) => {
189            this.c.moveTo(it, this.frame.y)
190            this.c.lineTo(it, this.frame.y + this.frame.height)
191            this.c.fillText(`+${this.range.xsTxt[i]}`, it + 3, this.frame.y + 12)
192        })
193
194        this.c.stroke();
195        this.c.closePath();
196    }
197
198    // drawTheFlag
199    drawTheFlag(x: number, color: string = "#999999", isFill: boolean = false, text: string = "") {
200        this.c.beginPath();
201        this.c.fillStyle = color;
202        this.c.strokeStyle = color;
203        this.c.moveTo(x, 125);
204        this.c.lineTo(x + 10, 125);
205        this.c.lineTo(x + 10, 127);
206        this.c.lineTo(x + 18, 127);
207        this.c.lineTo(x + 18, 137);
208        this.c.lineTo(x + 10, 137);
209        this.c.lineTo(x + 10, 135);
210        this.c.lineTo(x + 2, 135);
211        this.c.lineTo(x + 2, 143);
212        this.c.lineTo(x, 143);
213        this.c.closePath()
214        if (isFill) {
215            this.c.fill()
216        }
217        this.c.stroke();
218
219
220        if (text !== "") {
221            this.c.font = "10px Microsoft YaHei"
222            const {width} = this.c.measureText(text);
223            this.c.fillStyle = 'rgba(255, 255, 255, 0.8)'; //
224            this.c.fillRect(x + 21, 132, width + 4, 12);
225            this.c.fillStyle = "black";
226            this.c.fillText(text, x + 23, 142);
227            this.c.stroke();
228        }
229    }
230
231    randomRgbColor() {
232        const letters = '0123456789ABCDEF';
233        let color = '#';
234        for (let i = 0; i < 6; i++) {
235            color += letters[Math.floor(Math.random() * 16)]
236        }
237        return color;
238    }
239
240    mouseUp(ev: MouseEvent) {
241    }
242
243    onFlagRangeEvent(flagObj: Flag, idx: number) {
244        SportRuler.rulerFlagObj = flagObj;
245        this.flagListIdx = idx;
246    }
247
248    mouseMove(ev: MouseEvent) {
249        let x = ev.offsetX - (offsetLeft || 0)
250        let y = ev.offsetY - (offsetTop || 0)
251        if (y >= 50 && y < 200) {
252            this.draw()
253            if (y >= 123 && y < 142 && x > 0) {
254                let onFlagRange = this.flagList.findIndex((flagObj: Flag) => {
255                    let flag_x = Math.round(this.ruler_w * (flagObj.time - this.range.startNS) / (this.range.endNS - this.range.startNS));
256                    return (x >= flag_x && x <= flag_x + 18)
257                });
258                if (onFlagRange == -1) {
259                } else {
260                }
261            } else {
262            }
263        }
264    }
265}
266
267const markPadding = 5;
268
269export class Mark extends Graph {
270    name: string | undefined
271    inspectionFrame: Rect
272    private _isHover: boolean = false
273
274    get isHover(): boolean {
275        return this._isHover;
276    }
277
278    set isHover(value: boolean) {
279        this._isHover = value;
280        if (value) {
281        } else {
282        }
283    }
284
285    // @ts-ignore
286    constructor(canvas: OffscreenCanvas | undefined | null, name: string, c: OffscreenCanvasRenderingContext2D, frame: Rect) {
287        super(canvas, c, frame);
288        this.name = name;
289        this.inspectionFrame = new Rect(frame.x - markPadding, frame.y, frame.width + markPadding * 2, frame.height)
290    }
291
292    draw(): void {
293        this.c.beginPath();
294        this.c.lineWidth = 7
295        this.c.strokeStyle = '#999999'
296        this.c.moveTo(this.frame.x, this.frame.y);
297        this.c.lineTo(this.frame.x, this.frame.y + this.frame.height / 3)
298        this.c.stroke();
299        this.c.lineWidth = 1
300        this.c.strokeStyle = '#999999'
301        this.c.moveTo(this.frame.x, this.frame.y);
302        this.c.lineTo(this.frame.x, this.frame.y + this.frame.height)
303        this.c.stroke();
304        this.c.closePath();
305    }
306}
307
308export interface TimeRange {
309    totalNS: number
310    startX: number
311    endX: number
312    startNS: number
313    endNS: number
314    xs: Array<number>
315    xsTxt: Array<string>
316}
317
318export class RangeRuler extends Graph {
319    public rangeRect: Rect
320    public markA: Mark
321    public markB: Mark
322    public range: TimeRange;
323    mouseDownOffsetX = 0
324    mouseDownMovingMarkX = 0
325    movingMark: Mark | undefined | null;
326    isMouseDown: boolean = false;
327    isMovingRange: boolean = false;
328    isNewRange: boolean = false;
329    markAX: number = 0;
330    markBX: number = 0;
331    isPress: boolean = false
332    pressFrameId: number = -1
333    currentDuration: number = 0
334    centerXPercentage: number = 0;
335    animaStartTime: number | undefined
336    animTime: number = 100;
337    p: number = 800;
338    private readonly notifyHandler: (r: TimeRange) => void;
339    private scale: number = 0;
340    private scales: Array<number> = [50, 100, 200, 500, 1_000, 2_000, 5_000, 10_000, 20_000, 50_000, 100_000, 200_000, 500_000,
341        1_000_000, 2_000_000, 5_000_000, 10_000_000, 20_000_000, 50_000_000, 100_000_000, 200_000_000, 500_000_000,
342        1_000_000_000, 2_000_000_000, 5_000_000_000, 10_000_000_000, 20_000_000_000, 50_000_000_000,
343        100_000_000_000, 200_000_000_000, 500_000_000_000];
344
345    // @ts-ignore
346    constructor(canvas: OffscreenCanvas | undefined | null, c: OffscreenCanvasRenderingContext2D, frame: Rect, range: TimeRange, notifyHandler: (r: TimeRange) => void) {
347        super(canvas, c, frame)
348        this.range = range;
349        this.notifyHandler = notifyHandler;
350        this.markA = new Mark(canvas, 'A', c, new Rect(range.startX, frame.y, 1, frame.height))
351        this.markB = new Mark(canvas, 'B', c, new Rect(range.endX, frame.y, 1, frame.height))
352        this.rangeRect = new Rect(range.startX, frame.y, range.endX - range.startX, frame.height)
353    }
354
355    private _cpuUsage: Array<{ cpu: number, ro: number, rate: number }> = []
356    set cpuUsage(value: Array<{ cpu: number, ro: number, rate: number }>) {
357        this._cpuUsage = value
358        this.draw();
359    }
360
361    drawCpuUsage() {
362        let maxNum = Math.round(this._cpuUsage.length / 100)
363        let miniHeight = Math.round(this.frame.height / CpuStruct.cpuCount);//每格高度
364        let miniWidth = Math.ceil(this.frame.width / 100);//每格宽度
365        for (let i = 0; i < this._cpuUsage.length; i++) {
366            //cpu: 0, ro: 0, rate: 0.987620037556431
367            let it = this._cpuUsage[i]
368            this.c.fillStyle = ColorUtils.MD_PALETTE[it.cpu]
369            this.c.globalAlpha = it.rate
370            this.c.fillRect(this.frame.x + miniWidth * it.ro, this.frame.y + it.cpu * miniHeight, miniWidth, miniHeight)
371        }
372    }
373
374    draw(discardNotify: boolean = false): void {
375        this.c.clearRect(this.frame.x - markPadding, this.frame.y, this.frame.width + markPadding * 2, this.frame.height)
376        this.c.beginPath();
377        if (this._cpuUsage.length > 0) {
378            this.drawCpuUsage()
379            this.c.globalAlpha = 0;
380        } else {
381            this.c.globalAlpha = 1;
382        }
383        this.c.fillStyle = "#ffffff";//window.getComputedStyle(this.canvas!, null).getPropertyValue("background-color")
384        this.rangeRect.x = this.markA.frame.x < this.markB.frame.x ? this.markA.frame.x : this.markB.frame.x
385        this.rangeRect.width = Math.abs(this.markB.frame.x - this.markA.frame.x)
386        this.c.fillRect(this.rangeRect.x, this.rangeRect.y, this.rangeRect.width, this.rangeRect.height)
387        this.c.globalAlpha = 1;
388        this.c.globalAlpha = .5;
389        this.c.fillStyle = "#999999"
390        this.c.fillRect(this.frame.x, this.frame.y, this.rangeRect.x, this.rangeRect.height)
391        this.c.fillRect(this.rangeRect.x + this.rangeRect.width, this.frame.y, this.frame.width - this.rangeRect.width, this.rangeRect.height)
392        this.c.globalAlpha = 1;
393        this.c.closePath();
394        this.markA.draw();
395        this.markB.draw();
396        if (this.notifyHandler) {
397            this.range.startX = this.rangeRect.x
398            this.range.endX = this.rangeRect.x + this.rangeRect.width
399            this.range.startNS = this.range.startX * this.range.totalNS / (this.frame.width || 0)
400            this.range.endNS = this.range.endX * this.range.totalNS / (this.frame.width || 0)
401            let l20 = (this.range.endNS - this.range.startNS) / 20;
402            let min = 0;
403            let max = 0;
404            let weight = 0;
405            for (let index = 0; index < this.scales.length; index++) {
406                if (this.scales[index] > l20) {
407                    if (index > 0) {
408                        min = this.scales[index - 1];
409                    } else {
410                        min = 0;
411                    }
412                    max = this.scales[index];
413                    weight = (l20 - min) * 1.0 / (max - min);
414                    if (weight > 0.243) {
415                        this.scale = max;
416                    } else {
417                        this.scale = min;
418                    }
419                    break;
420                }
421            }
422            if (this.scale == 0) {
423                this.scale = this.scales[0];
424            }
425            let tmpNs = 0;
426            let yu = this.range.startNS % this.scale;
427            let realW = (this.scale * this.frame.width) / (this.range.endNS - this.range.startNS);
428            let startX = 0;
429            if (this.range.xs) {
430                this.range.xs.length = 0
431            } else {
432                this.range.xs = []
433            }
434            if (this.range.xsTxt) {
435                this.range.xsTxt.length = 0
436            } else {
437                this.range.xsTxt = []
438            }
439            if (yu != 0) {
440                let firstNodeWidth = ((this.scale - yu) / this.scale * realW);
441                startX += firstNodeWidth;
442                tmpNs += yu;
443                this.range.xs.push(startX)
444                this.range.xsTxt.push(ns2s(tmpNs))
445            }
446            while (tmpNs < this.range.endNS - this.range.startNS) {
447                startX += realW;
448                tmpNs += this.scale;
449                this.range.xs.push(startX)
450                this.range.xsTxt.push(ns2s(tmpNs))
451            }
452            if (!discardNotify) {
453                this.notifyHandler(this.range)
454            }
455        }
456    }
457
458    mouseDown(ev: MouseEvent) {
459        let x = ev.offsetX - (offsetLeft || 0)
460        let y = ev.offsetY - (offsetTop || 0)
461        this.isMouseDown = true;
462        this.mouseDownOffsetX = x;
463        if (this.markA.isHover) {
464            this.movingMark = this.markA;
465            this.mouseDownMovingMarkX = this.movingMark.frame.x || 0
466        } else if (this.markB.isHover) {
467            this.movingMark = this.markB;
468            this.mouseDownMovingMarkX = this.movingMark.frame.x || 0
469        } else {
470            this.movingMark = null;
471        }
472        if (this.rangeRect.containsWithPadding(x, y, 5, 0)) {
473            this.isMovingRange = true;
474            this.markAX = this.markA.frame.x;
475            this.markBX = this.markB.frame.x;
476        } else if (this.frame.containsWithMargin(x, y, 20, 0, 0, 0) && !this.rangeRect.containsWithMargin(x, y, 0, markPadding, 0, markPadding)) {
477            this.isNewRange = true;
478        }
479    }
480
481    mouseUp(ev: MouseEvent) {
482        this.isMouseDown = false;
483        this.isMovingRange = false;
484        this.isNewRange = false;
485        this.movingMark = null;
486    }
487
488    mouseMove(ev: MouseEvent) {
489        let x = ev.offsetX - (offsetLeft || 0);
490        let y = ev.offsetY - (offsetTop || 0)
491        this.centerXPercentage = x / (this.frame.width || 0)
492        if (this.centerXPercentage <= 0) {
493            this.centerXPercentage = 0
494        } else if (this.centerXPercentage >= 1) {
495            this.centerXPercentage = 1
496        }
497        let maxX = this.frame.width || 0
498        if (this.markA.inspectionFrame.contains(x, y)) {
499            this.markA.isHover = true
500        } else if (this.markB.inspectionFrame.contains(x, y)) {
501            this.markB.isHover = true;
502        } else {
503            this.markA.isHover = false;
504            this.markB.isHover = false;
505        }
506        if (this.movingMark) {
507            let result = x - this.mouseDownOffsetX + this.mouseDownMovingMarkX;
508            if (result >= 0 && result <= maxX) {
509                this.movingMark.frame.x = result
510            } else if (result < 0) {
511                this.movingMark.frame.x = 0
512            } else {
513                this.movingMark.frame.x = maxX
514            }
515            this.movingMark.inspectionFrame.x = this.movingMark.frame.x - markPadding
516            requestAnimationFrame(() => this.draw());
517        } else if (this.rangeRect.containsWithPadding(x, y, markPadding, 0)) {
518        } else if (this.frame.containsWithMargin(x, y, 20, 0, 0, 0) && !this.rangeRect.containsWithMargin(x, y, 0, markPadding, 0, markPadding)) {
519        }
520        if (this.isMovingRange && this.isMouseDown) {
521            let result = x - this.mouseDownOffsetX;
522            let mA = result + this.markAX
523            let mB = result + this.markBX
524            if (mA >= 0 && mA <= maxX) {
525                this.markA.frame.x = mA
526            } else if (mA < 0) {
527                this.markA.frame.x = 0
528            } else {
529                this.markA.frame.x = maxX
530            }
531            this.markA.inspectionFrame.x = this.markA.frame.x - markPadding
532            if (mB >= 0 && mB <= maxX) {
533                this.markB.frame.x = mB;
534            } else if (mB < 0) {
535                this.markB.frame.x = 0
536            } else {
537                this.markB.frame.x = maxX
538            }
539            this.markB.inspectionFrame.x = this.markB.frame.x - markPadding
540            requestAnimationFrame(() => this.draw());
541        } else if (this.isNewRange) {
542            this.markA.frame.x = this.mouseDownOffsetX;
543            this.markA.inspectionFrame.x = this.mouseDownOffsetX - markPadding;
544            if (x >= 0 && x <= maxX) {
545                this.markB.frame.x = x;
546            } else if (x < 0) {
547                this.markB.frame.x = 0;
548            } else {
549                this.markB.frame.x = maxX;
550            }
551            this.markB.inspectionFrame.x = this.markB.frame.x - markPadding;
552            requestAnimationFrame(() => this.draw());
553        }
554    }
555
556    mouseOut(ev: MouseEvent) {
557        this.movingMark = null;
558    }
559
560
561    fillX() {
562        if (this.range.startNS < 0) this.range.startNS = 0;
563        if (this.range.endNS < 0) this.range.endNS = 0;
564        if (this.range.endNS > this.range.totalNS) this.range.endNS = this.range.totalNS;
565        if (this.range.startNS > this.range.totalNS) this.range.startNS = this.range.totalNS;
566        this.range.startX = this.range.startNS * (this.frame.width || 0) / this.range.totalNS
567        this.range.endX = this.range.endNS * (this.frame.width || 0) / this.range.totalNS
568        this.markA.frame.x = this.range.startX
569        this.markA.inspectionFrame.x = this.markA.frame.x - markPadding
570        this.markB.frame.x = this.range.endX
571        this.markB.inspectionFrame.x = this.markB.frame.x - markPadding
572    }
573
574    keyPress(ev: KeyboardEvent) {
575        if (this.animaStartTime === undefined) {
576            this.animaStartTime = new Date().getTime();
577        }
578        let startTime = new Date().getTime();
579        let duration = (startTime - this.animaStartTime);
580        if (duration < this.animTime) duration = this.animTime
581        this.currentDuration = duration
582        if (this.isPress) return
583        this.isPress = true
584        switch (ev.key.toLocaleLowerCase()) {
585            case "w":
586                let animW = () => {
587                    if (this.scale === 50) return;
588                    this.range.startNS += (this.centerXPercentage * this.currentDuration * 2 * this.scale / this.p);
589                    this.range.endNS -= ((1 - this.centerXPercentage) * this.currentDuration * 2 * this.scale / this.p);
590                    this.fillX();
591                    this.draw();
592                    this.pressFrameId = requestAnimationFrame(animW)
593                }
594                this.pressFrameId = requestAnimationFrame(animW)
595                break;
596            case "s":
597                let animS = () => {
598                    if (this.range.startNS <= 0 && this.range.endNS >= this.range.totalNS) return;
599                    this.range.startNS -= (this.centerXPercentage * this.currentDuration * 2 * this.scale / this.p);
600                    this.range.endNS += ((1 - this.centerXPercentage) * this.currentDuration * 2 * this.scale / this.p);
601                    this.fillX();
602                    this.draw();
603                    this.pressFrameId = requestAnimationFrame(animS)
604                }
605                this.pressFrameId = requestAnimationFrame(animS)
606                break;
607            case "a":
608                let animA = () => {
609                    if (this.range.startNS == 0) return;
610                    let s = this.scale / this.p * this.currentDuration;
611                    this.range.startNS -= s;
612                    this.range.endNS -= s;
613                    this.fillX();
614                    this.draw();
615                    this.pressFrameId = requestAnimationFrame(animA)
616
617                }
618                this.pressFrameId = requestAnimationFrame(animA)
619                break;
620            case "d":
621                let animD = () => {
622                    if (this.range.endNS >= this.range.totalNS) return;
623                    this.range.startNS += this.scale / this.p * this.currentDuration;
624                    this.range.endNS += this.scale / this.p * this.currentDuration;
625                    this.fillX();
626                    this.draw();
627                    this.pressFrameId = requestAnimationFrame(animD)
628
629                }
630                this.pressFrameId = requestAnimationFrame(animD)
631                break;
632        }
633    }
634
635    keyUp(ev: KeyboardEvent) {
636        this.animaStartTime = undefined;
637        this.isPress = false
638        if (this.pressFrameId != -1) {
639            cancelAnimationFrame(this.pressFrameId)
640        }
641        let startTime = new Date().getTime();
642        switch (ev.key) {
643            case "w":
644                let animW = () => {
645                    if (this.scale === 50) return;
646                    let dur = (new Date().getTime() - startTime);
647                    this.range.startNS += (this.centerXPercentage * 100 * this.scale / this.p);
648                    this.range.endNS -= ((1 - this.centerXPercentage) * 100 * this.scale / this.p);
649                    this.fillX();
650                    this.draw();
651                    if (dur < 300) {
652                        requestAnimationFrame(animW)
653                    }
654                }
655                requestAnimationFrame(animW)
656                break;
657            case "s":
658                let animS = () => {
659                    if (this.range.startNS <= 0 && this.range.endNS >= this.range.totalNS) return;
660                    let dur = (new Date().getTime() - startTime);
661                    this.range.startNS -= (this.centerXPercentage * 100 * this.scale / this.p);
662                    this.range.endNS += ((1 - this.centerXPercentage) * 100 * this.scale / this.p);
663                    this.fillX();
664                    this.draw();
665                    if (dur < 300) {
666                        requestAnimationFrame(animS)
667                    }
668                }
669                requestAnimationFrame(animS)
670                break;
671            case "a":
672                let animA = () => {
673                    if (this.range.startNS <= 0) return
674                    let dur = (new Date().getTime() - startTime);
675                    let s = this.scale * 80 / this.p;
676                    this.range.startNS -= s;
677                    this.range.endNS -= s;
678                    this.fillX();
679                    this.draw();
680                    if (dur < 300) {
681                        requestAnimationFrame(animA)
682                    }
683                }
684                animA();
685                break;
686            case "d":
687                let animD = () => {
688                    if (this.range.endNS >= this.range.totalNS) return;
689                    let dur = (new Date().getTime() - startTime);
690                    let s = this.scale * 80 / this.p;
691                    this.range.startNS += s;
692                    this.range.endNS += s;
693                    this.fillX();
694                    this.draw();
695                    if (dur < 300) {
696                        requestAnimationFrame(animD)
697                    }
698                }
699                animD();
700                break;
701        }
702    }
703}
704
705export class Flag {
706    x: number = 0
707    y: number = 0
708    width: number = 0
709    height: number = 0
710    time: number = 0
711    color: string = ""
712    selected: boolean = false
713    text: string = ""
714
715    constructor(x: number, y: number, width: number, height: number, time: number, color: string = "#999999", selected = false) {
716        this.x = x;
717        this.y = y;
718        this.width = width;
719        this.height = height;
720        this.time = time;
721        this.color = color;
722        this.selected = selected;
723    }
724}