shrink imagies

This commit is contained in:
john 2025-05-26 22:05:52 +02:00
parent 8457604da7
commit a6022d31c6
9 changed files with 131 additions and 39 deletions

View file

@ -2,6 +2,7 @@ import { useState } from 'react'
import FancyTextEditor, { TextInputKeyDownEvent } from './inputs/FancyTextEditor.tsx'
import Button from './buttons/Button.tsx'
import { openFileDialog } from '../utils/openFileDialog.ts'
import makePica from 'pica'
interface NewPostWidgetProps {
onSubmit: (
@ -121,11 +122,13 @@ async function createAttachment(file: File): Promise<Attachment> {
throw new Error('not an image')
}
file = await optimizeImageSize(file)
const objectUrl = URL.createObjectURL(file)
const { width, height } = await getImageFileDimensions(objectUrl)
return {
id: crypto.randomUUID(),
id: getRandomId(),
file,
objectUrl,
width,
@ -144,3 +147,82 @@ function getImageFileDimensions(objectURL: string): Promise<{ width: number; hei
img.src = objectURL
})
}
const pica = makePica()
async function optimizeImageSize(
file: File,
{
targetMaxWidth = 1920,
targetMaxHeight = 1080,
targetSizeBytes = 500 * 1024,
outputType = 'image/jpeg',
quality = 0.9,
}: {
targetMaxWidth?: number
targetMaxHeight?: number
targetSizeBytes?: number
outputType?: string
quality?: number
} = {},
): Promise<File> {
const img = document.createElement('img')
const url = URL.createObjectURL(file)
img.src = url
await img.decode()
console.debug('processing image', {
width: img.width,
height: img.height,
targetMaxWidth,
targetMaxHeight,
targetSizeBytes,
outputType,
quality,
})
const scale = Math.min(1, targetMaxWidth / img.width, targetMaxHeight / img.height)
const width = Math.floor(img.width * scale)
const height = Math.floor(img.height * scale)
const srcCanvas = document.createElement('canvas')
srcCanvas.width = img.width
srcCanvas.height = img.height
srcCanvas.getContext('2d')!.drawImage(img, 0, 0)
const dstCanvas = document.createElement('canvas')
dstCanvas.width = width
dstCanvas.height = height
await pica.resize(srcCanvas, dstCanvas)
let blob = await pica.toBlob(dstCanvas, outputType, quality)
while (blob.size > targetSizeBytes && quality > 0.1) {
quality = parseFloat((quality - 0.1).toFixed(2))
blob = await pica.toBlob(dstCanvas, outputType, quality)
}
URL.revokeObjectURL(url)
return new File([blob], file.name, { type: file.type })
}
function getRandomId() {
if (window.isSecureContext) {
return crypto.randomUUID()
}
// Fallback using getRandomValues
const bytes = new Uint8Array(16)
crypto.getRandomValues(bytes)
// Format according to RFC4122 version 4
bytes[6] = (bytes[6]! & 0x0f) | 0x40
bytes[8] = (bytes[8]! & 0x3f) | 0x80
const hex = [...bytes].map((b) => b.toString(16).padStart(2, '0'))
return `${hex.slice(0, 4).join('')}-${hex.slice(4, 6).join('')}-${hex.slice(6, 8).join('')}-${hex.slice(8, 10).join('')}-${hex.slice(10, 16).join('')}`
}