import React, { useState, useRef, useEffect } from 'react'; import { Upload, Scissors, Download, Image as ImageIcon, Trash2, Layers, RefreshCw, ArrowRight, ArrowDown, Grid, Share2 } from 'lucide-react'; const ImageSplitterApp = () => { const [selectedImage, setSelectedImage] = useState(null); const [imageDimensions, setImageDimensions] = useState({ width: 0, height: 0 }); // Single dimension splits const [splitCount, setSplitCount] = useState(3); // Grid specific splits const [gridRows, setGridRows] = useState(3); const [gridCols, setGridCols] = useState(3); const [scale, setScale] = useState(100); // 'horizontal' = Panorama/Carousel, 'vertical' = Profile Column, 'grid' = Profile Grid const [splitDirection, setSplitDirection] = useState('horizontal'); const [processedImages, setProcessedImages] = useState([]); const [isProcessing, setIsProcessing] = useState(false); const [fileName, setFileName] = useState("image"); const resultsRef = useRef(null); // Handle file upload const handleImageUpload = (event) => { const file = event.target.files[0]; if (file) { setFileName(file.name.split('.')[0]); const reader = new FileReader(); reader.onload = (e) => { const img = new Image(); img.onload = () => { setImageDimensions({ width: img.width, height: img.height }); setSelectedImage(img); setProcessedImages([]); // Clear previous results if (navigator.vibrate) navigator.vibrate(20); // Subtle haptic }; img.src = e.target.result; }; reader.readAsDataURL(file); } }; // Reset everything const handleReset = () => { if (navigator.vibrate) navigator.vibrate(20); setSelectedImage(null); setProcessedImages([]); setSplitCount(3); setGridRows(3); setGridCols(3); setScale(100); }; // Mobile Share Functionality const handleShare = async (dataUrl, index) => { if (navigator.vibrate) navigator.vibrate(20); try { // Convert base64 to blob for sharing const res = await fetch(dataUrl); const blob = await res.blob(); const file = new File([blob], `${fileName}_${index}.png`, { type: 'image/png' }); if (navigator.canShare && navigator.canShare({ files: [file] })) { await navigator.share({ files: [file], title: 'Split Image Part', text: `Here is part ${index + 1} of my split image.` }); } else { // Fallback if sharing not supported downloadImage(dataUrl, index); } } catch (error) { console.log("Error sharing:", error); // Fallback downloadImage(dataUrl, index); } }; // The Core Logic: Split and Scale const processImage = async () => { if (!selectedImage) return; if (navigator.vibrate) navigator.vibrate(20); setIsProcessing(true); // Allow UI to update before heavy processing setTimeout(() => { const chunks = []; const { width, height } = imageDimensions; const totalScaledWidth = width * (scale / 100); const totalScaledHeight = height * (scale / 100); // Determine splitting parameters let rows, cols; if (splitDirection === 'grid') { rows = gridRows; cols = gridCols; } else if (splitDirection === 'horizontal') { rows = 1; cols = splitCount; } else { // vertical rows = splitCount; cols = 1; } // Calculate dimensions per chunk const chunkWidthOriginal = width / cols; const chunkHeightOriginal = height / rows; const chunkWidthScaled = totalScaledWidth / cols; const chunkHeightScaled = totalScaledHeight / rows; // Use an off-screen canvas const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); // Nested loop to handle grid splitting (Row by Row, then Col by Col) for (let r = 0; r < rows; r++) { for (let c = 0; c < cols; c++) { // Set canvas to the size of a single chunk canvas.width = chunkWidthScaled; canvas.height = chunkHeightScaled; // Calculate source position const srcX = c * chunkWidthOriginal; const srcY = r * chunkHeightOriginal; // Draw the specific slice ctx.drawImage( selectedImage, srcX, srcY, // Start X, Start Y (source) chunkWidthOriginal, chunkHeightOriginal, // Width, Height (source) 0, 0, // Start X, Start Y (destination) chunkWidthScaled, chunkHeightScaled // Width, Height (destination) ); chunks.push(canvas.toDataURL('image/png')); } } setProcessedImages(chunks); setIsProcessing(false); // Auto-scroll to results on mobile setTimeout(() => { if (resultsRef.current) { resultsRef.current.scrollIntoView({ behavior: 'smooth', block: 'start' }); if (navigator.vibrate) navigator.vibrate([50, 50, 50]); // Success haptic } }, 100); }, 100); }; // Helper to get correct post number const getPostNumber = (index) => { return processedImages.length - index; }; const downloadImage = (dataUrl, index) => { if (navigator.vibrate) navigator.vibrate(20); const postNum = getPostNumber(index); const link = document.createElement('a'); link.href = dataUrl; // Use "post" in filename to make it clear link.download = `${fileName}_post_${postNum}.png`; document.body.appendChild(link); link.click(); document.body.removeChild(link); }; // Determine grid columns for result display const getResultGridClass = () => { if (splitDirection === 'grid') { if (gridCols === 2) return 'grid-cols-2'; if (gridCols === 3) return 'grid-cols-3'; if (gridCols >= 4) return 'grid-cols-4'; return 'grid-cols-1'; } // Vertical splits are usually single column if (splitDirection === 'vertical') return 'grid-cols-1 max-w-sm mx-auto'; // Horizontal splits are usually 2 cols return 'grid-cols-1 md:grid-cols-2'; }; return (
{/* Compact Header for Mobile */}

Image Splitter

Grid • Carousel • Webtoon

{/* Left Column: Controls & Upload */}
{/* Upload Area - Mobile Optimized */} {!selectedImage ? (

Tap to upload

Open Camera Roll or Files

) : (
{/* Image Info & Thumbnail Preview - Adaptive Layout */}
{/* Huge Thumbnail on Mobile */}
Thumbnail

{fileName}

{imageDimensions.width} × {imageDimensions.height}


{/* Control: Split Direction - Large Touch Targets */}
{[ { id: 'horizontal', icon: ArrowRight, label: 'Panorama' }, { id: 'vertical', icon: ArrowDown, label: 'Vertical' }, { id: 'grid', icon: Grid, label: 'Grid' } ].map(mode => ( ))}
{/* Dynamic Controls based on Direction */} {splitDirection === 'grid' ? (
{gridRows}
setGridRows(parseInt(e.target.value))} className="w-full h-8 accent-blue-600 touch-pan-x" />
{gridCols}
setGridCols(parseInt(e.target.value))} className="w-full h-8 accent-blue-600 touch-pan-x" />
) : (
{splitCount}
setSplitCount(parseInt(e.target.value))} className="w-full h-8 accent-blue-600 touch-pan-x" />
1 10
)} {/* Control: Scale */}
{scale}%
setScale(parseInt(e.target.value))} className="w-full h-8 accent-blue-600 touch-pan-x" />
{/* Action Button - Large & Sticky-ish feel */}
)} {/* Hint Box */} {selectedImage && (
Note: Splits are numbered backward (e.g., upload Post #1 first) so they align correctly on your Instagram profile.
)}
{/* Right Column: Preview & Results */}
{processedImages.length > 0 ? (

Results ({processedImages.length})

{/* Result Grid - Responsive to the chosen layout */}
{processedImages.map((src, idx) => (
{/* Checkerboard pattern for transparency */}
{`Part
{splitDirection === 'horizontal' ? `Slide ${idx + 1}/${processedImages.length}` : `Post #${getPostNumber(idx)}` }
{/* Native Share Button */} {navigator.share && ( )}
))}
) : ( // Empty State !selectedImage && (

Select an image to start splitting

) )}
); }; export default ImageSplitterApp;