UNPKG

chayns-components

Version:

A set of beautiful React components for developing chayns® applications.

305 lines (287 loc) 8.87 kB
/* Creates a worker script like with following method but ensures the code is not further transpiled which could cause problems let workerScript = [exportCanvasSized] .map((f) => `const ${f.name} = ${f.toString()}`) .join('\n'); workerScript += `;\n(${workerThread.toString()})()`; */ const workerScript = ` const exportCanvasSized = async ( origCanvas, fileName, maxFileSize, type, quality, isIOS ) => { let tempCanvas; let scale = 1; let step = 1; let lastFitBlob = null; if (isIOS) { scale = Math.min(1200 / origCanvas.width, 1200 / origCanvas.height, 1); step = scale; } for (let i = 0; i < 10; i += 1) { // eslint-disable-next-line no-await-in-loop await new Promise((resolve) => { setTimeout(resolve, 10); }); if (typeof document !== 'undefined') { if (!tempCanvas) { tempCanvas = document.createElement('canvas'); // eslint-disable-next-line no-loop-func tempCanvas.convertToBlob = (options) => canvasToBlob(tempCanvas, options); } tempCanvas.width = origCanvas.width * scale; tempCanvas.height = origCanvas.height * scale; } else if (typeof OffscreenCanvas === 'function') { postMessage({ width: Math.round(origCanvas.width * scale), height: Math.round(origCanvas.height * scale), scale, }); tempCanvas = new OffscreenCanvas( Math.round(origCanvas.width * scale), Math.round(origCanvas.height * scale) ); } else { throw new Error('offscreen canvas not supported'); } if (isIOS) { if (tempCanvas.width > 1200 || tempCanvas.height > 1200) break; } tempCanvas .getContext('2d') .drawImage( origCanvas, 0, 0, origCanvas.width, origCanvas.height, 0, 0, tempCanvas.width, tempCanvas.height ); // eslint-disable-next-line no-await-in-loop const blob = await tempCanvas.convertToBlob({ type, quality }); const blobSize = blob?.size ?? maxFileSize + 1; if (blobSize > maxFileSize) { step *= 0.5; scale -= step; } else if (blobSize < maxFileSize) { lastFitBlob = blob; if (scale === 1) { break; } if (blobSize > maxFileSize * 0.8) { break; } step *= 0.5; scale += step; } } if (!lastFitBlob) { throw new Error( 'failed to compress image down to maxFileSize within 10 iterations' ); } return new File([lastFitBlob], fileName, { type }); }; (() => { // eslint-disable-next-line no-restricted-globals self.onmessage = async (ev) => { const { file, maxFileSize, quality, isIOS } = ev.data; const bitmap = await createImageBitmap(file); const canvas = new OffscreenCanvas(bitmap.width, bitmap.height); const ctx = canvas.getContext('2d'); ctx.drawImage(bitmap, 0, 0); const type = file.type === 'image/jpg' ? 'image/jpg' : 'image/webp'; try { const result = await exportCanvasSized( canvas, file.name, maxFileSize, type, quality, isIOS ); postMessage({ success: true, file: result }); } catch (e) { postMessage({ success: false, error: e }); } }; })() `; const blobToDataUrl = blob => new Promise((resolve, reject) => { const fr = new FileReader(); fr.onload = e => { resolve(e.target.result); }; fr.onerror = e => { reject(e); }; fr.readAsDataURL(blob); }); const blobToCanvas = async blob => { const url = await blobToDataUrl(blob); const img = new Image(); await new Promise((resolve, reject) => { img.onload = resolve; img.onerror = reject; img.src = url; }); const $canvas = document.createElement('canvas'); $canvas.width = img.width; $canvas.height = img.height; const ctx = $canvas.getContext('2d'); ctx.drawImage(img, 0, 0); return $canvas; }; const canvasToBlob = (canvas, _ref) => { let { type = 'image/webp', quality = 0.8 } = _ref; return new Promise((resolve, reject) => { try { canvas.toBlob(blob => { if (blob) { resolve(blob); } else { reject(); } }, type, quality); } catch (e) { reject(e); } }); }; const exportCanvasSized = async (origCanvas, fileName, maxFileSize, type, quality, isIOS) => { let tempCanvas; let scale = 1; let step = 1; let lastFitBlob = null; if (isIOS) { scale = Math.min(1200 / origCanvas.width, 1200 / origCanvas.height, 1); step = scale; } for (let i = 0; i < 10; i += 1) { var _blob$size; // eslint-disable-next-line no-await-in-loop await new Promise(resolve => { setTimeout(resolve, 10); }); if (typeof document !== 'undefined') { if (!tempCanvas) { tempCanvas = document.createElement('canvas'); // eslint-disable-next-line no-loop-func tempCanvas.convertToBlob = options => canvasToBlob(tempCanvas, options); } tempCanvas.width = origCanvas.width * scale; tempCanvas.height = origCanvas.height * scale; } else if (typeof OffscreenCanvas === 'function') { postMessage({ width: Math.round(origCanvas.width * scale), height: Math.round(origCanvas.height * scale), scale }); tempCanvas = new OffscreenCanvas(Math.round(origCanvas.width * scale), Math.round(origCanvas.height * scale)); } else { throw new Error('offscreen canvas not supported'); } if (isIOS) { if (tempCanvas.width > 1200 || tempCanvas.height > 1200) break; } tempCanvas.getContext('2d').drawImage(origCanvas, 0, 0, origCanvas.width, origCanvas.height, 0, 0, tempCanvas.width, tempCanvas.height); // eslint-disable-next-line no-await-in-loop const blob = await tempCanvas.convertToBlob({ type, quality }); const blobSize = (_blob$size = blob === null || blob === void 0 ? void 0 : blob.size) !== null && _blob$size !== void 0 ? _blob$size : maxFileSize + 1; if (blobSize > maxFileSize) { step *= 0.5; scale -= step; } else if (blobSize < maxFileSize) { lastFitBlob = blob; if (scale === 1) { break; } if (blobSize > maxFileSize * 0.8) { break; } step *= 0.5; scale += step; } } if (!lastFitBlob) { throw new Error('failed to compress image down to maxFileSize within 10 iterations'); } return new File([lastFitBlob], fileName, { type }); }; const workerThread = () => { // eslint-disable-next-line no-restricted-globals self.onmessage = async ev => { const { file, maxFileSize, quality, isIOS } = ev.data; const bitmap = await createImageBitmap(file); const canvas = new OffscreenCanvas(bitmap.width, bitmap.height); const ctx = canvas.getContext('2d'); ctx.drawImage(bitmap, 0, 0); const type = file.type === 'image/jpg' ? 'image/jpg' : 'image/webp'; try { const result = await exportCanvasSized(canvas, file.name, maxFileSize, type, quality, isIOS); postMessage({ success: true, file: result }); } catch (e) { postMessage({ success: false, error: e }); } }; }; const compressImage = async function (file, maxFileSize, quality) { if (quality === void 0) { quality = 0.8; } if (typeof OffscreenCanvas !== 'function') { const origCanvas = await blobToCanvas(file); const type = file.type === 'image/jpg' ? 'image/jpg' : 'image/webp'; return exportCanvasSized(origCanvas, file.name, maxFileSize, type, quality, chayns.env.isIOS); } const blob = new Blob([workerScript], { type: 'application/javascript' }); const url = URL.createObjectURL(blob); return new Promise((resolve, reject) => { const worker = new Worker(url); worker.onmessage = ev => { if (typeof ev.data.success !== 'boolean') return; if (ev.data.success) { resolve(ev.data.file); } else { reject(ev.data.error); } worker.terminate(); }; worker.postMessage({ file, maxFileSize, quality, isIOS: chayns.env.isIOS }); }); }; export default compressImage; //# sourceMappingURL=compressImage.js.map