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