feat: gif resize

This commit is contained in:
Ibrahima G. Coulibaly
2025-04-02 04:25:02 +00:00
parent 676359ed50
commit 8f42d2dc27
6 changed files with 89 additions and 22 deletions

34
.idea/workspace.xml generated
View File

@@ -4,11 +4,13 @@
<option name="autoReloadType" value="SELECTIVE" />
</component>
<component name="ChangeListManager">
<list default="true" id="b30e2810-c4c1-4aad-b134-794e52cc1c7d" name="Changes" comment="feat: image resize init">
<change afterPath="$PROJECT_DIR$/src/pages/tools/image/generic/resize/service.ts" afterDir="false" />
<change afterPath="$PROJECT_DIR$/src/pages/tools/image/generic/resize/types.ts" afterDir="false" />
<list default="true" id="b30e2810-c4c1-4aad-b134-794e52cc1c7d" name="Changes" comment="feat: svg resize">
<change beforePath="$PROJECT_DIR$/.idea/workspace.xml" beforeDir="false" afterPath="$PROJECT_DIR$/.idea/workspace.xml" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/tools/image/generic/resize/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/image/generic/resize/index.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/ToolHeader.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/ToolHeader.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/components/ToolLayout.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/components/ToolLayout.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/tools-by-category/index.tsx" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools-by-category/index.tsx" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/pages/tools/image/generic/resize/service.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/pages/tools/image/generic/resize/service.ts" afterDir="false" />
<change beforePath="$PROJECT_DIR$/src/tools/index.ts" beforeDir="false" afterPath="$PROJECT_DIR$/src/tools/index.ts" afterDir="false" />
</list>
<option name="SHOW_DIALOG" value="false" />
<option name="HIGHLIGHT_CONFLICTS" value="true" />
@@ -399,15 +401,7 @@
<workItem from="1743047367993" duration="986000" />
<workItem from="1743103182313" duration="4264000" />
<workItem from="1743348610793" duration="21855000" />
<workItem from="1743556259185" duration="5197000" />
</task>
<task id="LOCAL-00129" summary="feat: json pretty">
<option name="closed" value="true" />
<created>1740661424202</created>
<option name="number" value="00129" />
<option name="presentableId" value="LOCAL-00129" />
<option name="project" value="LOCAL" />
<updated>1740661424202</updated>
<workItem from="1743556259185" duration="6368000" />
</task>
<task id="LOCAL-00130" summary="feat: json pretty">
<option name="closed" value="true" />
@@ -793,7 +787,15 @@
<option name="project" value="LOCAL" />
<updated>1743565606951</updated>
</task>
<option name="localTasksCounter" value="178" />
<task id="LOCAL-00178" summary="feat: svg resize">
<option name="closed" value="true" />
<created>1743566704552</created>
<option name="number" value="00178" />
<option name="presentableId" value="LOCAL-00178" />
<option name="project" value="LOCAL" />
<updated>1743566704552</updated>
</task>
<option name="localTasksCounter" value="179" />
<servers />
</component>
<component name="TypeScriptGeneratedFilesManager">
@@ -840,7 +842,6 @@
<option name="CHECK_CODE_SMELLS_BEFORE_PROJECT_COMMIT" value="false" />
<option name="CHECK_NEW_TODO" value="false" />
<option name="ADD_EXTERNAL_FILES_SILENTLY" value="true" />
<MESSAGE value="feat: change pgn opacity" />
<MESSAGE value="feat: crop png" />
<MESSAGE value="chore: remove unnecessary files" />
<MESSAGE value="refactor: validateJson" />
@@ -865,7 +866,8 @@
<MESSAGE value="chore: compress video icon" />
<MESSAGE value="chore: tool description" />
<MESSAGE value="feat: image resize init" />
<option name="LAST_COMMIT_MESSAGE" value="feat: image resize init" />
<MESSAGE value="feat: svg resize" />
<option name="LAST_COMMIT_MESSAGE" value="feat: svg resize" />
</component>
<component name="XSLT-Support.FileAssociations.UIState">
<expand />

View File

@@ -5,6 +5,7 @@ import { capitalizeFirstLetter } from '../utils/string';
import Grid from '@mui/material/Grid';
import { Icon, IconifyIcon } from '@iconify/react';
import { categoriesColors } from '../config/uiConfig';
import { getToolsByCategory } from '@tools/index';
const StyledButton = styled(Button)(({ theme }) => ({
backgroundColor: 'white',
@@ -70,7 +71,9 @@ export default function ToolHeader({
items={[
{ title: 'All tools', link: '/' },
{
title: capitalizeFirstLetter(type),
title: getToolsByCategory().find(
(category) => category.type === type
)!.rawTitle,
link: '/categories/' + type
},
{ title }

View File

@@ -53,7 +53,10 @@ export default function ToolLayout({
{children}
<Separator backgroundColor="#5581b5" margin="50px" />
<AllTools
title={`All ${capitalizeFirstLetter(type)} tools`}
title={`All ${capitalizeFirstLetter(
getToolsByCategory().find((category) => category.type === type)!
.rawTitle
)} tools`}
toolCards={otherCategoryTools}
/>
</Box>

View File

@@ -43,10 +43,11 @@ export default function Home() {
<IconButton onClick={() => navigate('/')}>
<ArrowBackIcon color={'primary'} />
</IconButton>
<Typography
fontSize={22}
color={theme.palette.primary.main}
>{`All ${capitalizeFirstLetter(categoryName)} Tools`}</Typography>
<Typography fontSize={22} color={theme.palette.primary.main}>{`All ${
getToolsByCategory().find(
(category) => category.type === categoryName
)!.rawTitle
} Tools`}</Typography>
</Stack>
<Grid container spacing={2} mt={2}>
{getToolsByCategory()

View File

@@ -1,4 +1,6 @@
import { InitialValuesType } from './types';
import { FFmpeg } from '@ffmpeg/ffmpeg';
import { fetchFile, toBlobURL } from '@ffmpeg/util';
export const processImage = async (
file: File,
@@ -97,6 +99,60 @@ export const processImage = async (
console.error('Error processing SVG:', error);
// Fall back to canvas method if SVG processing fails
}
} else if (file.type === 'image/gif') {
try {
const ffmpeg = new FFmpeg();
await ffmpeg.load({
wasmURL:
'https://cdn.jsdelivr.net/npm/@ffmpeg/core@0.12.9/dist/esm/ffmpeg-core.wasm'
});
// Write the input file to memory
await ffmpeg.writeFile('input.gif', await fetchFile(file));
// Calculate new dimensions
let newWidth = 0;
let newHeight = 0;
let scaleFilter = '';
if (resizeMethod === 'pixels') {
if (dimensionType === 'width') {
newWidth = parseInt(width);
if (maintainAspectRatio) {
scaleFilter = `scale=${newWidth}:-1`;
} else {
newHeight = parseInt(height);
scaleFilter = `scale=${newWidth}:${newHeight}`;
}
} else {
// height
newHeight = parseInt(height);
if (maintainAspectRatio) {
scaleFilter = `scale=-1:${newHeight}`;
} else {
newWidth = parseInt(width);
scaleFilter = `scale=${newWidth}:${newHeight}`;
}
}
} else {
// percentage
const scale = parseInt(percentage) / 100;
scaleFilter = `scale=iw*${scale}:ih*${scale}`;
}
// Run FFmpeg command
await ffmpeg.exec(['-i', 'input.gif', '-vf', scaleFilter, 'output.gif']);
// Read the output file
const data = await ffmpeg.readFile('output.gif');
// Create a new File object
return new File([data], file.name, { type: 'image/gif' });
} catch (error) {
console.error('Error processing GIF with FFmpeg:', error);
// Fall back to canvas method if FFmpeg processing fails
}
}
// Create canvas
const canvas = document.createElement('canvas');

View File

@@ -130,6 +130,7 @@ export const filterTools = (
export const getToolsByCategory = (): {
title: string;
rawTitle: string;
description: string;
icon: IconifyIcon | string;
type: string;
@@ -144,6 +145,7 @@ export const getToolsByCategory = (): {
(config) => config.type === type
);
return {
rawTitle: categoryConfig?.title ?? capitalizeFirstLetter(type),
title: `${categoryConfig?.title ?? capitalizeFirstLetter(type)} Tools`,
description: categoryConfig?.value ?? '',
type,