Advertisement
Guest User

Untitled

a guest
Aug 11th, 2024
2,526
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
TypeScript 7.67 KB | Source Code | 0 0
  1. import React, { useState, useRef, useEffect } from 'react';
  2.  
  3. const ImageSaturationComponent = () => {
  4.   const [image, setImage] = useState(null);
  5.   const [saturatedImage, setSaturatedImage] = useState(null);
  6.   const [saturation, setSaturation] = useState(100); // Default 100% saturation
  7.   const [imageUrl, setImageUrl] = useState('');
  8.   const [sliderPosition, setSliderPosition] = useState(50);
  9.   const canvasRef = useRef(null);
  10.   const pasteRef = useRef(null);
  11.   const containerRef = useRef(null);
  12.  
  13.   const handleImageUpload = (event) => {
  14.     const file = event.target.files[0];
  15.     if (file) {
  16.       const reader = new FileReader();
  17.       reader.onload = (e) => setImage(e.target.result);
  18.       reader.readAsDataURL(file);
  19.     }
  20.   };
  21.  
  22.   const handleUrlSubmit = (event) => {
  23.     event.preventDefault();
  24.     if (imageUrl) {
  25.       setImage(imageUrl);
  26.     }
  27.   };
  28.  
  29.   const handlePaste = (event) => {
  30.     const items = event.clipboardData.items;
  31.     for (let i = 0; i < items.length; i++) {
  32.       if (items[i].type.indexOf('image') !== -1) {
  33.         const blob = items[i].getAsFile();
  34.         const reader = new FileReader();
  35.         reader.onload = (e) => setImage(e.target.result);
  36.         reader.readAsDataURL(blob);
  37.         break;
  38.       }
  39.     }
  40.   };
  41.  
  42.   const adjustSaturation = (imgData, sat) => {
  43.     const data = imgData.data;
  44.     for (let i = 0; i < data.length; i += 4) {
  45.       const r = data[i];
  46.       const g = data[i + 1];
  47.       const b = data[i + 2];
  48.      
  49.       const max = Math.max(r, g, b);
  50.       const min = Math.min(r, g, b);
  51.       const l = (max + min) / 2;
  52.      
  53.       if (max !== min) {
  54.         const d = max - min;
  55.         const s = l > 0.5 ? d / (2 - max - min) : d / (max + min);
  56.         let newS;
  57.  
  58.         if (sat <= 100) {
  59.           newS = s * (sat / 100);
  60.         } else {
  61.           // Gradually approach maximum saturation
  62.           const t = (sat - 100) / 100; // 0 to 1
  63.           newS = s + (1 - s) * t;
  64.         }
  65.        
  66.         const factor = newS / s;
  67.        
  68.         data[i] = Math.round(Math.min(255, Math.max(0, l + (r - l) * factor)));
  69.         data[i + 1] = Math.round(Math.min(255, Math.max(0, l + (g - l) * factor)));
  70.         data[i + 2] = Math.round(Math.min(255, Math.max(0, l + (b - l) * factor)));
  71.       }
  72.     }
  73.     return imgData;
  74.   };
  75.  
  76.   useEffect(() => {
  77.     if (image) {
  78.       const img = new Image();
  79.       img.crossOrigin = "Anonymous";  // Allow loading cross-origin images
  80.       img.onload = () => {
  81.         const canvas = canvasRef.current;
  82.         const ctx = canvas.getContext('2d');
  83.        
  84.         canvas.width = img.width;
  85.         canvas.height = img.height;
  86.        
  87.         ctx.drawImage(img, 0, 0);
  88.        
  89.         const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
  90.         const processedData = adjustSaturation(imageData, saturation);
  91.        
  92.         ctx.putImageData(processedData, 0, 0);
  93.         setSaturatedImage(canvas.toDataURL());
  94.       };
  95.       img.onerror = () => {
  96.         alert("Failed to load image. Please check the URL or try another image.");
  97.       };
  98.       img.src = image;
  99.     }
  100.   }, [image, saturation]);
  101.  
  102.   useEffect(() => {
  103.     const pasteArea = pasteRef.current;
  104.     pasteArea.addEventListener('paste', handlePaste);
  105.     return () => {
  106.       pasteArea.removeEventListener('paste', handlePaste);
  107.     };
  108.   }, []);
  109.  
  110.   const handleSliderChange = (e) => {
  111.     setSliderPosition(Number(e.target.value));
  112.   };
  113.  
  114.   const handleMouseMove = (e) => {
  115.     if (e.buttons === 1) {
  116.       const container = containerRef.current;
  117.       const rect = container.getBoundingClientRect();
  118.       const x = e.clientX - rect.left;
  119.       const newPosition = (x / rect.width) * 100;
  120.       setSliderPosition(Math.max(0, Math.min(100, newPosition)));
  121.     }
  122.   };
  123.  
  124.   return (
  125.     <div className="flex flex-col items-center gap-4 p-4">
  126.       <h1 className="text-2xl font-bold">Image Saturation Adjustment Tool</h1>
  127.       <div className="flex flex-wrap gap-4 justify-center">
  128.         <div>
  129.           <h2 className="text-lg font-semibold">Upload local image:</h2>
  130.           <input
  131.             type="file"
  132.             accept="image/*"
  133.             onChange={handleImageUpload}
  134.             className="border border-gray-300 p-2 rounded"
  135.           />
  136.         </div>
  137.         <div>
  138.           <h2 className="text-lg font-semibold">Or enter image URL:</h2>
  139.           <form onSubmit={handleUrlSubmit} className="flex gap-2">
  140.             <input
  141.               type="url"
  142.               value={imageUrl}
  143.               onChange={(e) => setImageUrl(e.target.value)}
  144.               placeholder="https://example.com/image.jpg"
  145.               className="border border-gray-300 p-2 rounded flex-grow"
  146.             />
  147.             <button type="submit" className="bg-blue-500 text-white p-2 rounded">Load</button>
  148.           </form>
  149.         </div>
  150.         <div>
  151.           <h2 className="text-lg font-semibold">Or paste image:</h2>
  152.           <div
  153.             ref={pasteRef}
  154.             className="border-2 border-dashed border-gray-300 p-4 rounded cursor-pointer text-center"
  155.             style={{ width: '200px', height: '100px' }}
  156.           >
  157.             Click here and paste image (Ctrl+V)
  158.           </div>
  159.         </div>
  160.       </div>
  161.       {image && saturatedImage && (
  162.         <>
  163.           <div
  164.             ref={containerRef}
  165.             className="relative w-full max-w-2xl overflow-hidden"
  166.             onMouseMove={handleMouseMove}
  167.             onMouseDown={handleMouseMove}
  168.           >
  169.             <img
  170.               src={saturatedImage}
  171.               alt="Saturated"
  172.               className="w-full h-auto"
  173.             />
  174.             <div
  175.               className="absolute top-0 left-0 bottom-0 right-0 overflow-hidden"
  176.               style={{ clipPath: `inset(0 ${100 - sliderPosition}% 0 0)` }}
  177.             >
  178.               <img
  179.                 src={image}
  180.                 alt="Original"
  181.                 className="absolute top-0 left-0 w-full h-full object-cover"
  182.               />
  183.             </div>
  184.             <div
  185.               className="absolute top-0 bottom-0 w-0.5 bg-white cursor-col-resize"
  186.               style={{ left: `calc(${sliderPosition}% - 1px)` }}
  187.             >
  188.               <div className="absolute top-1/2 left-1/2 w-6 h-6 -mt-3 -ml-3 bg-white rounded-full shadow-md flex items-center justify-center">
  189.                 <svg width="20" height="20" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
  190.                   <path d="M18 8L22 12L18 16M6 8L2 12L6 16" stroke="black" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"/>
  191.                 </svg>
  192.               </div>
  193.             </div>
  194.           </div>
  195.           <input
  196.             type="range"
  197.             min="0"
  198.             max="100"
  199.             value={sliderPosition}
  200.             onChange={handleSliderChange}
  201.             className="w-full max-w-2xl mt-2"
  202.           />
  203.           <div className="w-full max-w-xs">
  204.             <label htmlFor="saturation" className="block text-sm font-medium text-gray-700">
  205.               Saturation: {saturation}% {saturation > 100 ? "(approaching maximum saturation)" : ""}
  206.             </label>
  207.             <input
  208.               type="range"
  209.               id="saturation"
  210.               name="saturation"
  211.               min="0"
  212.               max="200"
  213.               value={saturation}
  214.               onChange={(e) => setSaturation(Number(e.target.value))}
  215.               className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer"
  216.             />
  217.           </div>
  218.         </>
  219.       )}
  220.       <canvas ref={canvasRef} style={{ display: 'none' }} />
  221.     </div>
  222.   );
  223. };
  224.  
  225. export default ImageSaturationComponent;
  226.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement