Skip to content

Commit

Permalink
[CreateMeme] Add Basic functionalities (#82)
Browse files Browse the repository at this point in the history
* Add Basic Meme Tool

* Multiple Render Fix

Co-authored-by: Fahmi Bnchi <[email protected]>
  • Loading branch information
bnchi and Fahme authored Dec 17, 2022
1 parent d451ccb commit 5b56c4e
Show file tree
Hide file tree
Showing 3 changed files with 154 additions and 9 deletions.
4 changes: 4 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import workerContent from './pdfjs.worker.min.json';
import QuickSignPDF from './QuickSignPDF/QuickSignPDF';
import SplitPDF from './SplitPDF/SplitPDF';
import ToolsNav from './ToolsNav/ToolsNav';
import CreateMeme from './CreateMeme/CreateMeme';

const workerBlob = new Blob([workerContent], { type: 'text/javascript' });
const workerBlobURL = URL.createObjectURL(workerBlob);
Expand Down Expand Up @@ -47,6 +48,9 @@ export const App = (): JSX.Element => {
<Route path="/bulk-edit-photos" exact>
<BulkEditPhotos />
</Route>
<Route path="/create-meme" exact>
<CreateMeme />
</Route>
<Route path="/*">
<div className="p-4 flex flex-col justify-center items-center content-center h-full">
<div>
Expand Down
142 changes: 142 additions & 0 deletions src/CreateMeme/CreateMeme.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import { useCallback, useState, useEffect } from 'react';
import UploadButton from '../components/UploadButton';
import { fabric } from 'fabric'; // this also installed on your project
import { useFabricJSEditor, FabricJSCanvas } from 'fabricjs-react';

const MEME_TEXT_SHADOW = new fabric.Shadow({
color: 'black',
blur: 15,
});

let wasImageLoadedToCanvas = false;

const CreateMeme = (): JSX.Element => {
const { editor, onReady } = useFabricJSEditor();
const [imagePath, setImagePath] = useState<string | null>(null);
const [fileName, setFileName] = useState<string>(
`new-meme-${new Date().getTime()}.png`
);

const onDrop = useCallback(
async (files) => {
wasImageLoadedToCanvas = false;
if (files.length === 0 || files.length > 1) return;
setImagePath(URL.createObjectURL(files[0]));
},
[editor]
);

useEffect(() => {
if (!imagePath || wasImageLoadedToCanvas || !editor) return;
editor?.deleteAll();
wasImageLoadedToCanvas = true;
fabric.Image.fromURL(
imagePath,
function (oImg: any) {
const aspectRatio = oImg.width / oImg.height;
const width = 600;
const height = width / aspectRatio;
oImg.scaleToWidth(width);
oImg.scaleToHeight(height);
editor?.canvas.setDimensions({
width: width,
height: height,
});
editor?.canvas.add(oImg);
},
{
selectable: false,
hasControls: false,
lockMovementX: true,
lockMovementY: true,
}
);
}, [imagePath, fabric, editor]);

const onSave = useCallback(async () => {
if (!imagePath || !editor) return;
saveAs(
editor.canvas.toDataURL({ format: 'png' }),
fileName.endsWith('.png') ? fileName : `${fileName}.png`
);
}, [imagePath, editor, fileName]);

return (
<div className="h-full flex flex-col">
<div className="flex flex-grow h-full w-full xl:flex-row">
{!imagePath && (
<div className="flex flex-col flex-grow h-full w-full lg:flex-row">
<div className="px-3 py-3 flex-grow ">
<UploadButton onDrop={onDrop} accept="image/*" fullSized={true} />
</div>
</div>
)}
{imagePath && (
<div className="flex flex-col flex-grow">
{/* Toolbar */}
<div className="flex p-3">
<div className="h-10 w-48 mr-2">
<UploadButton
onDrop={onDrop}
accept="image/*"
fullSized={false}
>
<span className="text-base">Create New Meme</span>
</UploadButton>
</div>
<div>
<button
className="h-10 self-end bg-gray-500 text-white px-3 py-2 hover:bg-green-700 mr-2"
onClick={() => {
const text = new fabric.Textbox('YOUR TEXT', {
fontFamily: 'IMPACT',
textAlign: 'center',
fontWeight: 'bold',
shadow: MEME_TEXT_SHADOW,
fontSize: 50,
hasControls: true,
fill: '#ffffff',
width: 400,
});
editor?.canvas.add(text);
}}
>
Add Text
</button>
</div>
<div>
<button
className="h-10 self-end bg-gray-500 text-white px-3 py-2 hover:bg-green-700 mr-2"
onClick={() => {
editor?.deleteSelected();
}}
>
Delete Selected Text
</button>
</div>
</div>
<div className="flex justify-center">
<FabricJSCanvas onReady={onReady} />
</div>
<div className="flex w-full sticky bottom-0 bg-white p-2 shadow border-black border-opacity-20 border-solid border lg:static lg:bg-none lg:border-none lg:justify-end lg:shadow-none">
<input
onChange={(e) => setFileName(e.target.value)}
type="text"
className="flex-grow h-10 py-0 mr-2 lg:mr-5 px-2 lg:px-5 border-gray-300 placeholder-gray-200 leading-0 lg:leading-3 focus:ring-green-700 lg:max-w-sm"
placeholder="name-your-meme.png"
/>
<button
onClick={onSave}
className="h-10 self-end bg-gray-500 text-white px-3 py-2 hover:bg-green-700"
>
Download Meme
</button>
</div>
</div>
)}
</div>
</div>
);
};

export default CreateMeme;
17 changes: 8 additions & 9 deletions src/ToolsNav/ToolsNav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,14 @@ const ToolsNav = ({
>
Bulk-Edit Photos
</NavLink>
<NavLink
exact
className={className}
activeClassName={activeClassName}
to="/create-meme"
>
Create Meme
</NavLink>
{showTodo && (
<>
<NavLink
Expand Down Expand Up @@ -103,15 +111,6 @@ const ToolsNav = ({
<span className="text-xs bg-green-500 px-1 text-white">TODO</span>{' '}
Make GIF
</NavLink>
<NavLink
exact
className={className}
activeClassName={activeClassName}
to="/create-meme"
>
<span className="text-xs bg-green-500 px-1 text-white">TODO</span>{' '}
Create Meme
</NavLink>
</>
)}
</>
Expand Down

0 comments on commit 5b56c4e

Please sign in to comment.