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 {BaseElement, element} from "../../../base-ui/BaseElement.js"; 17import {TimeRuler} from "./timer-shaft/TimeRuler.js"; 18import {Rect} from "./timer-shaft/Rect.js"; 19import {RangeRuler, TimeRange} from "./timer-shaft/RangeRuler.js"; 20import {SportRuler} from "./timer-shaft/SportRuler.js"; 21import {procedurePool} from "../../database/Procedure.js"; 22 23export function ns2s(ns: number): string { 24 let second1 = 1_000_000_000; // 1 second 25 let millisecond1 = 1_000_000; // 1 millisecond 26 let microsecond1 = 1_000; // 1 microsecond 27 let nanosecond1 = 1000.0; 28 let res; 29 if (ns >= second1) { 30 res = (ns / 1000 / 1000 / 1000).toFixed(1) + " s"; 31 } else if (ns >= millisecond1) { 32 res = (ns / 1000 / 1000).toFixed(1) + " ms"; 33 } else if (ns >= microsecond1) { 34 res = (ns / 1000).toFixed(1) + " μs"; 35 } else if (ns > 0) { 36 res = ns.toFixed(1) + " ns"; 37 } else { 38 res = ns.toFixed(1) + " s"; 39 } 40 return res; 41} 42 43export function ns2x(ns: number, startNS: number, endNS: number, duration: number, rect: Rect) { 44 if (endNS == 0) { 45 endNS = duration; 46 } 47 let xSize: number = (ns - startNS) * rect.width / (endNS - startNS); 48 if (xSize < 0) { 49 xSize = 0; 50 } 51 if (xSize > rect.width) { 52 xSize = rect.width; 53 } 54 return xSize; 55} 56 57@element('timer-shaft-element') 58export class TimerShaftElement extends BaseElement { 59 public ctx: CanvasRenderingContext2D | undefined | null 60 public canvas: HTMLCanvasElement | null | undefined 61 public totalEL: HTMLDivElement | null | undefined 62 public timeTotalEL: HTMLSpanElement | null | undefined 63 public timeOffsetEL: HTMLSpanElement | null | undefined 64 public loadComplete: boolean = false 65 // @ts-ignore 66 offscreen: OffscreenCanvas | undefined; 67 isOffScreen: boolean = false; 68 rangeChangeHandler: ((timeRange: TimeRange) => void) | undefined = undefined 69 dpr = window.devicePixelRatio || 1; 70 frame: Rect = new Rect(0, 0, 0, 0); 71 must: boolean = true 72 hoverX: number = 0 73 hoverY: number = 0 74 canvasWidth: number = 0 75 canvasHeight: number = 0 76 protected timeRuler: TimeRuler | undefined; 77 protected rangeRuler: RangeRuler | undefined; 78 protected sportRuler: SportRuler | undefined; 79 private root: HTMLDivElement | undefined | null 80 81 _cpuUsage: Array<{ cpu: number, ro: number, rate: number }> = [] 82 83 set cpuUsage(value: Array<{ cpu: number, ro: number, rate: number }>) { 84 this._cpuUsage = value; 85 if (this.rangeRuler) { 86 this.rangeRuler.cpuUsage = this._cpuUsage; 87 } 88 } 89 90 private _totalNS: number = 10_000_000_000; 91 92 get totalNS(): number { 93 return this._totalNS; 94 } 95 96 set totalNS(value: number) { 97 this._totalNS = value; 98 if (this.timeRuler) this.timeRuler.totalNS = value; 99 if (this.rangeRuler) this.rangeRuler.range.totalNS = value; 100 if (this.timeTotalEL) this.timeTotalEL.textContent = `${ns2s(value)}` 101 requestAnimationFrame(() => this.render()) 102 } 103 104 private _startNS: number = 0; 105 106 get startNS(): number { 107 return this._startNS; 108 } 109 110 set startNS(value: number) { 111 this._startNS = value; 112 } 113 114 private _endNS: number = 10_000_000_000; 115 116 get endNS(): number { 117 return this._endNS; 118 } 119 120 set endNS(value: number) { 121 this._endNS = value; 122 } 123 124 isScaling(): boolean { 125 return this.rangeRuler?.isPress || false; 126 } 127 128 reset(): void { 129 this.loadComplete = false; 130 if (this.rangeRuler) { 131 this.rangeRuler.markA.frame.x = 0; 132 this.rangeRuler.markB.frame.x = this.rangeRuler.frame.width 133 this.rangeRuler.cpuUsage = [] 134 } 135 this.totalNS = 10_000_000_000; 136 } 137 138 initElements(): void { 139 this.root = this.shadowRoot?.querySelector('.root') 140 this.canvas = this.shadowRoot?.querySelector('.panel') 141 this.totalEL = this.shadowRoot?.querySelector('.total') 142 this.timeTotalEL = this.shadowRoot?.querySelector('.time-total') 143 this.timeOffsetEL = this.shadowRoot?.querySelector('.time-offset') 144 procedurePool.timelineChange = (a: any) => { 145 this.rangeChangeHandler?.(a); 146 } 147 } 148 149 connectedCallback() { 150 if (this.canvas) { 151 if (this.isOffScreen) { 152 console.log("timeline offscreen"); 153 // @ts-ignore 154 this.offscreen = this.canvas.transferControlToOffscreen(); 155 return; 156 } else { 157 this.ctx = this.canvas?.getContext('2d', {alpha: true}); 158 } 159 } 160 if (this.timeTotalEL) this.timeTotalEL.textContent = ns2s(this._totalNS) 161 if (this.timeOffsetEL) this.timeOffsetEL.textContent = ns2s(this._startNS) 162 const width = this.canvas?.clientWidth || 0; 163 const height = this.canvas?.clientHeight || 0; 164 if (!this.timeRuler) { 165 this.timeRuler = new TimeRuler(this.canvas, this.ctx!, new Rect(0, 0, width, 20), this._totalNS); 166 } 167 if (!this.sportRuler) { 168 this.sportRuler = new SportRuler(this.canvas, this.ctx!, new Rect(0, 100.5, width, height - 100)); 169 } 170 if (!this.rangeRuler) { 171 this.rangeRuler = new RangeRuler(this.canvas, this.ctx!, new Rect(0, 25, width, 75), { 172 startX: 0, 173 endX: this.canvas?.clientWidth || 0, 174 startNS: 0, 175 endNS: this.totalNS, 176 totalNS: this.totalNS, 177 xs: [], 178 xsTxt: [] 179 }, (a) => { 180 if (this.sportRuler) { 181 this.sportRuler.range = a; 182 } 183 if (this.timeOffsetEL) { 184 this.timeOffsetEL.textContent = ns2s(a.startNS) 185 } 186 if (this.loadComplete) { 187 this.rangeChangeHandler?.(a) 188 } 189 }); 190 } 191 this.rangeRuler.frame.width = width; 192 this.sportRuler.frame.width = width; 193 this.timeRuler.frame.width = width; 194 // @ts-ignore 195 let dpr = window.devicePixelRatio || window.webkitDevicePixelRatio || window.mozDevicePixelRatio || 1; 196 } 197 198 199 updateWidth(width: number) { 200 if (this.isOffScreen) { 201 this.frame.width = width - (this.totalEL?.clientWidth || 0); 202 this.frame.height = this.shadowRoot!.host.clientHeight || 0; 203 this.canvasWidth = Math.round((this.frame.width) * this.dpr); 204 this.canvasHeight = Math.round((this.frame.height) * this.dpr); 205 this.render(); 206 return; 207 } 208 this.canvas!.width = width - (this.totalEL?.clientWidth || 0); 209 this.canvas!.height = this.shadowRoot!.host.clientHeight || 0; 210 let oldWidth = this.canvas!.width; 211 let oldHeight = this.canvas!.height; 212 this.canvas!.width = Math.round((oldWidth) * this.dpr); 213 this.canvas!.height = Math.round(oldHeight * this.dpr); 214 this.canvas!.style.width = oldWidth + 'px'; 215 this.canvas!.style.height = oldHeight + 'px'; 216 this.ctx?.scale(this.dpr, this.dpr); 217 this.ctx?.translate(0, 0) 218 this.rangeRuler!.frame.width = oldWidth; 219 this.sportRuler!.frame.width = oldWidth; 220 this.timeRuler!.frame.width = oldWidth; 221 this.rangeRuler?.fillX() 222 this.render() 223 } 224 225 documentOnMouseDown = (ev: MouseEvent) => { 226 if (this.isOffScreen) { 227 procedurePool.submitWithName(`timeline`, `timeline`, { 228 offscreen: this.must ? this.offscreen : undefined, 229 dpr: this.dpr, 230 hoverX: this.hoverX, 231 hoverY: this.hoverY, 232 canvasWidth: this.canvasWidth, 233 canvasHeight: this.canvasHeight, 234 offsetLeft: this.canvas?.offsetLeft || 0, 235 offsetTop: this.canvas?.offsetTop || 0, 236 mouseDown: {offsetX: ev.offsetX, offsetY: ev.offsetY}, 237 mouseUp: null, 238 mouseMove: null, 239 mouseOut: null, 240 keyPressCode: null, 241 keyUpCode: null, 242 lineColor: "#dadada", 243 startNS: this.startNS, 244 endNS: this.endNS, 245 totalNS: this.totalNS, 246 frame: this.frame, 247 }, this.must ? this.offscreen : undefined, (res: any) => { 248 this.must = false; 249 }) 250 } else { 251 this.rangeRuler?.mouseDown(ev); 252 } 253 } 254 255 documentOnMouseUp = (ev: MouseEvent) => { 256 if (this.isOffScreen) { 257 procedurePool.submitWithName(`timeline`, `timeline`, { 258 offscreen: this.must ? this.offscreen : undefined, 259 dpr: this.dpr, 260 hoverX: this.hoverX, 261 hoverY: this.hoverY, 262 canvasWidth: this.canvasWidth, 263 canvasHeight: this.canvasHeight, 264 offsetLeft: this.canvas?.offsetLeft || 0, 265 offsetTop: this.canvas?.offsetTop || 0, 266 mouseUp: {offsetX: ev.offsetX, offsetY: ev.offsetY}, 267 mouseMove: null, 268 mouseOut: null, 269 keyPressCode: null, 270 keyUpCode: null, 271 lineColor: "#dadada", 272 startNS: this.startNS, 273 endNS: this.endNS, 274 totalNS: this.totalNS, 275 frame: this.frame, 276 }, this.must ? this.offscreen : undefined, (res: any) => { 277 this.must = false; 278 }) 279 } else { 280 this.rangeRuler?.mouseUp(ev); 281 this.sportRuler?.mouseUp(ev); 282 } 283 } 284 285 documentOnMouseMove = (ev: MouseEvent) => { 286 if (this.isOffScreen) { 287 procedurePool.submitWithName(`timeline`, `timeline`, { 288 offscreen: this.must ? this.offscreen : undefined, 289 dpr: this.dpr, 290 hoverX: this.hoverX, 291 hoverY: this.hoverY, 292 canvasWidth: this.canvasWidth, 293 canvasHeight: this.canvasHeight, 294 offsetLeft: this.canvas?.offsetLeft || 0, 295 offsetTop: this.canvas?.offsetTop || 0, 296 mouseMove: {offsetX: ev.offsetX, offsetY: ev.offsetY}, 297 mouseOut: null, 298 keyPressCode: null, 299 keyUpCode: null, 300 lineColor: "#dadada", 301 startNS: this.startNS, 302 endNS: this.endNS, 303 totalNS: this.totalNS, 304 frame: this.frame, 305 }, this.must ? this.offscreen : undefined, (res: any) => { 306 this.must = false; 307 }) 308 } else { 309 this.rangeRuler?.mouseMove(ev); 310 this.sportRuler?.mouseMove(ev); 311 } 312 } 313 314 documentOnMouseOut = (ev: MouseEvent) => { 315 if (this.isOffScreen) { 316 procedurePool.submitWithName(`timeline`, `timeline`, { 317 offscreen: this.must ? this.offscreen : undefined, 318 dpr: this.dpr, 319 hoverX: this.hoverX, 320 hoverY: this.hoverY, 321 canvasWidth: this.canvasWidth, 322 canvasHeight: this.canvasHeight, 323 offsetLeft: this.canvas?.offsetLeft || 0, 324 offsetTop: this.canvas?.offsetTop || 0, 325 mouseOut: {offsetX: ev.offsetX, offsetY: ev.offsetY}, 326 keyPressCode: null, 327 keyUpCode: null, 328 lineColor: "#dadada", 329 startNS: this.startNS, 330 endNS: this.endNS, 331 totalNS: this.totalNS, 332 frame: this.frame, 333 }, this.must ? this.offscreen : undefined, (res: any) => { 334 this.must = false; 335 }) 336 } else { 337 this.rangeRuler?.mouseOut(ev); 338 } 339 } 340 documentOnKeyPress = (ev: KeyboardEvent) => { 341 if (this.isOffScreen) { 342 procedurePool.submitWithName(`timeline`, `timeline`, { 343 offscreen: this.must ? this.offscreen : undefined, 344 dpr: this.dpr, 345 hoverX: this.hoverX, 346 hoverY: this.hoverY, 347 canvasWidth: this.canvasWidth, 348 canvasHeight: this.canvasHeight, 349 keyPressCode: {key: ev.key}, 350 keyUpCode: null, 351 lineColor: "#dadada", 352 startNS: this.startNS, 353 endNS: this.endNS, 354 totalNS: this.totalNS, 355 frame: this.frame, 356 }, this.must ? this.offscreen : undefined, (res: any) => { 357 this.must = false; 358 }) 359 } else { 360 this.rangeRuler?.keyPress(ev); 361 } 362 } 363 364 documentOnKeyUp = (ev: KeyboardEvent) => { 365 if (this.isOffScreen) { 366 procedurePool.submitWithName(`timeline`, `timeline`, { 367 offscreen: this.must ? this.offscreen : undefined, 368 dpr: this.dpr, 369 hoverX: this.hoverX, 370 hoverY: this.hoverY, 371 canvasWidth: this.canvasWidth, 372 canvasHeight: this.canvasHeight, 373 keyPressCode: null, 374 keyUpCode: {key: ev.key}, 375 lineColor: "#dadada", 376 startNS: this.startNS, 377 endNS: this.endNS, 378 totalNS: this.totalNS, 379 frame: this.frame, 380 }, this.must ? this.offscreen : undefined, (res: any) => { 381 this.must = false; 382 }) 383 } else { 384 this.rangeRuler?.keyUp(ev); 385 } 386 } 387 388 disconnectedCallback() { 389 } 390 391 render() { 392 if (this.ctx) { 393 this.ctx.fillStyle = 'transparent'; 394 this.ctx?.fillRect(0, 0, this.canvas?.width || 0, this.canvas?.height || 0) 395 this.timeRuler?.draw() 396 this.rangeRuler?.draw() 397 this.sportRuler?.draw() 398 } else { 399 procedurePool.submitWithName(`timeline`, `timeline`, { 400 offscreen: this.must ? this.offscreen : undefined, 401 dpr: this.dpr, 402 hoverX: this.hoverX, 403 hoverY: this.hoverY, 404 canvasWidth: this.canvasWidth, 405 canvasHeight: this.canvasHeight, 406 keyPressCode: null, 407 keyUpCode: null, 408 lineColor: "#dadada", 409 startNS: this.startNS, 410 endNS: this.endNS, 411 totalNS: this.totalNS, 412 frame: this.frame, 413 }, this.must ? this.offscreen : undefined, (res: any) => { 414 this.must = false; 415 }) 416 } 417 } 418 419 modifyList(type: string, flag: any = {}) { 420 this.sportRuler?.modifyFlagList(type, flag) 421 } 422 423 initHtml(): string { 424 return ` 425<style> 426:host{ 427 box-sizing: border-box; 428 display: flex; 429 width: 100%; 430 height: 147px; 431 border-bottom: 1px solid var(--dark-background,#dadada); 432 border-top: 1px solid var(--dark-background,#dadada); 433} 434*{ 435 box-sizing: border-box; 436} 437.root{ 438 width: 100%; 439 height: 100%; 440 display: grid; 441 grid-template-rows: 100%; 442 grid-template-columns: 248px 1fr; 443 background: var(--dark-background4,#FFFFFF); 444} 445.total{ 446 display: grid; 447 grid-template-columns: 1fr; 448 grid-template-rows: min-content 1fr; 449 background-color: transparent; 450} 451.panel{ 452 color: var(--dark-border,#dadada); 453 width: 100%; 454 height: 100%; 455 overflow: visible; 456 background-color: var(--dark-background4,#ffffff); 457} 458.time-div{ 459 box-sizing: border-box; 460 width: 100%;border-top: 1px solid var(--dark-background,#dadada);height: 100%;display: flex;justify-content: space-between;background-color: var(--dark-background1,white);color: var(--dark-color1,#212121);font-size: 0.7rem; 461 border-right: 1px solid var(--dark-background,#999); 462 padding: 2px 6px; 463 display: flex;justify-content: space-between; 464 user-select: none; 465} 466.time-total::after{ 467 content: " +"; 468} 469 470</style> 471<div class="root"> 472 <div class="total"> 473 <div style="width: 100%;height: 100px;background: var(--dark-background4,#F6F6F6)"></div> 474 <div class="time-div"> 475 <span class="time-total">10</span> 476 <span class="time-offset">0</span> 477 </div> 478 </div> 479 <canvas class="panel"></canvas> 480</div> 481 `; 482 } 483} 484