WIP PWA for Grain
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

feat: add image resize utility

+94
+94
src/utils/image-resize.js
··· 1 + export 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 + 10 + function getBase64Size(base64) { 11 + const str = base64.split(',')[1] || base64; 12 + return Math.ceil((str.length * 3) / 4); 13 + } 14 + 15 + function 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 + 49 + export 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 + 78 + export 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 + }