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