feat: i18n translation and icon for convert to webp

This commit is contained in:
wz
2026-03-19 10:59:39 +08:00
parent 750e3ff600
commit 81e0942fa9
4 changed files with 173 additions and 1 deletions
+5
View File
@@ -101,5 +101,10 @@
"description": "Rotate an image by a specified angle.",
"shortDescription": "Rotate an image easily.",
"title": "Rotate Image"
},
"convertToWebp": {
"description": "Convert various image formats (PNG, JPG, JPEG) to WebP format.",
"shortDescription": "Convert Images to WebP Format with maximum file size.",
"title": "Convert Image to WebP"
}
}
@@ -0,0 +1,148 @@
import { Box, Slider, Typography } from '@mui/material';
import ToolImageInput from 'components/input/ToolImageInput';
import ColorSelector from 'components/options/ColorSelector';
import ToolFileResult from 'components/result/ToolFileResult';
import Color from 'color';
import React, { useState } from 'react';
import * as Yup from 'yup';
import ToolContent from '@components/ToolContent';
import { ToolComponentProps } from '@tools/defineTool';
const initialValues = {
quality: 85,
backgroundColor: '#ffffff'
};
const validationSchema = Yup.object({
quality: Yup.number().min(1).max(100).required('Quality is required'),
backgroundColor: Yup.string().required('Background color is required')
});
export default function ConvertToJpg({ title }: ToolComponentProps) {
const [input, setInput] = useState<File | null>(null);
const [result, setResult] = useState<File | null>(null);
const compute = async (
optionsValues: typeof initialValues,
input: any
): Promise<void> => {
if (!input) return;
const processImage = async (
file: File,
quality: number,
backgroundColor: string
) => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
if (ctx == null) return;
const img = new Image();
img.src = URL.createObjectURL(file);
try {
await img.decode();
canvas.width = img.width;
canvas.height = img.height;
// Fill background with selected color (important for transparency)
let bgColor: [number, number, number];
try {
//@ts-ignore
bgColor = Color(backgroundColor).rgb().array();
} catch (err) {
bgColor = [255, 255, 255]; // Default to white
}
ctx.fillStyle = `rgb(${bgColor[0]}, ${bgColor[1]}, ${bgColor[2]})`;
ctx.fillRect(0, 0, canvas.width, canvas.height);
// Draw the image on top
ctx.drawImage(img, 0, 0);
// Convert to JPG with specified quality
canvas.toBlob(
(blob) => {
if (blob) {
const fileName = file.name.replace(/\.[^/.]+$/, '') + '.jpg';
const newFile = new File([blob], fileName, {
type: 'image/jpeg'
});
setResult(newFile);
}
},
'image/jpeg',
quality / 100
);
} catch (error) {
console.error('Error processing image:', error);
} finally {
URL.revokeObjectURL(img.src);
}
};
processImage(input, optionsValues.quality, optionsValues.backgroundColor);
};
return (
<ToolContent
title={title}
input={input}
inputComponent={
<ToolImageInput
value={input}
onChange={setInput}
accept={['image/jpg']}
title={'Input Image'}
/>
}
resultComponent={
<ToolFileResult title={'Output JPG'} value={result} extension={'jpg'} />
}
initialValues={initialValues}
validationSchema={validationSchema}
getGroups={({ values, updateField }) => [
{
title: 'JPG Quality Settings',
component: (
<Box>
<Box mb={3}>
<Typography variant="body2" color="text.secondary" gutterBottom>
JPG Quality: {values.quality}%
</Typography>
<Slider
value={values.quality}
onChange={(_, value) =>
updateField(
'quality',
Array.isArray(value) ? value[0] : value
)
}
min={1}
max={100}
step={1}
valueLabelDisplay="auto"
valueLabelFormat={(value) => `${value}%`}
sx={{ mt: 1 }}
/>
<Typography variant="caption" color="text.secondary">
Higher values = better quality, larger file size
</Typography>
</Box>
<ColorSelector
value={values.backgroundColor}
onColorChange={(val) => updateField('backgroundColor', val)}
description={'Background color (for transparent images)'}
inputProps={{ 'data-testid': 'background-color-input' }}
/>
</Box>
)
}
]}
compute={compute}
setInput={setInput}
/>
);
}
@@ -0,0 +1,16 @@
import { defineTool } from '@tools/defineTool';
import { lazy } from 'react';
export const tool = defineTool('image-generic', {
i18n: {
name: 'image:convertToWebp.title',
description: 'image:convertToWebp.description',
shortDescription: 'image:convertToWebp.shortDescription'
},
path: 'comvert-to-webp',
icon: 'iconoir:media-image-list',
keywords: ['convert', 'jpg', 'jpeg', 'png', 'webp', 'compression'],
component: lazy(() => import('./index'))
});
+4 -1
View File
@@ -10,6 +10,8 @@ import { tool as qrCodeGenerator } from './qr-code/meta';
import { tool as rotateImage } from './rotate/meta';
import { tool as convertToJpg } from './convert-to-jpg/meta';
import { tool as imageEditor } from './editor/meta';
import { tool as convertToWebp } from './convert-to-webp/meta';
export const imageGenericTools = [
imageEditor,
resizeImage,
@@ -22,5 +24,6 @@ export const imageGenericTools = [
imageToText,
qrCodeGenerator,
rotateImage,
convertToJpg
convertToJpg,
convertToWebp
];