blob: 87da9e1ccee8eba35c122f2e3baeae6d000d831d [file] [log] [blame]
import canvasSnapshot from './canvasSnapshot.js'; // eslint-disable-line import/extensions
import wait from './wait.js'; // eslint-disable-line import/extensions
import puppeteerHelper from './puppeteerHelper.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, elementSize) => {
const innerContent = element.innerHTML;
const iframeElement = document.createElement('iframe');
container.appendChild(iframeElement);
iframeElement.style.width = `${elementSize.width}px`;
iframeElement.style.height = `${elementSize.height}px`;
iframeElement.style.padding = `${elementSize.yPadding * 0.5}px ${elementSize.xPadding * 0.5}px`;
iframeElement.style.border = 'none';
iframeElement.style.lineHeight = '0';
iframeElement.contentWindow.document.open();
iframeElement.contentWindow.document.write(innerContent);
iframeElement.contentWindow.document.close();
return iframeElement;
};
const calculateFrameAdvance = (renderSettings, anim) => {
const sampleRate = renderSettings.sampleRate > 0 ? renderSettings.sampleRate : 1;
const traversedFrames = anim.totalFrames / sampleRate;
let advance = 1 / sampleRate;
if (renderSettings.frames && renderSettings.frames < traversedFrames) {
advance = anim.totalFrames / renderSettings.frames;
}
return advance;
};
const calculateGridSize = (renderSettings) => {
const grid = renderSettings.grid?.split('x');
if (grid && grid.length && grid.length === 2) {
const width = parseInt(grid[0]);
const height = parseInt(grid[1]);
if (typeof width === 'number' && isFinite(width) && typeof height === 'number' && isFinite(height)) {
return {
width,
height,
}
}
}
return null;
}
const calculateElementSize = (frameAdvance, anim, grid) => {
if (!grid) {
return {
width: anim.animationData.w,
height: anim.animationData.h,
}
} else {
const totalFrames = anim.totalFrames / frameAdvance;
const maxColumns = 30;
const cellCandidate = {
width: 0,
height: 0,
};
const elementRatio = anim.animationData.w / anim.animationData.h;
for (let i = 1; i <= maxColumns; i += 1) {
const cellWidth = grid.width / i;
const cellHeight = cellWidth / elementRatio;
const totalRows = Math.ceil(totalFrames / i);
if (totalRows * cellHeight < grid.height) {
if (cellWidth * cellHeight > cellCandidate.width * cellCandidate.height) {
cellCandidate.width = cellWidth;
cellCandidate.height = cellHeight;
cellCandidate.xPadding = (grid.width - cellWidth * i) / (i)
cellCandidate.yPadding = (grid.height - cellHeight * totalRows) / (totalRows)
}
}
}
return cellCandidate;
}
}
const takeSnapshots = async (anim, renderSettings) => {
const frameAdvance = calculateFrameAdvance(renderSettings, anim);
const grid = calculateGridSize(renderSettings);
const elementSize = calculateElementSize(frameAdvance, anim, grid);
let currentFrame = 0;
const container = document.getElementById('lottie');
const snapshotsContainer = document.getElementById('snapshotsContainer');
snapshotsContainer.style.lineHeight = 0;
// const width = anim.animationData.w * renderSettings.resolution;
// const height = anim.animationData.h * renderSettings.resolution;
// TODO: use resolution
const width = elementSize.width;
const height = elementSize.height;
container.style.width = `${width}px`;
container.style.height = `${height}px`;
// TODO: comment these lines
if (grid) {
snapshotsContainer.style.width = `${grid.width}px`;
// snapshotsContainer.style.height = `${grid.height}px`;
}
while (currentFrame < anim.totalFrames) {
// Disabling rule because execution can't be parallelized
/* eslint-disable no-await-in-loop */
anim.resize();
anim.goToAndStop(currentFrame, true);
let element;
if (renderSettings.renderer === 'svg') {
element = createSVGSnapshot(container, snapshotsContainer, elementSize);
} else if (renderSettings.renderer === 'canvas') {
const canvas = container.getElementsByTagName('canvas')[0];
element = canvasSnapshot(canvas, snapshotsContainer, width, height);
}
currentFrame += frameAdvance;
const isLastFrame = currentFrame + frameAdvance >= anim.totalFrames;
if (Number(renderSettings.individualAssets) === 0) {
await wait(1);
} else {
await puppeteerHelper.submitAndWaitForResponse(
currentFrame,
isLastFrame,
width,
height,
);
element.remove();
}
/* eslint-enable 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,
};