blob: d618bfb9cb46e7dfb78e3a963fa9903e63188c04 [file] [log] [blame]
const DEFAULT_METHOD = 'SVG';
const worker = new Worker('worker.js');
const svgObjectElement = document.getElementById('svg');
document.getElementById('svg').addEventListener('load', () => {
const svgElement = svgObjectElement.contentDocument;
const svgData = svgToPathStringAndFillColorPairs(svgElement);
// Send svgData and transfer an offscreenCanvas to the worker for Path2D and CanvasKit rendering
const path2dCanvas =
document.getElementById('Path2D-canvas').transferControlToOffscreen();
worker.postMessage({
svgData: svgData,
offscreenCanvas: path2dCanvas,
type: 'Path2D'
}, [path2dCanvas]);
const canvasKitCanvas =
document.getElementById('CanvasKit-canvas').transferControlToOffscreen();
worker.postMessage({
svgData: svgData,
offscreenCanvas: canvasKitCanvas,
type: 'CanvasKit'
}, [canvasKitCanvas]);
// The Canvas2D and CanvasKit rendering methods are executed in a web worker to avoid blocking
// the main thread. The SVG rendering method is executed in the main thread. SVG rendering is
// not in a worker because it is not possible - the DOM cannot be accessed from a web worker.
const svgAnimator = new Animator();
svgAnimator.renderer = new SVGRenderer(svgObjectElement);
switchRenderMethodCallback(DEFAULT_METHOD)();
// Listen to framerate reports from the worker, and update framerate text
worker.addEventListener('message', ({ data: {renderMethod, framesCount, totalFramesMs} }) => {
const fps = fpsFromFramesInfo(framesCount, totalFramesMs);
let textEl;
if (renderMethod === 'Path2D') {
textEl = document.getElementById('Path2D-fps');
}
if (renderMethod === 'CanvasKit') {
textEl = document.getElementById('CanvasKit-fps');
}
textEl.innerText = `${fps.toFixed(2)} fps over ${framesCount} frames`;
});
// Update framerate text every second
setInterval(() => {
if (svgAnimator.framesCount > 0) {
const fps = fpsFromFramesInfo(svgAnimator.framesCount, svgAnimator.totalFramesMs);
document.getElementById('SVG-fps').innerText =
`${fps.toFixed(2)} fps over ${svgAnimator.framesCount} frames`;
}
}, 1000);
document.getElementById('SVG-input')
.addEventListener('click', switchRenderMethodCallback('SVG'));
document.getElementById('Path2D-input')
.addEventListener('click', switchRenderMethodCallback('Path2D'));
document.getElementById('CanvasKit-input')
.addEventListener('click', switchRenderMethodCallback('CanvasKit'));
function switchRenderMethodCallback(switchMethod) {
return () => {
// Hide all renderer elements and stop svgAnimator
document.getElementById('CanvasKit-canvas').style.visibility = 'hidden';
document.getElementById('Path2D-canvas').style.visibility = 'hidden';
for (const svgEl of svgAnimator.renderer.svgElArray) {
svgEl.style.visibility = 'hidden';
}
svgAnimator.stop();
// Show only the active renderer element
if (switchMethod === 'SVG') {
svgAnimator.start();
for (const svgEl of svgAnimator.renderer.svgElArray) {
svgEl.style.visibility = 'visible';
}
}
if (switchMethod === 'CanvasKit') {
document.getElementById('CanvasKit-canvas').style.visibility = 'visible';
}
if (switchMethod === 'Path2D') {
document.getElementById('Path2D-canvas').style.visibility = 'visible';
}
worker.postMessage({ switchMethod });
};
}
});
// Add .data after the load listener so that the listener always fires an event
svgObjectElement.data = 'garbage.svg';
const EMPTY_SVG_PATH_STRING = 'M 0 0';
const COLOR_WHITE = '#000000';
function svgToPathStringAndFillColorPairs(svgElement) {
const pathElements = Array.from(svgElement.getElementsByTagName('path'));
return pathElements.map((path) => [
path.getAttribute('d') ?? EMPTY_SVG_PATH_STRING,
path.getAttribute('fill') ?? COLOR_WHITE
]);
}
const MS_IN_A_SECOND = 1000;
function fpsFromFramesInfo(framesCount, totalFramesMs) {
const averageFrameTime = totalFramesMs / framesCount;
return (1 / averageFrameTime) * MS_IN_A_SECOND;
}