From 200fb093b10af0dc02c185b29b686d9c780608ee Mon Sep 17 00:00:00 2001 From: Shirasawa <764798966@qq.com> Date: Sat, 7 Mar 2026 05:48:44 +0800 Subject: [PATCH] fix: Use toBlob on first mobile export to avoid black canvas image on Android (#22317) --- src/lib/utils/index.ts | 53 +++++++++++++++++++++++++++++++++--------- 1 file changed, 42 insertions(+), 11 deletions(-) diff --git a/src/lib/utils/index.ts b/src/lib/utils/index.ts index 18c5c6b67..3b8a6a3d7 100644 --- a/src/lib/utils/index.ts +++ b/src/lib/utils/index.ts @@ -275,11 +275,50 @@ export const canvasPixelTest = () => { return true; }; + +let resizeImageWarmupDone = false; +/** + * Draws an image to a canvas at the given dimensions and returns a data URL. + * On mobile, the first export uses toBlob (avoids black image on Android); later exports use toDataURL. + */ +async function resizeImageToDataURL( + img: HTMLImageElement, + width: number, + height: number, + mimeType = 'image/jpeg', +): Promise { + const canvas = document.createElement('canvas'); + canvas.width = width; + canvas.height = height; + canvas.getContext('2d')?.drawImage(img, 0, 0, width, height); + + const toDataURL = () => canvas.toDataURL(mimeType); + + if (!resizeImageWarmupDone && canvas.toBlob && /android|iphone|ipad|ipod/i.test(navigator?.userAgent)) { + resizeImageWarmupDone = true; + return new Promise((resolve) => { + canvas.toBlob( + (blob) => { + if (!blob) { + resolve(toDataURL()); + return; + } + const reader = new FileReader(); + reader.onload = () => resolve(String(reader.result)); + reader.onerror = () => resolve(toDataURL()); + reader.readAsDataURL(blob); + }, + mimeType, + ); + }); + } + return Promise.resolve(toDataURL()); +} + export const compressImage = async (imageUrl, maxWidth, maxHeight) => { return new Promise((resolve, reject) => { const img = new Image(); img.onload = () => { - const canvas = document.createElement('canvas'); let width = img.width; let height = img.height; @@ -322,16 +361,8 @@ export const compressImage = async (imageUrl, maxWidth, maxHeight) => { height = maxHeight; } - canvas.width = width; - canvas.height = height; - - const context = canvas.getContext('2d'); - context.drawImage(img, 0, 0, width, height); - - // Get compressed image URL - const mimeType = imageUrl.match(/^data:([^;]+);/)?.[1]; - const compressedUrl = canvas.toDataURL(mimeType); - resolve(compressedUrl); + const mimeType = imageUrl.match(/^data:([^;]+);/)?.[1] ?? 'image/jpeg'; + resolve(await resizeImageToDataURL(img, width, height, mimeType)); }; img.onerror = (error) => reject(error); img.src = imageUrl;