feat: i18n translation and icon for convert to webp
This commit is contained in:
@@ -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'))
|
||||
});
|
||||
@@ -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
|
||||
];
|
||||
|
||||
Reference in New Issue
Block a user