blob: aefd75faa278246c895df2c9dfc497dffd39d719 [file] [log] [blame]
/**
* @module skottie-performance-sk
* @description <h2><code>skottie-performance-sk</code></h2>
*
* <p>
* A skottie performance graph.
* It exposes two methods start and end to calculate rendering time of each frame
* </p>
*
*
*
*/
import { define } from 'elements-sk/define';
import { html } from 'lit-html';
import { $$ } from 'common-sk/modules/dom';
import {
Chart,
BarController,
CategoryScale,
LinearScale,
BarElement,
} from 'chart.js';
import { ElementSk } from '../../../infra-sk/modules/ElementSk';
Chart.register(
BarElement,
BarController,
CategoryScale,
LinearScale,
);
const marks = {
START: 'Start Skottie Frame',
END: 'End Skottie Frame',
NAME: 'Skottie Frame',
};
interface FrameMetric {
values: number[];
average: number;
}
export class SkottiePerformanceSk extends ElementSk {
private static template = () => html`
<div>
<div class="chart">
<canvas id=performance-chart height=400 width=400></canvas>
</div>
</div>
`;
private currentMeasuringFrame: number = -1;
private currentMetrics: FrameMetric[] = [];
private chart: Chart | null = null;
constructor() {
super(SkottiePerformanceSk.template);
}
connectedCallback() {
super.connectedCallback();
this._render();
this._createNewChart();
}
disconnectedCallback() {
if (this.chart) {
this.chart.destroy();
}
super.disconnectedCallback();
}
_createNewChart() {
if (this.chart) {
this.currentMetrics = [];
this.chart.destroy();
}
const canvas = $$<HTMLCanvasElement>('#performance-chart', this)!;
const ctx = canvas.getContext('2d')!;
this.chart = new Chart(ctx, {
type: 'bar',
data: {
labels: [],
datasets: [
{
label: 'Frame Duration (ms)',
backgroundColor: 'rgba(0,220,220,0.4)',
borderColor: 'rgba(0,220,220,1)',
data: [],
},
],
},
options: {
maintainAspectRatio: false,
events: ['click', 'mousemove'],
},
});
}
getClickedFrame(e: Event): number {
const points = this.chart?.getElementsAtEventForMode(e, 'index', { intersect: false, axis: 'x' }, true);
let frame: number = -1;
if (points && points[0]) {
const point = points[0];
frame = point.index;
}
return frame;
}
reset(): void {
this._createNewChart();
}
private addDataPoint(frame: number, metrics: PerformanceEntryList) {
if (!this.chart) {
return;
}
if (!this.currentMetrics[frame]) {
this.currentMetrics[frame] = {
values: [],
average: 0,
};
}
const metricData = this.currentMetrics[frame];
metrics.forEach((metric: PerformanceEntry) => metricData.values.push(metric.duration));
const average = metricData.values.reduce((acc: number, value) => acc + value, 0) / metricData.values.length;
if (!this.chart.data.labels![frame]) {
while (!this.chart.data.labels![frame]) {
this.chart.data.labels!.push(`frame ${this.chart.data.labels!.length}`);
}
}
this.chart.data.datasets[0].data[frame] = average;
this.chart.update();
}
start(progress: number, duration: number, fps: number) {
const newFrame = Math.floor((progress * fps) / 1000);
if (this.currentMeasuringFrame !== newFrame) {
this.currentMeasuringFrame = newFrame;
const metrics = performance.getEntriesByName(marks.NAME);
this.addDataPoint(newFrame, metrics);
performance.clearMarks();
performance.clearResourceTimings();
performance.clearMeasures();
}
performance.mark(marks.START);
}
end(): void {
performance.mark(marks.END);
performance.measure(marks.NAME, marks.START, marks.END);
}
}
define('skottie-performance-sk', SkottiePerformanceSk);