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