Skip to content

Commit

Permalink
it works! ( and sucks )
Browse files Browse the repository at this point in the history
  • Loading branch information
beggers committed Nov 21, 2024
1 parent a358f1e commit 2b2eca5
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 5 deletions.
12 changes: 8 additions & 4 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@ import EffectsSidebar from './components/EffectsSidebar';
import { FabricImage } from 'fabric';

function App() {
// Base user-uploaded image.
const [image, setImage] = useState<Blob | null>(null);
const [isCropping, setIsCropping] = useState<boolean>(false);
const [fileName, setFileName] = useState<string>('');

// Yeah yeah we should probably have a sub-component to hold this state.
// Yeah yeah we should probably have a sub-component to hold the
// state after this point.
const [overlayImages, setOverlayImages] = useState<FabricImage[]>([]);
// This is an abomination unto god. Holds the complete image (with overlays) for download.
const [downloadableImage, setDownloadableImage] = useState<Blob | null>(null);

return (
<div className="App">
Expand All @@ -27,14 +31,14 @@ function App() {
{!!image && isCropping && (
<UploadModal
image={image}
setImage={(blob) => { setImage(blob); setIsCropping(false); }}
setImage={(blob) => { setImage(blob); setDownloadableImage(blob); setIsCropping(false); }}
onClose={() => setIsCropping(false)}
/>
)}
{!!image && !isCropping && (
<ImageCanvas image={image} overlayImages={overlayImages} />
<ImageCanvas image={image} overlayImages={overlayImages} setDownloadableImage={setDownloadableImage} />
)}
<DownloadButton image={image} fileName={fileName} />
<DownloadButton image={downloadableImage} fileName={fileName} />
</div>
</div>
<EffectsSidebar overlayImages={overlayImages} setOverlayImages={setOverlayImages} />
Expand Down
1 change: 1 addition & 0 deletions src/components/DownloadButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ function DownloadButton({ image, fileName }: DownloadButtonProps) {
const handleDownload = () => {
if (!image) return;

// I am ashamed to have written this. There must be a better way.
const link = document.createElement('a');
const imageUrl = URL.createObjectURL(image);
link.href = imageUrl;
Expand Down
35 changes: 34 additions & 1 deletion src/components/ImageCanvas.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,35 @@ import { useEffect, useRef } from 'react';
interface ImageCanvasProps {
image: Blob | null;
overlayImages: FabricImage[];
setDownloadableImage: (image: Blob | null) => void;
}

function ImageCanvas({ image, overlayImages }: ImageCanvasProps) {
function ImageCanvas({ image, overlayImages, setDownloadableImage }: ImageCanvasProps) {
// TODO at least one of these, probably both, should be in the parent
// component so clicking the download button can trigger the final image render.
const canvasRef = useRef<HTMLCanvasElement | null>(null);
const fabricCanvasRef = useRef<Canvas | null>(null);

function updateDownloadableImage() {
if (!fabricCanvasRef.current) return;

// TODO other image types
const downloadableImageUrl = fabricCanvasRef.current.toDataURL({
format: 'png',
multiplier: 1
});
const base64Data = downloadableImageUrl.replace(/^data:image\/png;base64,/, '');

const byteCharacters = atob(base64Data);
const byteNumbers = new Array(byteCharacters.length);
for (let i = 0; i < byteCharacters.length; i++) {
byteNumbers[i] = byteCharacters.charCodeAt(i);
}
const byteArray = new Uint8Array(byteNumbers);
const blob = new Blob([byteArray], { type: 'image/png' });
setDownloadableImage(blob);
}

useEffect(() => {
if (!fabricCanvasRef.current && canvasRef.current) {
const canvasElement = canvasRef.current;
Expand All @@ -22,6 +45,15 @@ function ImageCanvas({ image, overlayImages }: ImageCanvasProps) {

const newCanvas = new Canvas(canvasElement);
fabricCanvasRef.current = newCanvas;
// https://github.com/fabricjs/fabric.js/wiki/Working-with-events
// TODO We should be able to do this in a for-loop, but then TS
// complains about using a string as type CanvasEvent.
fabricCanvasRef.current.on('object:added', updateDownloadableImage);
fabricCanvasRef.current.on('object:modified', updateDownloadableImage);
fabricCanvasRef.current.on('object:removed', updateDownloadableImage);
fabricCanvasRef.current.on('object:rotating', updateDownloadableImage);
fabricCanvasRef.current.on('object:scaling', updateDownloadableImage);
fabricCanvasRef.current.on('object:moving', updateDownloadableImage);
}
}

Expand All @@ -48,6 +80,7 @@ function ImageCanvas({ image, overlayImages }: ImageCanvasProps) {
}
};
reader.readAsDataURL(image);

}
}, [image, overlayImages]);

Expand Down

0 comments on commit 2b2eca5

Please sign in to comment.