blob: 63c41a98cd2ed762ac521baa88e2b8573ecf5c2e [file] [log] [blame]
function workerContent() {
var localIdCounter = 0;
var animations = {};
var styleProperties = ['width', 'height', 'display', 'transform', 'opacity', 'contentVisibility', 'mix-blend-mode'];
function createElement(namespace, type) {
var style = {
serialize: function () {
var obj = {};
for (var i = 0; i < styleProperties.length; i += 1) {
var propertyKey = styleProperties[i];
var keyName = '_' + propertyKey;
if (keyName in this) {
obj[propertyKey] = this[keyName];
}
}
return obj;
},
};
styleProperties.forEach(function (propertyKey) {
Object.defineProperty(style, propertyKey, {
set: function (value) {
if (!element._isDirty) {
element._isDirty = true;
}
element._changedStyles.push(propertyKey);
var keyName = '_' + propertyKey;
this[keyName] = value;
},
get: function () {
var keyName = '_' + propertyKey;
return this[keyName];
},
});
});
localIdCounter += 1;
var element = {
_state: 'init',
_isDirty: false,
_changedStyles: [],
_changedAttributes: [],
_changedElements: [],
_textContent: null,
type: type,
namespace: namespace,
children: [],
attributes: {
id: 'l_d_' + localIdCounter,
},
style: style,
appendChild: function (child) {
child.parentNode = this;
this.children.push(child);
this._isDirty = true;
this._changedElements.push([child, this.attributes.id]);
},
insertBefore: function (newElement, nextElement) {
var children = this.children;
for (var i = 0; i < children.length; i += 1) {
if (children[i] === nextElement) {
children.splice(i, 0, newElement);
this._isDirty = true;
this._changedElements.push([newElement, this.attributes.id, nextElement.attributes.id]);
return;
}
}
children.push(nextElement);
},
setAttribute: function (attribute, value) {
this.attributes[attribute] = value;
if (!element._isDirty) {
element._isDirty = true;
}
element._changedAttributes.push(attribute);
},
serialize: function () {
return {
type: this.type,
namespace: this.namespace,
style: this.style.serialize(),
attributes: this.attributes,
children: this.children.map(function (child) { return child.serialize(); }),
textContent: this._textContent,
};
},
getContext: function () { return { fillRect: function () {} }; },
addEventListener: function (_, callback) {
setTimeout(callback, 1);
},
setAttributeNS: function (_, attribute, value) {
this.attributes[attribute] = value;
if (!element._isDirty) {
element._isDirty = true;
}
element._changedAttributes.push(attribute);
},
};
element.style = style;
Object.defineProperty(element, 'textContent', {
set: function (value) {
element._isDirty = true;
element._textContent = value;
},
});
return element;
}
var window = self; // eslint-disable-line no-redeclare, no-unused-vars
var document = { // eslint-disable-line no-redeclare
createElementNS: function (namespace, type) {
return createElement(namespace, type);
},
createElement: function (type) {
return createElement('', type);
},
getElementsByTagName: function () {
return [];
},
};
/* eslint-enable */
var lottieInternal = (function () {
'use strict';
/* <%= contents %> */
function addElementToList(element, list) {
list.push(element);
element._isDirty = false;
element._changedStyles.length = 0;
element._changedAttributes.length = 0;
element._changedElements.length = 0;
element._textContent = null;
element.children.forEach(function (child) {
addElementToList(child, list);
});
}
function addChangedAttributes(element) {
var changedAttributes = element._changedAttributes;
var attributes = [];
var attribute;
for (var i = 0; i < changedAttributes.length; i += 1) {
attribute = changedAttributes[i];
attributes.push([attribute, element.attributes[attribute]]);
}
return attributes;
}
function addChangedStyles(element) {
var changedStyles = element._changedStyles;
var styles = [];
var style;
for (var i = 0; i < changedStyles.length; i += 1) {
style = changedStyles[i];
styles.push([style, element.style[style]]);
}
return styles;
}
function addChangedElements(element, elements) {
var changedElements = element._changedElements;
var elementsList = [];
var elementData;
for (var i = 0; i < changedElements.length; i += 1) {
elementData = changedElements[i];
elementsList.push([elementData[0].serialize(), elementData[1], elementData[2]]);
addElementToList(elementData[0], elements);
}
return elementsList;
}
function loadAnimation(payload) {
var params = payload.params;
var wrapper;
var animation;
var elements = [];
if (params.renderer === 'svg') {
wrapper = document.createElement('div');
params.container = wrapper;
} else {
var canvas = params.rendererSettings.canvas;
var ctx = canvas.getContext('2d');
params.rendererSettings.context = ctx;
}
animation = lottie.loadAnimation(params);
animation.addEventListener('error', function (error) {
console.log(error); // eslint-disable-line
});
animation.onError = function (error) {
console.log('ERRORO', error); // eslint-disable-line
};
animation.addEventListener('_play', function () {
self.postMessage({
type: 'playing',
payload: {
id: payload.id,
},
});
});
animation.addEventListener('_pause', function () {
self.postMessage({
type: 'paused',
payload: {
id: payload.id,
},
});
});
if (params.renderer === 'svg') {
animation.addEventListener('DOMLoaded', function () {
var serialized = wrapper.serialize();
addElementToList(wrapper, elements);
self.postMessage({
type: 'loaded',
payload: {
id: payload.id,
tree: serialized.children[0],
totalFrames: animation.totalFrames,
frameRate: animation.frameRate,
},
});
});
animation.addEventListener('drawnFrame', function (event) {
var changedElements = [];
var element;
for (var i = 0; i < elements.length; i += 1) {
element = elements[i];
if (element._isDirty) {
var changedElement = {
id: element.attributes.id,
styles: addChangedStyles(element),
attributes: addChangedAttributes(element),
elements: addChangedElements(element, elements),
textContent: element._textContent || undefined,
};
changedElements.push(changedElement);
element._isDirty = false;
element._changedAttributes.length = 0;
element._changedStyles.length = 0;
element._changedElements.length = 0;
element._textContent = null;
}
}
self.postMessage({
type: 'updated',
payload: {
elements: changedElements,
id: payload.id,
currentTime: event.currentTime,
},
});
});
}
animations[payload.id] = {
animation: animation,
events: {},
};
}
return {
loadAnimation: loadAnimation,
};
}({}));
onmessage = function (evt) {
var data = evt.data;
var type = data.type;
var payload = data.payload;
if (type === 'load') {
lottieInternal.loadAnimation(payload);
} else if (type === 'pause') {
if (animations[payload.id]) {
animations[payload.id].animation.pause();
}
} else if (type === 'play') {
if (animations[payload.id]) {
animations[payload.id].animation.play();
}
} else if (type === 'stop') {
if (animations[payload.id]) {
animations[payload.id].animation.stop();
}
} else if (type === 'setSpeed') {
if (animations[payload.id]) {
animations[payload.id].animation.setSpeed(payload.value);
}
} else if (type === 'setDirection') {
if (animations[payload.id]) {
animations[payload.id].animation.setDirection(payload.value);
}
} else if (type === 'setDirection') {
if (animations[payload.id]) {
animations[payload.id].animation.setDirection(payload.value);
}
} else if (type === 'goToAndPlay') {
if (animations[payload.id]) {
animations[payload.id].animation.goToAndPlay(payload.value, payload.isFrame);
}
} else if (type === 'goToAndStop') {
if (animations[payload.id]) {
animations[payload.id].animation.goToAndStop(payload.value, payload.isFrame);
}
} else if (type === 'setSubframe') {
if (animations[payload.id]) {
animations[payload.id].animation.setSubframe(payload.value);
}
} else if (type === 'addEventListener') {
if (animations[payload.id]) {
var eventCallback = function () {
self.postMessage({
type: 'event',
payload: {
id: payload.id,
callbackId: payload.callbackId,
argument: arguments[0],
},
});
};
animations[payload.id].events[payload.callbackId] = {
callback: eventCallback,
};
animations[payload.id].animation.addEventListener(payload.eventName, eventCallback);
}
} else if (type === 'removeEventListener') {
if (animations[payload.id]) {
var callback = animations[payload.id].events[payload.callbackId];
animations[payload.id].animation.removeEventListener(payload.eventName, callback);
}
} else if (type === 'destroy') {
if (animations[payload.id]) {
animations[payload.id].animation.destroy();
animations[payload.id] = null;
}
} else if (type === 'resize') {
if (animations[payload.id]) {
animations[payload.id].animation.resize();
}
} else if (type === 'playSegments') {
if (animations[payload.id]) {
animations[payload.id].animation.playSegments(payload.arr, payload.forceFlag);
}
} else if (type === 'updateDocumentData') {
animations[payload.id].animation.updateDocumentData(payload.path, payload.documentData, payload.index);
}
};
}
function createWorker(fn) {
var blob = new Blob(['(' + fn.toString() + '())'], { type: 'text/javascript' });
var url = URL.createObjectURL(blob);
return new Worker(url);
}
// eslint-disable-next-line no-unused-vars
var lottie = (function () {
'use strict';
var workerInstance = createWorker(workerContent);
var animationIdCounter = 0;
var eventsIdCounter = 0;
var animations = {};
var defaultSettings = {
rendererSettings: {},
};
function createTree(data, container, map, afterElement) {
var elem;
if (data.type === 'div') {
elem = document.createElement('div');
} else {
elem = document.createElementNS(data.namespace, data.type);
}
if (data.textContent) {
elem.textContent = data.textContent;
}
for (var attr in data.attributes) {
if (Object.prototype.hasOwnProperty.call(data.attributes, attr)) {
if (attr === 'href') {
elem.setAttributeNS('http://www.w3.org/1999/xlink', attr, data.attributes[attr]);
} else {
elem.setAttribute(attr, data.attributes[attr]);
}
if (attr === 'id') {
map[data.attributes[attr]] = elem;
}
}
}
for (var style in data.style) {
if (Object.prototype.hasOwnProperty.call(data.style, style)) {
elem.style[style] = data.style[style];
}
}
data.children.forEach(function (element) {
createTree(element, elem, map);
});
if (!afterElement) {
container.appendChild(elem);
} else {
container.insertBefore(elem, afterElement);
}
}
var handleAnimationLoaded = (function () {
return function (payload) {
var animation = animations[payload.id];
animation._loaded = true;
// if callbacks have been added before the animation has loaded
animation.pendingCallbacks.forEach(function (callbackData) {
animation.animInstance.addEventListener(callbackData.eventName, callbackData.callback);
if (callbackData.eventName === 'DOMLoaded') {
callbackData.callback();
}
});
animation.animInstance.totalFrames = payload.totalFrames;
animation.animInstance.frameRate = payload.frameRate;
var container = animation.container;
var elements = animation.elements;
createTree(payload.tree, container, elements);
};
}());
function addNewElements(newElements, elements) {
var element;
for (var i = 0; i < newElements.length; i += 1) {
element = newElements[i];
var parent = elements[element[1]];
if (parent) {
var sibling;
if (element[2]) {
sibling = elements[element[2]];
}
createTree(element[0], parent, elements, sibling);
newElements.splice(i, 1);
i -= 1;
}
}
}
function updateElementStyles(element, styles) {
var style;
for (var i = 0; i < styles.length; i += 1) {
style = styles[i];
element.style[style[0]] = style[1];
}
}
function updateElementAttributes(element, attributes) {
var attribute;
for (var i = 0; i < attributes.length; i += 1) {
attribute = attributes[i];
element.setAttribute(attribute[0], attribute[1]);
}
}
function updateTextContent(element, text) {
if (text) {
element.textContent = text;
}
}
function handleAnimationUpdate(payload) {
var changedElements = payload.elements;
var animation = animations[payload.id];
if (animation) {
var elements = animation.elements;
var elementData;
for (var i = 0; i < changedElements.length; i += 1) {
elementData = changedElements[i];
var element = elements[elementData.id];
addNewElements(elementData.elements, elements);
updateElementStyles(element, elementData.styles);
updateElementAttributes(element, elementData.attributes);
updateTextContent(element, elementData.textContent);
}
animation.animInstance.currentFrame = payload.currentTime;
}
}
function handleEvent(payload) {
var animation = animations[payload.id];
if (animation) {
var callbacks = animation.callbacks;
if (callbacks[payload.callbackId]) {
callbacks[payload.callbackId].callback(payload.argument);
}
}
}
function handlePlaying(payload) {
var animation = animations[payload.id];
if (animation) {
animation.animInstance.isPaused = false;
}
}
function handlePaused(payload) {
var animation = animations[payload.id];
if (animation) {
animation.animInstance.isPaused = true;
}
}
var messageHandlers = {
loaded: handleAnimationLoaded,
updated: handleAnimationUpdate,
event: handleEvent,
playing: handlePlaying,
paused: handlePaused,
};
workerInstance.onmessage = function (event) {
if (messageHandlers[event.data.type]) {
messageHandlers[event.data.type](event.data.payload);
}
};
function resolveAnimationData(params) {
return new Promise(function (resolve, reject) {
var paramsCopy = Object.assign({}, defaultSettings, params);
if (paramsCopy.animType && !paramsCopy.renderer) {
paramsCopy.renderer = paramsCopy.animType;
}
if (paramsCopy.wrapper) {
if (!paramsCopy.container) {
paramsCopy.container = paramsCopy.wrapper;
}
delete paramsCopy.wrapper;
}
if (paramsCopy.animationData) {
resolve(paramsCopy);
} else if (paramsCopy.path) {
fetch(paramsCopy.path)
.then(function (response) {
return response.json();
})
.then(function (animationData) {
paramsCopy.animationData = animationData;
delete paramsCopy.path;
resolve(paramsCopy);
});
} else {
reject();
}
});
}
function loadAnimation(params) {
animationIdCounter += 1;
var animationId = 'lottie_animationId_' + animationIdCounter;
var animation = {
elements: {},
callbacks: {},
pendingCallbacks: [],
status: 'init',
};
var animInstance = {
id: animationId,
isPaused: true,
pause: function () {
workerInstance.postMessage({
type: 'pause',
payload: {
id: animationId,
},
});
},
play: function () {
workerInstance.postMessage({
type: 'play',
payload: {
id: animationId,
},
});
},
stop: function () {
workerInstance.postMessage({
type: 'stop',
payload: {
id: animationId,
},
});
},
setSpeed: function (value) {
workerInstance.postMessage({
type: 'setSpeed',
payload: {
id: animationId,
value: value,
},
});
},
setDirection: function (value) {
workerInstance.postMessage({
type: 'setDirection',
payload: {
id: animationId,
value: value,
},
});
},
goToAndStop: function (value, isFrame) {
workerInstance.postMessage({
type: 'goToAndStop',
payload: {
id: animationId,
value: value,
isFrame: isFrame,
},
});
},
goToAndPlay: function (value, isFrame) {
workerInstance.postMessage({
type: 'goToAndPlay',
payload: {
id: animationId,
value: value,
isFrame: isFrame,
},
});
},
playSegments: function (arr, forceFlag) {
workerInstance.postMessage({
type: 'playSegments',
payload: {
id: animationId,
arr: arr,
forceFlag: forceFlag,
},
});
},
setSubframe: function (value) {
workerInstance.postMessage({
type: 'setSubframe',
payload: {
id: animationId,
value: value,
},
});
},
addEventListener: function (eventName, callback) {
if (!animation._loaded) {
animation.pendingCallbacks.push({
eventName: eventName,
callback: callback,
});
} else {
eventsIdCounter += 1;
var callbackId = 'callback_' + eventsIdCounter;
animation.callbacks[callbackId] = {
eventName: eventName,
callback: callback,
};
workerInstance.postMessage({
type: 'addEventListener',
payload: {
id: animationId,
callbackId: callbackId,
eventName: eventName,
},
});
}
},
removeEventListener: function (eventName, callback) {
Object.keys(animation.callbacks)
.forEach(function (key) {
if (animation.callbacks[key].eventName === eventName
&& (animation.callbacks[key].callback === callback || !callback)) {
delete animation.callbacks[key];
workerInstance.postMessage({
type: 'removeEventListener',
payload: {
id: animationId,
callbackId: key,
eventName: eventName,
},
});
}
});
},
destroy: function () {
if (animation.status === 'init') {
animation.status = 'destroyable';
} else {
animation.status = 'destroyed';
animations[animationId] = null;
if (animation.container) {
animation.container.innerHTML = '';
}
workerInstance.postMessage({
type: 'destroy',
payload: {
id: animationId,
},
});
}
},
resize: function () {
workerInstance.postMessage({
type: 'resize',
payload: {
id: animationId,
},
});
},
updateDocumentData: function (path, documentData, index) {
workerInstance.postMessage({
type: 'updateDocumentData',
payload: {
id: animationId,
path: path,
documentData: documentData,
index: index,
},
});
},
};
animation.animInstance = animInstance;
resolveAnimationData(params)
.then(function (animationParams) {
if (animation.status === 'destroyable') {
animation.animInstance.destroy();
return;
}
animation.status = 'loaded';
var transferedObjects = [];
if (animationParams.container) {
animation.container = animationParams.container;
delete animationParams.container;
}
if (animationParams.renderer === 'canvas') {
var canvas = animationParams.rendererSettings.canvas;
// If no custom canvas was passed
if (!canvas) {
canvas = document.createElement('canvas');
animation.container.appendChild(canvas);
canvas.width = animationParams.animationData.w;
canvas.height = animationParams.animationData.h;
canvas.style.width = '100%';
canvas.style.height = '100%';
}
// Transfer control to offscreen if it's not already
var offscreen = canvas instanceof OffscreenCanvas ? canvas : canvas.transferControlToOffscreen();
animationParams.rendererSettings.canvas = offscreen;
transferedObjects.push(animationParams.rendererSettings.canvas);
}
animations[animationId] = animation;
workerInstance.postMessage({
type: 'load',
payload: {
params: animationParams,
id: animationId,
},
}, transferedObjects);
});
return animInstance;
}
var lottiejs = {
loadAnimation: loadAnimation,
};
return lottiejs;
}());