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