1const DEFAULT_METHOD = 'SVG'; 2 3const worker = new Worker('worker.js'); 4 5const svgObjectElement = document.getElementById('svg'); 6document.getElementById('svg').addEventListener('load', () => { 7 8 const svgElement = svgObjectElement.contentDocument; 9 const svgData = svgToPathStringAndFillColorPairs(svgElement); 10 11 // Send svgData and transfer an offscreenCanvas to the worker for Path2D and CanvasKit rendering 12 const path2dCanvas = 13 document.getElementById('Path2D-canvas').transferControlToOffscreen(); 14 worker.postMessage({ 15 svgData: svgData, 16 offscreenCanvas: path2dCanvas, 17 type: 'Path2D' 18 }, [path2dCanvas]); 19 const canvasKitCanvas = 20 document.getElementById('CanvasKit-canvas').transferControlToOffscreen(); 21 worker.postMessage({ 22 svgData: svgData, 23 offscreenCanvas: canvasKitCanvas, 24 type: 'CanvasKit' 25 }, [canvasKitCanvas]); 26 27 // The Canvas2D and CanvasKit rendering methods are executed in a web worker to avoid blocking 28 // the main thread. The SVG rendering method is executed in the main thread. SVG rendering is 29 // not in a worker because it is not possible - the DOM cannot be accessed from a web worker. 30 const svgAnimator = new Animator(); 31 svgAnimator.renderer = new SVGRenderer(svgObjectElement); 32 switchRenderMethodCallback(DEFAULT_METHOD)(); 33 34 // Listen to framerate reports from the worker, and update framerate text 35 worker.addEventListener('message', ({ data: {renderMethod, framesCount, totalFramesMs} }) => { 36 const fps = fpsFromFramesInfo(framesCount, totalFramesMs); 37 let textEl; 38 if (renderMethod === 'Path2D') { 39 textEl = document.getElementById('Path2D-fps'); 40 } 41 if (renderMethod === 'CanvasKit') { 42 textEl = document.getElementById('CanvasKit-fps'); 43 } 44 textEl.innerText = `${fps.toFixed(2)} fps over ${framesCount} frames`; 45 }); 46 // Update framerate text every second 47 setInterval(() => { 48 if (svgAnimator.framesCount > 0) { 49 const fps = fpsFromFramesInfo(svgAnimator.framesCount, svgAnimator.totalFramesMs); 50 document.getElementById('SVG-fps').innerText = 51 `${fps.toFixed(2)} fps over ${svgAnimator.framesCount} frames`; 52 } 53 }, 1000); 54 55 document.getElementById('SVG-input') 56 .addEventListener('click', switchRenderMethodCallback('SVG')); 57 document.getElementById('Path2D-input') 58 .addEventListener('click', switchRenderMethodCallback('Path2D')); 59 document.getElementById('CanvasKit-input') 60 .addEventListener('click', switchRenderMethodCallback('CanvasKit')); 61 62 function switchRenderMethodCallback(switchMethod) { 63 return () => { 64 // Hide all renderer elements and stop svgAnimator 65 document.getElementById('CanvasKit-canvas').style.visibility = 'hidden'; 66 document.getElementById('Path2D-canvas').style.visibility = 'hidden'; 67 for (const svgEl of svgAnimator.renderer.svgElArray) { 68 svgEl.style.visibility = 'hidden'; 69 } 70 svgAnimator.stop(); 71 72 // Show only the active renderer element 73 if (switchMethod === 'SVG') { 74 svgAnimator.start(); 75 for (const svgEl of svgAnimator.renderer.svgElArray) { 76 svgEl.style.visibility = 'visible'; 77 } 78 } 79 if (switchMethod === 'CanvasKit') { 80 document.getElementById('CanvasKit-canvas').style.visibility = 'visible'; 81 } 82 if (switchMethod === 'Path2D') { 83 document.getElementById('Path2D-canvas').style.visibility = 'visible'; 84 } 85 worker.postMessage({ switchMethod }); 86 }; 87 } 88}); 89// Add .data after the load listener so that the listener always fires an event 90svgObjectElement.data = 'garbage.svg'; 91 92const EMPTY_SVG_PATH_STRING = 'M 0 0'; 93const COLOR_WHITE = '#000000'; 94function svgToPathStringAndFillColorPairs(svgElement) { 95 const pathElements = Array.from(svgElement.getElementsByTagName('path')); 96 return pathElements.map((path) => [ 97 path.getAttribute('d') ?? EMPTY_SVG_PATH_STRING, 98 path.getAttribute('fill') ?? COLOR_WHITE 99 ]); 100} 101 102const MS_IN_A_SECOND = 1000; 103function fpsFromFramesInfo(framesCount, totalFramesMs) { 104 const averageFrameTime = totalFramesMs / framesCount; 105 return (1 / averageFrameTime) * MS_IN_A_SECOND; 106} 107