Merge branch 'main' into skottie_test
diff --git a/images/index.js b/images/index.js
index 10fc63d..c8d68f1 100644
--- a/images/index.js
+++ b/images/index.js
@@ -48,7 +48,7 @@
{
name: 'renderer',
alias: 'r',
- type: (value) => (['svg', 'canvas'].includes(value) ? value : 'svg'),
+ type: (value) => (['svg', 'canvas', 'skottie'].includes(value) ? value : 'svg'),
description: 'The renderer to use',
},
];
@@ -68,20 +68,100 @@
const wait = (time) => new Promise((resolve) => setTimeout(resolve, time));
+const filesData = [
+ {
+ path: '/js/main.js',
+ filePath: './js/main.js',
+ type: 'js',
+
+ },
+ {
+ path: '/js/screenshot.js',
+ filePath: './js/screenshot.js',
+ type: 'js',
+
+ },
+ {
+ path: '/js/screenshot_skottie.js',
+ filePath: './js/screenshot_skottie.js',
+ type: 'js',
+
+ },
+ {
+ path: '/js/canvasSnapshot.js',
+ filePath: './js/canvasSnapshot.js',
+ type: 'js',
+
+ },
+ {
+ path: '/js/wait.js',
+ filePath: './js/wait.js',
+ type: 'js',
+
+ },
+ {
+ path: '/lottie.js',
+ filePath: 'node_modules/lottie-web/build/player/lottie.min.js',
+ type: 'js',
+
+ },
+ {
+ path: '/canvaskit.js',
+ filePath: 'node_modules/canvaskit-wasm/bin/full/canvaskit.js',
+ type: 'js',
+
+ },
+ {
+ path: '/lottie.json',
+ // filePath: '../examples/image.json',
+ filePath: '../examples/rectangle.json',
+ // filePath: './data.json',
+ type: 'json',
+ },
+ {
+ path: '/screenshot.html',
+ filePath: './screenshot.html',
+ type: 'html',
+ },
+ {
+ path: '/canvasKit.wasm',
+ filePath: 'node_modules/canvaskit-wasm/bin/full/canvaskit.wasm',
+ type: 'wasm',
+ },
+];
+
+const getEncoding = (() => {
+ const encodingMap = {
+ js: 'utf8',
+ json: 'utf8',
+ html: 'utf8',
+ };
+ return (fileType) => encodingMap[fileType];
+})();
+
+const getContentTypeHeader = (() => {
+ const contentTypeMap = {
+ js: { 'Content-Type': 'application/javascript' },
+ json: { 'Content-Type': 'application/json' },
+ html: { 'Content-Type': 'text/html; charset=utf-8' },
+ wasm: { 'Content-Type': 'application/wasm' },
+ };
+ return (fileType) => contentTypeMap[fileType];
+})();
+
const startServer = async () => {
- const lottieJS = await readFile('node_modules/lottie-web/build/player/lottie.min.js', 'utf8');
- const screenshotJS = await readFile('screenshot.js', 'utf8');
- const driverHTML = await readFile('screenshot.html', 'utf8');
- const lottieJSON = await readFile('../examples/rectangle.json', 'utf8');
const app = express();
- app.get('/screenshot.html', (req, res) => res.send(driverHTML));
- app.get('/screenshot_live.html', async (req, res) => {
- const file = await readFile('screenshot.html', 'utf8');
- res.send(file);
- });
- app.get('/lottie.js', (req, res) => res.send(lottieJS));
- app.get('/screenshot.js', (req, res) => res.send(screenshotJS));
- app.get('/lottie.json', (req, res) => res.send(lottieJSON));
+ await Promise.all(filesData.map(async (file) => {
+ const fileData = await readFile(file.filePath, getEncoding(file.type));
+ app.get(file.path, async (req, res) => {
+ res.writeHead(200, getContentTypeHeader(file.type));
+ // TODO: comment line. Only for live updates.
+ // const fileData = await readFile(file.filePath, getEncoding(file.type));
+ res.end(fileData);
+ });
+ return file;
+ }));
+
app.get('/*', async (req, res) => {
try {
if (req.originalUrl.indexOf('.json') !== -1) {
diff --git a/images/js/canvasSnapshot.js b/images/js/canvasSnapshot.js
new file mode 100644
index 0000000..1ba2b46
--- /dev/null
+++ b/images/js/canvasSnapshot.js
@@ -0,0 +1,24 @@
+const snapshot = (canvas, container, width, height) => {
+ const canvasElement = document.createElement('canvas');
+ container.appendChild(canvasElement);
+ canvasElement.width = width;
+ canvasElement.height = height;
+ canvasElement.style.width = `${width}px`;
+ canvasElement.style.height = `${height}px`;
+ canvasElement.style.display = 'inline-block';
+ canvasElement.style.verticalAlign = 'top';
+ const canvasContext = canvasElement.getContext('2d');
+ canvasContext.drawImage(
+ canvas,
+ 0,
+ 0,
+ canvas.width,
+ canvas.height,
+ 0,
+ 0,
+ canvasElement.width,
+ canvasElement.height,
+ );
+};
+
+export default snapshot;
diff --git a/images/js/main.js b/images/js/main.js
new file mode 100644
index 0000000..de0bb1c
--- /dev/null
+++ b/images/js/main.js
@@ -0,0 +1,35 @@
+import lottieScreenshot from './screenshot.js'; // eslint-disable-line import/extensions
+import skottieScreenshot from './screenshot_skottie.js'; // eslint-disable-line import/extensions
+
+const buildRenderSettings = async (searchParams) => {
+ const defaultValues = {
+ renderer: 'skottie',
+ sampleRate: 1,
+ resolution: 1,
+ // path: 'lottie.json',
+ path: '../examples/image.json',
+ };
+ searchParams.forEach((value, key) => {
+ defaultValues[key] = value;
+ });
+
+ return defaultValues;
+};
+
+const start = async () => {
+ const url = new URL(window.location);
+ const renderSettings = await buildRenderSettings(url.searchParams);
+ if (renderSettings.renderer === 'canvas' || renderSettings.renderer === 'svg') {
+ await lottieScreenshot.start(renderSettings);
+ } else if (renderSettings.renderer === 'skottie') {
+ await skottieScreenshot.start(renderSettings);
+ }
+ window._finished = true; // eslint-disable-line no-underscore-dangle
+};
+
+try {
+ start();
+} catch (err) {
+ console.log('ERROR'); // eslint-disable-line no-console
+ console.log(err.message); // eslint-disable-line no-console
+}
diff --git a/images/js/screenshot.js b/images/js/screenshot.js
new file mode 100644
index 0000000..a765617
--- /dev/null
+++ b/images/js/screenshot.js
@@ -0,0 +1,82 @@
+import canvasSnapshot from './canvasSnapshot.js'; // eslint-disable-line import/extensions
+import wait from './wait.js'; // eslint-disable-line import/extensions
+
+const loadAnimation = async (renderSettings) => new Promise((resolve, reject) => {
+ const elem = document.getElementById('lottie');
+ const animData = {
+ container: elem,
+ renderer: renderSettings.renderer,
+ loop: true,
+ autoplay: true,
+ rendererSettings: {
+ progressiveLoad: false,
+ preserveAspectRatio: 'xMidYMid meet',
+ imagePreserveAspectRatio: 'xMidYMid meet',
+ filterSize: {
+ width: '500%',
+ height: '500%',
+ x: '-200%',
+ y: '-200%',
+ },
+ },
+ path: renderSettings.path,
+ };
+ const anim = lottie.loadAnimation(animData);
+ anim.addEventListener('DOMLoaded', () => {
+ resolve(anim);
+ elem.style.width = `${anim.animationData.w}px`;
+ elem.style.height = `${anim.animationData.h}px`;
+ });
+ anim.onError = (errorType) => {
+ reject(errorType);
+ };
+});
+
+const createSVGSnapshot = (element, container, width, height) => {
+ const innerContent = element.innerHTML;
+ const iframeElement = document.createElement('iframe');
+ container.appendChild(iframeElement);
+ iframeElement.style.width = `${width}px`;
+ iframeElement.style.height = `${height}px`;
+ iframeElement.style.border = 'none';
+ iframeElement.contentWindow.document.open();
+ iframeElement.contentWindow.document.write(innerContent);
+ iframeElement.contentWindow.document.close();
+};
+
+const takeSnapshots = async (anim, renderSettings) => {
+ let currentFrame = 0;
+ const sampleRate = renderSettings.sampleRate > 0 ? renderSettings.sampleRate : 1;
+ const elem = document.getElementById('lottie');
+ const snapshotsContainer = document.getElementById('snapshotsContainer');
+ const width = anim.animationData.w * renderSettings.resolution;
+ const height = anim.animationData.h * renderSettings.resolution;
+
+ while (currentFrame < anim.totalFrames) {
+ anim.resize();
+ anim.goToAndStop(currentFrame);
+ if (renderSettings.renderer === 'svg') {
+ createSVGSnapshot(elem, snapshotsContainer, width, height);
+ } else if (renderSettings.renderer === 'canvas') {
+ const canvas = elem.getElementsByTagName('canvas')[0];
+ canvasSnapshot(canvas, snapshotsContainer, width, height);
+ }
+ currentFrame += 1 / sampleRate;
+ await wait(1); // eslint-disable-line no-await-in-loop
+ }
+};
+
+const start = async (renderSettings) => {
+ try {
+ const anim = await loadAnimation(renderSettings);
+ await takeSnapshots(anim, renderSettings);
+ window._finished = true; // eslint-disable-line no-underscore-dangle
+ } catch (err) {
+ console.log('ERROR'); // eslint-disable-line no-console
+ console.log(err.message); // eslint-disable-line no-console
+ }
+};
+
+export default {
+ start,
+};
diff --git a/images/js/screenshot_skottie.js b/images/js/screenshot_skottie.js
new file mode 100644
index 0000000..1eb75de
--- /dev/null
+++ b/images/js/screenshot_skottie.js
@@ -0,0 +1,106 @@
+import canvasSnapshot from './canvasSnapshot.js'; // eslint-disable-line import/extensions
+import wait from './wait.js'; // eslint-disable-line import/extensions
+
+const loadSkottieModule = async () => {
+ const canvasKit = await window.CanvasKitInit({
+ locateFile: () => 'canvaskit.wasm',
+ });
+ return canvasKit;
+};
+
+const createCanvas = async (animationData) => {
+ const canvas = document.createElement('canvas');
+ canvas.width = animationData.w;
+ canvas.height = animationData.h;
+ canvas.setAttribute('id', 'skottie-canvas');
+ const lottie = document.getElementById('lottie');
+ lottie.appendChild(canvas);
+ return canvas;
+};
+
+const getAnimationData = async (rendererSettings) => {
+ const fetchResponse = await fetch(rendererSettings.path);
+ const animData = await fetchResponse.json();
+ return animData;
+};
+
+const createSkottiePlayer = async (canvasKit, animationData, canvas, assets) => {
+ // const { devicePixelRatio } = window; // TODO: check if using the pixel ratio is preferred.
+ const devicePixelRatio = 1;
+ canvas.width = devicePixelRatio * animationData.w; // eslint-disable-line no-param-reassign
+ canvas.height = devicePixelRatio * animationData.h; // eslint-disable-line no-param-reassign
+ const surface = canvasKit.MakeCanvasSurface(canvas);
+ const skcanvas = surface.getCanvas();
+ const animation = canvasKit.MakeManagedAnimation(
+ JSON.stringify(animationData), assets, '',
+ );
+
+ const goToAndStop = (pos) => {
+ animation.seekFrame(pos);
+ const bounds = canvasKit.LTRBRect(
+ 0,
+ 0,
+ animationData.w * devicePixelRatio,
+ animationData.h * devicePixelRatio,
+ );
+ animation.render(skcanvas, bounds);
+ surface.flush();
+ };
+ return {
+ goToAndStop,
+ };
+};
+
+const iterateFrames = async (player, animationData, canvas, renderSettings) => {
+ const snapshotsContainer = document.getElementById('snapshotsContainer');
+ let currentFrame = 0;
+ const sampleRate = renderSettings.sampleRate > 0 ? renderSettings.sampleRate : 1;
+ const totalFrames = animationData.op - animationData.ip;
+ while (currentFrame < totalFrames) {
+ player.goToAndStop(currentFrame);
+ canvasSnapshot(
+ canvas,
+ snapshotsContainer,
+ animationData.w * renderSettings.resolution,
+ animationData.h * renderSettings.resolution,
+ );
+ await wait(1); // eslint-disable-line no-await-in-loop
+ currentFrame += 1 / sampleRate;
+ }
+};
+
+const getAssets = async (animationData, rendererSettings) => {
+ const assets = {
+
+ };
+ await Promise.all(
+ animationData.assets
+ .filter((asset) => asset.p && asset.u)
+ .map(async (asset) => {
+ const assetPathParts = rendererSettings.path.split('/');
+ assetPathParts.pop();
+ const assetPath = `${assetPathParts.join('/')}/`;
+ const assetData = await fetch(assetPath + asset.u + asset.p);
+ assets[asset.p] = await assetData.arrayBuffer();
+ }),
+ );
+ return assets;
+};
+
+const start = async (rendererSettings) => {
+ try {
+ const canvasKit = await loadSkottieModule();
+ const animationData = await getAnimationData(rendererSettings);
+ const canvas = await createCanvas(animationData);
+ const assets = await getAssets(animationData, rendererSettings);
+ const skottiePlayer = await createSkottiePlayer(canvasKit, animationData, canvas, assets);
+ await iterateFrames(skottiePlayer, animationData, canvas, rendererSettings);
+ } catch (error) {
+ console.log('ERROR'); // eslint-disable-line no-console
+ console.log(error.message); // eslint-disable-line no-console
+ }
+};
+
+export default {
+ start,
+};
diff --git a/images/js/wait.js b/images/js/wait.js
new file mode 100644
index 0000000..009da81
--- /dev/null
+++ b/images/js/wait.js
@@ -0,0 +1,4 @@
+const wait = (time = 1) => new Promise((resolve) => setTimeout(resolve, time));
+
+export default wait;
+
diff --git a/images/package-lock.json b/images/package-lock.json
index 6c66d2d..39ec3e9 100644
--- a/images/package-lock.json
+++ b/images/package-lock.json
@@ -282,6 +282,11 @@
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
"dev": true
},
+ "canvaskit-wasm": {
+ "version": "0.30.0",
+ "resolved": "https://registry.npmjs.org/canvaskit-wasm/-/canvaskit-wasm-0.30.0.tgz",
+ "integrity": "sha512-iow8oe8NXJ5o16a1yZDp4rhEEhsfzAzmPFIIgIV/++QCPojM+5vXM2mS9GVRzdm2V3EWBc8Wbc7yDd854f7meQ=="
+ },
"chalk": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
diff --git a/images/package.json b/images/package.json
index 66602b3..1f83417 100644
--- a/images/package.json
+++ b/images/package.json
@@ -4,6 +4,7 @@
"description": "",
"main": "index.js",
"dependencies": {
+ "canvaskit-wasm": "^0.30.0",
"command-line-args": "^5.2.0",
"express": "^4.17.1",
"lottie-web": "^5.7.14",
diff --git a/images/screenshot.html b/images/screenshot.html
index d08c20e..06af0a1 100644
--- a/images/screenshot.html
+++ b/images/screenshot.html
@@ -25,6 +25,7 @@
</style>
<script src="lottie.js" ></script>
+ <script src="canvaskit.js" ></script>
@@ -33,7 +34,7 @@
<div id="lottie"></div>
<div id="snapshotsContainer"></div>
-<script src="screenshot.js" ></script>
+<script src="js/main.js" type="module" ></script>
</body>
</html>
diff --git a/images/screenshot.js b/images/screenshot.js
deleted file mode 100644
index d308974..0000000
--- a/images/screenshot.js
+++ /dev/null
@@ -1,116 +0,0 @@
-const wait = (time = 1) => new Promise((resolve) => setTimeout(resolve, time));
-
-const buildRenderSettings = async (searchParams) => {
- const defaultValues = {
- renderer: 'svg',
- sampleRate: 1,
- resolution: 1,
- path: 'lottie.json',
- };
- searchParams.forEach((value, key) => {
- defaultValues[key] = value;
- });
-
- return defaultValues;
-};
-
-const loadAnimation = async (renderSettings) => new Promise((resolve, reject) => {
- const elem = document.getElementById('lottie');
- const animData = {
- container: elem,
- renderer: renderSettings.renderer,
- loop: true,
- autoplay: true,
- rendererSettings: {
- progressiveLoad: false,
- preserveAspectRatio: 'xMidYMid meet',
- imagePreserveAspectRatio: 'xMidYMid meet',
- filterSize: {
- width: '500%',
- height: '500%',
- x: '-200%',
- y: '-200%',
- },
- },
- path: renderSettings.path,
- };
- const anim = lottie.loadAnimation(animData);
- anim.addEventListener('DOMLoaded', () => {
- resolve(anim);
- elem.style.width = `${anim.animationData.w}px`;
- elem.style.height = `${anim.animationData.h}px`;
- });
- anim.onError = (errorType) => {
- reject(errorType);
- };
-});
-
-const createSVGSnapshot = (element, container, width, height) => {
- const innerContent = element.innerHTML;
- const iframeElement = document.createElement('iframe');
- container.appendChild(iframeElement);
- iframeElement.style.width = `${width}px`;
- iframeElement.style.height = `${height}px`;
- iframeElement.style.border = 'none';
- iframeElement.contentWindow.document.open();
- iframeElement.contentWindow.document.write(innerContent);
- iframeElement.contentWindow.document.close();
-};
-
-const createCanvasSnapshot = (element, container, width, height) => {
- const innerContent = element.getElementsByTagName('canvas')[0];
- const canvasElement = document.createElement('canvas');
- container.appendChild(canvasElement);
- canvasElement.width = width;
- canvasElement.height = height;
- canvasElement.style.width = `${width}px`;
- canvasElement.style.height = `${height}px`;
- const canvasContext = canvasElement.getContext('2d');
- canvasContext.drawImage(
- innerContent,
- 0,
- 0,
- innerContent.width,
- innerContent.height,
- 0,
- 0,
- canvasElement.width,
- canvasElement.height,
- );
-};
-
-const takeSnapshots = async (anim, renderSettings) => {
- let currentFrame = 0;
- const sampleRate = renderSettings.sampleRate > 0 ? renderSettings.sampleRate : 1;
- const elem = document.getElementById('lottie');
- const snapshotsContainer = document.getElementById('snapshotsContainer');
- const width = anim.animationData.w * renderSettings.resolution;
- const height = anim.animationData.h * renderSettings.resolution;
-
- while (currentFrame < anim.totalFrames) {
- anim.resize();
- anim.goToAndStop(currentFrame);
- if (renderSettings.renderer === 'svg') {
- createSVGSnapshot(elem, snapshotsContainer, width, height);
- } else if (renderSettings.renderer === 'canvas') {
- createCanvasSnapshot(elem, snapshotsContainer, width, height);
- }
- currentFrame += 1 / sampleRate;
- await wait(1); // eslint-disable-line no-await-in-loop
- }
-};
-
-const start = async () => {
- const url = new URL(window.location);
- const renderSettings = await buildRenderSettings(url.searchParams);
- const anim = await loadAnimation(renderSettings);
- await takeSnapshots(anim, renderSettings);
- window._finished = true; // eslint-disable-line no-underscore-dangle
-};
-
-try {
- start();
-} catch (err) {
- console.log('ERROR'); // eslint-disable-line no-console
- console.log(err.message); // eslint-disable-line no-console
-}