107 lines
4.4 KiB
JavaScript
107 lines
4.4 KiB
JavaScript
|
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;
|
||
|
}
|