feat: change pgn opacity

This commit is contained in:
Ibrahima G. Coulibaly 2025-03-08 08:38:35 +00:00
commit 5d04e5794a
11 changed files with 237 additions and 69 deletions

View file

@ -5,7 +5,6 @@ import React, { ReactNode, RefObject, useContext, useEffect } from 'react';
import { Formik, FormikProps, FormikValues, useFormikContext } from 'formik';
import ToolOptionGroups, { ToolOptionGroup } from './ToolOptionGroups';
import { CustomSnackBarContext } from '../../contexts/CustomSnackBarContext';
import * as Yup from 'yup';
export type UpdateField<T> = <Y extends keyof T>(field: Y, value: T[Y]) => void;
@ -26,6 +25,7 @@ const FormikListenerComponent = <T,>({
compute(values, input);
} catch (exception: unknown) {
if (exception instanceof Error) showSnackBar(exception.message, 'error');
else console.error(exception);
}
}, [values, input]);

View file

@ -0,0 +1,87 @@
import React, { useEffect, useState } from 'react';
import ToolFileInput from '@components/input/ToolFileInput';
import ToolFileResult from '@components/result/ToolFileResult';
import { changeOpacity } from './service';
import ToolContent from '@components/ToolContent';
import TextFieldWithDesc from '@components/options/TextFieldWithDesc';
import { CardExampleType } from '@components/examples/ToolExamples';
import { ToolComponentProps } from '@tools/defineTool';
import { updateNumberField } from '@utils/string';
type InitialValuesType = {
opacity: number;
};
const initialValues: InitialValuesType = {
opacity: 1
};
const exampleCards: CardExampleType<InitialValuesType>[] = [
{
title: 'Semi-transparent PNG',
description: 'Make an image 50% transparent',
sampleOptions: {
opacity: 0.5
},
sampleResult: ''
},
{
title: 'Slightly Faded PNG',
description: 'Create a subtle transparency effect',
sampleOptions: {
opacity: 0.8
},
sampleResult: ''
}
];
export default function ChangeOpacity({ title }: ToolComponentProps) {
const [input, setInput] = useState<File | null>(null);
const [result, setResult] = useState<File | null>(null);
const compute = (values: InitialValuesType, input: any) => {
if (input) {
changeOpacity(input, values.opacity).then(setResult);
}
};
return (
<ToolContent
title={title}
input={input}
inputComponent={
<ToolFileInput
value={input}
onChange={setInput}
accept={['image/png']}
title={'Input PNG'}
/>
}
resultComponent={
<ToolFileResult
title={'Changed PNG'}
value={result}
extension={'png'}
/>
}
initialValues={initialValues}
exampleCards={exampleCards}
getGroups={({ values, updateField }) => [
{
title: 'Opacity Settings',
component: (
<TextFieldWithDesc
description="Set opacity between 0 (transparent) and 1 (opaque)"
value={values.opacity}
onOwnChange={(val) =>
updateNumberField(val, 'opacity', updateField)
}
type="number"
inputProps={{ step: 0.1, min: 0, max: 1 }}
/>
)
}
]}
compute={compute}
/>
);
}

View file

@ -0,0 +1,12 @@
import { defineTool } from '@tools/defineTool';
import { lazy } from 'react';
export const tool = defineTool('png', {
name: 'Change PNG Opacity',
path: 'change-opacity',
icon: 'material-symbols:opacity',
description: 'Easily adjust the transparency of your PNG images. Simply upload your PNG file, use the slider to set the desired opacity level between 0 (fully transparent) and 1 (fully opaque), and download the modified image.',
shortDescription: 'Adjust transparency of PNG images',
keywords: ['opacity', 'transparency', 'png', 'alpha'],
component: lazy(() => import('./index'))
});

View file

@ -0,0 +1,39 @@
export async function changeOpacity(
file: File,
opacity: number
): Promise<File> {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.onload = (event) => {
const img = new Image();
img.onload = () => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
if (!ctx) {
reject(new Error('Canvas context not supported'));
return;
}
canvas.width = img.width;
canvas.height = img.height;
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.globalAlpha = opacity;
ctx.drawImage(img, 0, 0);
canvas.toBlob((blob) => {
if (blob) {
const newFile = new File([blob], file.name, { type: 'image/png' });
resolve(newFile);
} else {
reject(new Error('Failed to generate image blob'));
}
}, 'image/png');
};
img.onerror = () => reject(new Error('Failed to load image'));
img.src = <string>event.target?.result;
};
reader.onerror = () => reject(new Error('Failed to read file'));
reader.readAsDataURL(file);
});
}

View file

@ -2,10 +2,12 @@ import { tool as pngCompressPng } from './compress-png/meta';
import { tool as convertJgpToPng } from './convert-jgp-to-png/meta';
import { tool as pngCreateTransparent } from './create-transparent/meta';
import { tool as changeColorsInPng } from './change-colors-in-png/meta';
import { tool as changeOpacity } from './change-opacity/meta';
export const pngTools = [
pngCompressPng,
pngCreateTransparent,
changeColorsInPng,
convertJgpToPng
convertJgpToPng,
changeOpacity
];

View file

@ -12,7 +12,7 @@ import { tool as listSort } from './sort/meta';
export const listTools = [
listSort,
listUnwrap,
// listUnwrap,
listReverse,
listFindUnique,
listFindMostPopular,

View file

@ -5,7 +5,8 @@ export const tool = defineTool('string', {
name: 'Reverse',
path: 'reverse',
icon: '',
description: "World's simplest browser-based utility for reversing text. Input any text and get it instantly reversed, character by character. Perfect for creating mirror text, analyzing palindromes, or playing with text patterns. Preserves spaces and special characters while reversing.",
description:
"World's simplest browser-based utility for reversing text. Input any text and get it instantly reversed, character by character. Perfect for creating mirror text, analyzing palindromes, or playing with text patterns. Preserves spaces and special characters while reversing.",
shortDescription: 'Reverse any text character by character',
keywords: ['reverse'],
component: lazy(() => import('./index'))

7
src/typed/jimp.d.ts vendored
View file

@ -1,7 +0,0 @@
declare module 'jimp' {
class JimpImage {
getPixelColor: (x: number, y: number) => number;
}
export function read(buffer: Buffer): Promise<JimpImage>;
}