asdflkfjkadslghuier
Version:
Player for animated stickers
246 lines (212 loc) • 7.22 kB
JavaScript
/* eslint-disable no-prototype-builtins */
/* eslint-disable no-inner-declarations */
/* eslint-disable no-undef */
// if ('function' === typeof importScripts) {
importScripts('lottie-wasm.js');
importScripts('pako-inflate.min.js');
function LottieItem(reqId, jsString, width, height, fps) {
this.stringOnWasmHeap = null;
this.handle = null;
this.frameCount = 0;
this.reqId = reqId;
this.width = width;
this.height = height;
this.fps = Math.max(1, Math.min(60, fps || 60));
this.dead = false;
this.init(jsString, width, height);
reply('loaded', this.reqId, this.frameCount, this.fps);
}
LottieItem.prototype.init = function(jsString) {
try {
this.handle = LottieWorker.Api.init();
this.stringOnWasmHeap = allocate(intArrayFromString(jsString), 'i8', 0);
this.frameCount = LottieWorker.Api.loadFromData(this.handle, this.stringOnWasmHeap);
LottieWorker.Api.resize(this.handle, this.width, this.height);
} catch (e) {
console.error('init LottieItem error:', e);
}
};
LottieItem.prototype.render = function(frameNo, segmentId, clamped) {
if (this.dead) return;
if (this.frameCount < frameNo || frameNo < 0) {
return;
}
try {
LottieWorker.Api.render(this.handle, frameNo);
const bufferPointer = LottieWorker.Api.buffer(this.handle);
const data = Module.HEAPU8.subarray(bufferPointer, bufferPointer + this.width * this.height * 4);
if (!clamped) {
clamped = new Uint8ClampedArray(data);
} else {
clamped.set(data);
}
reply('frame', this.reqId, frameNo, clamped, segmentId);
} catch (e) {
console.error('Render error:', e);
this.dead = true;
}
};
LottieItem.prototype.destroy = function() {
this.dead = true;
LottieWorker.Api.destroy(this.handle);
};
const items = {};
const LottieWorker = (function() {
const worker = {};
worker.Api = {};
function initApi() {
worker.Api = {
init: Module.cwrap('lottie_init', '', []),
destroy: Module.cwrap('lottie_destroy', '', ['number']),
resize: Module.cwrap('lottie_resize', '', ['number', 'number', 'number']),
buffer: Module.cwrap('lottie_buffer', 'number', ['number']),
frameCount: Module.cwrap('lottie_frame_count', 'number', ['number']),
render: Module.cwrap('lottie_render', '', ['number', 'number']),
loadFromData: Module.cwrap('lottie_load_from_data', 'number', ['number', 'number'])
};
}
worker.init = function() {
initApi();
reply('ready');
};
return worker;
})();
Module.onRuntimeInitialized = function() {
LottieWorker.init();
};
const queryableFunctions = {
loadFromData: function(reqId, url, width, height) {
getUrlContent(url, function(err, data) {
if (err) {
return console.warn("Can't fetch file " + url, err);
}
try {
const json = pako.inflate(data, { to: 'string' });
const json_parsed = JSON.parse(json);
// if (!json_parsed.tgs) {
// throw new Error('Invalid file');
// }
items[reqId] = new LottieItem(reqId, json, width, height, json_parsed.fr);
} catch (e) {
return console.warn('Invalid file ' + url);
}
});
},
loadFromBlob: function(reqId, blob, width, height) {
const reader = new FileReader();
reader.onload = async e => {
try {
const json = pako.inflate(e.target.result, { to: 'string' });
const json_parsed = JSON.parse(json);
// if (!json_parsed.tgs) {
// throw new Error('Invalid file');
// }
items[reqId] = new LottieItem(reqId, json, width, height, json_parsed.fr);
} catch (e) {
return console.warn('Invalid blob ' + reqId);
}
};
reader.readAsArrayBuffer(blob);
},
loadFromJson: function(reqId, json, width, height) {
// console.log('json', JSON.parse(JSON.stringify(json)));
try {
const json_parsed = JSON.parse(json);
// if (!json_parsed.tgs) {
// throw new Error('Invalid file');
// }
items[reqId] = new LottieItem(reqId, json, width, height, json_parsed.fr);
} catch (e) {
console.log('e', e);
return console.warn('Invalid file ' + e);
}
},
destroy: function(reqId) {
items[reqId].destroy();
delete items[reqId];
},
renderFrame: function(reqId, frameNo, segmentId, clamped) {
items[reqId].render(frameNo, segmentId, clamped);
}
};
function defaultReply(message) {
// your default PUBLIC function executed only when main page calls the queryableWorker.postMessage() method directly
// do something
}
/**
* Returns true when run in WebKit derived browsers.
* This is used as a workaround for a memory leak in Safari caused by using Transferable objects to
* transfer data between WebWorkers and the main thread.
* https://github.com/mapbox/mapbox-gl-js/issues/8771
*
* This should be removed once the underlying Safari issue is fixed.
*
* @private
* @param scope {WindowOrWorkerGlobalScope} Since this function is used both on the main thread and WebWorker context,
* let the calling scope pass in the global scope object.
* @returns {boolean}
*/
var _isSafari = null;
function isSafari(scope) {
if (_isSafari == null) {
var userAgent = scope.navigator ? scope.navigator.userAgent : null;
_isSafari =
!!scope.safari || !!(userAgent && (/\b(iPad|iPhone|iPod)\b/.test(userAgent) || (!!userAgent.match('Safari') && !userAgent.match('Chrome'))));
}
return _isSafari;
}
function reply() {
if (arguments.length < 1) {
throw new TypeError('reply - not enough arguments');
}
var args = Array.prototype.slice.call(arguments, 1);
if (isSafari(self)) {
postMessage({ queryMethodListener: arguments[0], queryMethodArguments: args });
} else {
let transfer = [];
for (let i = 0; i < args.length; i++) {
if (args[i] === undefined) {
continue;
}
if (args[i] instanceof ArrayBuffer) {
transfer.push(args[i]);
}
if (args[i].buffer && args[i].buffer instanceof ArrayBuffer) {
transfer.push(args[i].buffer);
}
}
postMessage({ queryMethodListener: arguments[0], queryMethodArguments: args }, transfer);
}
}
onmessage = function(oEvent) {
if (oEvent.data instanceof Object && oEvent.data.hasOwnProperty('queryMethod') && oEvent.data.hasOwnProperty('queryMethodArguments')) {
queryableFunctions[oEvent.data.queryMethod].apply(self, oEvent.data.queryMethodArguments);
} else {
defaultReply(oEvent.data);
}
};
function getUrlContent(path, callback) {
try {
const xhr = new XMLHttpRequest();
xhr.open('GET', path, true);
if ('responseType' in xhr) {
xhr.responseType = 'arraybuffer';
}
if (xhr.overrideMimeType) {
xhr.overrideMimeType('text/plain; charset=x-user-defined');
}
xhr.onreadystatechange = function(event) {
if (xhr.readyState === 4) {
if (xhr.status === 200 || xhr.status === 0) {
callback(null, xhr.response || xhr.responseText);
} else {
callback(new Error('Ajax error: ' + this.status + ' ' + this.statusText));
}
}
};
xhr.send();
} catch (e) {
callback(new Error(e));
}
}
// }