WIP PWA for Grain next.grain.social
at main 94 lines 2.3 kB view raw
1export function readFileAsDataURL(file) { 2 return new Promise((resolve, reject) => { 3 const reader = new FileReader(); 4 reader.onload = () => resolve(reader.result); 5 reader.onerror = reject; 6 reader.readAsDataURL(file); 7 }); 8} 9 10function getBase64Size(base64) { 11 const str = base64.split(',')[1] || base64; 12 return Math.ceil((str.length * 3) / 4); 13} 14 15function createResizedImage(dataUrl, options) { 16 return new Promise((resolve, reject) => { 17 const img = new Image(); 18 img.onload = () => { 19 const scale = Math.min( 20 options.width / img.width, 21 options.height / img.height, 22 1 23 ); 24 const w = Math.round(img.width * scale); 25 const h = Math.round(img.height * scale); 26 27 const canvas = document.createElement('canvas'); 28 canvas.width = w; 29 canvas.height = h; 30 const ctx = canvas.getContext('2d'); 31 32 ctx.fillStyle = '#fff'; 33 ctx.fillRect(0, 0, w, h); 34 ctx.imageSmoothingEnabled = true; 35 ctx.imageSmoothingQuality = 'high'; 36 ctx.drawImage(img, 0, 0, w, h); 37 38 resolve({ 39 dataUrl: canvas.toDataURL('image/jpeg', options.quality), 40 width: w, 41 height: h 42 }); 43 }; 44 img.onerror = reject; 45 img.src = dataUrl; 46 }); 47} 48 49export async function resizeImage(dataUrl, opts) { 50 let bestResult = null; 51 let minQuality = 0; 52 let maxQuality = 100; 53 54 while (maxQuality - minQuality > 1) { 55 const quality = Math.round((minQuality + maxQuality) / 2); 56 const result = await createResizedImage(dataUrl, { 57 width: opts.width, 58 height: opts.height, 59 quality: quality / 100 60 }); 61 62 const size = getBase64Size(result.dataUrl); 63 if (size <= opts.maxSize) { 64 bestResult = result; 65 minQuality = quality; 66 } else { 67 maxQuality = quality; 68 } 69 } 70 71 if (!bestResult) { 72 throw new Error('Failed to compress image within size limit'); 73 } 74 75 return bestResult; 76} 77 78export async function processPhotos(files) { 79 const processed = []; 80 for (const file of files) { 81 const dataUrl = await readFileAsDataURL(file); 82 const resized = await resizeImage(dataUrl, { 83 width: 2000, 84 height: 2000, 85 maxSize: 900000 86 }); 87 processed.push({ 88 dataUrl: resized.dataUrl, 89 width: resized.width, 90 height: resized.height 91 }); 92 } 93 return processed; 94}