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 { ColorUtils } from '../../component/trace/base/ColorUtils.js'; 17import { TraceRow } from '../../component/trace/base/TraceRow.js'; 18import { BaseStruct, drawString, isFrameContainPoint, ns2x, Rect, Render } from './ProcedureWorkerCommon.js'; 19 20export class FrameAnimationRender extends Render { 21 renderMainThread( 22 req: { 23 useCache: boolean; 24 context: CanvasRenderingContext2D; 25 type: string; 26 }, 27 row: TraceRow<FrameAnimationStruct> 28 ): void { 29 let frameAnimationList: FrameAnimationStruct[] = row.dataList; 30 let frameAnimationFilter: FrameAnimationStruct[] = row.dataListCache; 31 this.frameAnimation( 32 frameAnimationList, 33 frameAnimationFilter, 34 TraceRow.range!.startNS, 35 TraceRow.range!.endNS, 36 TraceRow.range!.totalNS, 37 row.frame, 38 req.useCache || !TraceRow.range!.refresh 39 ); 40 req.context.beginPath(); 41 let find: boolean = false; 42 for (let index: number = 0; index < frameAnimationFilter.length; index++) { 43 let currentAnimationStruct: FrameAnimationStruct = frameAnimationFilter[index]; 44 FrameAnimationStruct.draw(req.context, index, currentAnimationStruct, row); 45 if (row.isHover && currentAnimationStruct.frame && 46 isFrameContainPoint(currentAnimationStruct.frame, row.hoverX, row.hoverY)) { 47 FrameAnimationStruct.hoverFrameAnimationStruct = currentAnimationStruct; 48 find = true; 49 } 50 } 51 if (!find && row.isHover) { 52 FrameAnimationStruct.hoverFrameAnimationStruct = undefined; 53 } 54 req.context.closePath(); 55 } 56 57 private frameAnimation( 58 frameAnimationList: FrameAnimationStruct[], 59 frameAnimationFilter: FrameAnimationStruct[], 60 startNS: number = 0, 61 endNS: number = 0, 62 totalNS: number, 63 frame: Rect, 64 use: boolean 65 ): void { 66 if (use && frameAnimationFilter.length > 0) { 67 for (let index: number = 0; index < frameAnimationFilter.length; index++) { 68 let frameAnimationNode: FrameAnimationStruct = frameAnimationFilter[index]; 69 frameAnimationNode.frame = undefined; 70 FrameAnimationStruct.setFrameAnimation(frameAnimationNode, padding, startNS, endNS, totalNS, frame); 71 } 72 return; 73 } 74 frameAnimationFilter.length = 0; 75 if (frameAnimationList) { 76 for (let index: number = 0; index < frameAnimationList.length; index++) { 77 let currentFrameAnimation: FrameAnimationStruct = frameAnimationList[index]; 78 let currentResponseFrame: FrameAnimationStruct = JSON.parse(JSON.stringify(currentFrameAnimation)); 79 currentResponseFrame.status = 'Response delay'; 80 currentResponseFrame.dur = currentFrameAnimation.dynamicStartTs - currentFrameAnimation.ts; 81 if ((currentResponseFrame.ts || 0) + (currentResponseFrame.dur || 0) > startNS && 82 (currentResponseFrame.ts || 0) < endNS) { 83 FrameAnimationStruct.setFrameAnimation(currentResponseFrame, padding, startNS, 84 endNS || 0, totalNS || 0, frame); 85 frameAnimationFilter.push(currentResponseFrame); 86 } 87 let currentCompletionFrame: FrameAnimationStruct = JSON.parse(JSON.stringify(currentFrameAnimation)); 88 currentCompletionFrame.status = 'Completion delay'; 89 currentCompletionFrame.ts = currentFrameAnimation.dynamicStartTs; 90 currentCompletionFrame.dur = currentFrameAnimation.dynamicEndTs - currentFrameAnimation.dynamicStartTs; 91 if ((currentCompletionFrame.ts || 0) + (currentCompletionFrame.dur || 0) > startNS && 92 (currentCompletionFrame.ts || 0) < endNS) { 93 FrameAnimationStruct.setFrameAnimation(currentCompletionFrame, padding, startNS, 94 endNS || 0, totalNS || 0, frame); 95 frameAnimationFilter.push(currentCompletionFrame); 96 } 97 } 98 } 99 }; 100} 101 102export class FrameAnimationStruct extends BaseStruct { 103 static hoverFrameAnimationStruct: FrameAnimationStruct | undefined; 104 static selectFrameAnimationStruct: FrameAnimationStruct | undefined; 105 ts: number = 0; 106 dur: number = 0; 107 status: string = ''; 108 animationId: number | undefined; 109 dynamicStartTs: number = 0; 110 dynamicEndTs: number = 0; 111 fps: number | undefined; 112 113 static setFrameAnimation( 114 animationNode: FrameAnimationStruct, 115 padding: number, 116 startNS: number, 117 endNS: number, 118 totalNS: number, 119 frame: Rect 120 ): void { 121 let stateStartPointX: number; 122 let stateEndPointX: number; 123 if ((animationNode.ts || 0) < startNS) { 124 stateStartPointX = 0; 125 } else { 126 stateStartPointX = ns2x(animationNode.ts || 0, startNS, endNS, totalNS, frame); 127 } 128 if ((animationNode.ts || 0) + (animationNode.dur || 0) > endNS) { 129 stateEndPointX = frame.width; 130 } else { 131 stateEndPointX = ns2x((animationNode.ts || 0) + (animationNode.dur || 0), startNS, endNS, totalNS, frame); 132 } 133 let frameWidth: number = stateEndPointX - stateStartPointX <= unitIndex ? unitIndex : 134 stateEndPointX - stateStartPointX; 135 if (!animationNode.frame) { 136 animationNode.frame = new Rect(0, 0, 0, 0); 137 } 138 animationNode.frame.x = Math.floor(stateStartPointX); 139 animationNode.frame.y = frame.y + padding; 140 animationNode.frame.width = Math.ceil(frameWidth); 141 animationNode.frame.height = Math.floor(frame.height - padding * multiple); 142 } 143 144 static draw( 145 ctx: CanvasRenderingContext2D, 146 index: number, 147 frameAnimationNode: FrameAnimationStruct, 148 row: TraceRow<FrameAnimationStruct> 149 ): void { 150 let tsFixed: number = 6; 151 let isHover: boolean = row.isHover; 152 if (frameAnimationNode.frame) { 153 let nsToMillisecond = 1000_000; 154 ctx.globalAlpha = 1.0; 155 ctx.lineWidth = 1; 156 ctx.lineJoin = 'round'; 157 ctx.fillStyle = ColorUtils.ANIMATION_COLOR[6]; 158 ctx.fillRect(frameAnimationNode.frame.x, frameAnimationNode.frame.y + (multiple * padding), 159 frameAnimationNode.frame.width, frameAnimationNode.frame.height); 160 ctx.fillStyle = ColorUtils.ANIMATION_COLOR[3]; 161 ctx.textBaseline = 'middle'; 162 ctx.font = '8px sans-serif'; 163 drawString(ctx, `${frameAnimationNode.status} (${(frameAnimationNode.dur / nsToMillisecond). 164 toFixed(tsFixed)} ms)`, textPadding, frameAnimationNode.frame, frameAnimationNode); 165 ctx.lineWidth = 2; 166 if ((frameAnimationNode === FrameAnimationStruct.hoverFrameAnimationStruct && isHover) || 167 frameAnimationNode === FrameAnimationStruct.selectFrameAnimationStruct) { 168 ctx.globalAlpha = 0.8; 169 ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[3]; 170 ctx.strokeRect(frameAnimationNode.frame.x + padding, frameAnimationNode.frame.y + (multiple * padding), 171 frameAnimationNode.frame.width - padding, frameAnimationNode.frame.height - (multiple * padding)); 172 } else { 173 ctx.strokeStyle = ColorUtils.ANIMATION_COLOR[2]; 174 ctx.strokeRect(frameAnimationNode.frame.x + padding, frameAnimationNode.frame.y + (multiple * padding), 175 frameAnimationNode.frame.width - padding, frameAnimationNode.frame.height - (multiple * padding)); 176 } 177 } 178 } 179} 180 181const padding: number = 2; 182const multiple: number = 2; 183const unitIndex: number = 1; 184const textPadding: number = 5; 185