feat(home-banner): 轮播支持 PC/移动两端分图与热区跳转,热区图悬浮放大;修复上传压缩压白透明 PNG
This commit is contained in:
@@ -1,3 +1,5 @@
|
||||
import { probeImageTransparency } from './imageTransparency'
|
||||
|
||||
const IMAGE_UPLOAD_MAX_SIDE: Record<string, number> = {
|
||||
avatar: 512,
|
||||
chat: 1280,
|
||||
@@ -26,15 +28,41 @@ const IMAGE_UPLOAD_QUALITY: Record<string, number> = {
|
||||
qrcode: 0.95,
|
||||
}
|
||||
|
||||
export interface UploadEncoding {
|
||||
maxSide: number
|
||||
quality: number
|
||||
outputType: 'image/webp' | 'image/png'
|
||||
extension: 'webp' | 'png'
|
||||
}
|
||||
|
||||
/**
|
||||
* 决定上传前的压缩参数。
|
||||
*
|
||||
* 关键点:带透明通道的图(例如热区透明按钮图、透明 Logo)必须输出 PNG,
|
||||
* 否则压缩过程会把透明区域压成实色(历史上就是先铺白底再编码,导致透明按钮图变白底方块)。
|
||||
*/
|
||||
export function resolveUploadEncoding(scene: string, preserveAlpha: boolean): UploadEncoding {
|
||||
const maxSide = IMAGE_UPLOAD_MAX_SIDE[scene] ?? 1600
|
||||
const quality = IMAGE_UPLOAD_QUALITY[scene] ?? 0.82
|
||||
return {
|
||||
maxSide,
|
||||
quality,
|
||||
outputType: preserveAlpha ? 'image/png' : 'image/webp',
|
||||
extension: preserveAlpha ? 'png' : 'webp',
|
||||
}
|
||||
}
|
||||
|
||||
export async function optimizeImageForUpload(file: File, scene: string) {
|
||||
if (!file.type.startsWith('image/')) return file
|
||||
if (!['image/jpeg', 'image/png', 'image/webp'].includes(file.type)) return file
|
||||
if (typeof document === 'undefined') return file
|
||||
|
||||
const maxSide = IMAGE_UPLOAD_MAX_SIDE[scene] ?? 1600
|
||||
const quality = IMAGE_UPLOAD_QUALITY[scene] ?? 0.82
|
||||
|
||||
try {
|
||||
// 先判断原图是否真的有透明像素:有则全程保留 alpha
|
||||
const probe = await probeImageTransparency(file)
|
||||
const preserveAlpha = Boolean(probe?.hasTransparentPixels)
|
||||
const { maxSide, quality, outputType, extension } = resolveUploadEncoding(scene, preserveAlpha)
|
||||
|
||||
const image = await loadImage(file)
|
||||
const { width, height } = fitSize(image.naturalWidth, image.naturalHeight, maxSide)
|
||||
const canvas = document.createElement('canvas')
|
||||
@@ -42,13 +70,13 @@ export async function optimizeImageForUpload(file: File, scene: string) {
|
||||
canvas.height = height
|
||||
const context = canvas.getContext('2d')
|
||||
if (!context) return file
|
||||
context.fillStyle = '#ffffff'
|
||||
context.fillRect(0, 0, width, height)
|
||||
// 不铺白底:canvas 本身保留透明,输出 PNG / WebP 都能带 alpha
|
||||
context.clearRect(0, 0, width, height)
|
||||
context.drawImage(image, 0, 0, width, height)
|
||||
const blob = await canvasToBlob(canvas, 'image/webp', quality)
|
||||
const blob = await canvasToBlob(canvas, outputType, quality)
|
||||
if (!blob || blob.size >= file.size) return file
|
||||
return new File([blob], replaceFileExt(file.name, 'webp'), {
|
||||
type: 'image/webp',
|
||||
return new File([blob], replaceFileExt(file.name, extension), {
|
||||
type: outputType,
|
||||
lastModified: Date.now(),
|
||||
})
|
||||
} catch {
|
||||
|
||||
Reference in New Issue
Block a user