MogensR's picture
Create examples/javascript/browser/react_components.jsx
e510a3d
raw
history blame
13.7 kB
/**
* BackgroundFX Pro - React Component Example
*
* A complete React component for background removal with drag-and-drop,
* progress tracking, and result preview.
*/
import React, { useState, useCallback, useRef } from 'react';
import axios from 'axios';
// Configuration
const API_BASE_URL = process.env.REACT_APP_BACKGROUNDFX_API_URL || 'https://api.backgroundfx.pro/v1';
const API_KEY = process.env.REACT_APP_BACKGROUNDFX_API_KEY;
/**
* BackgroundFX API Client Hook
*/
const useBackgroundFX = (apiKey) => {
const [isProcessing, setIsProcessing] = useState(false);
const [progress, setProgress] = useState(0);
const [error, setError] = useState(null);
const client = useRef(
axios.create({
baseURL: API_BASE_URL,
headers: {
'Authorization': `Bearer ${apiKey}`,
},
})
);
const removeBackground = useCallback(async (file, options = {}) => {
setIsProcessing(true);
setError(null);
setProgress(0);
const formData = new FormData();
formData.append('file', file);
Object.keys(options).forEach(key => {
formData.append(key, options[key]);
});
try {
const response = await client.current.post('/process/remove-background', formData, {
onUploadProgress: (progressEvent) => {
const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total);
setProgress(percentCompleted);
},
});
setIsProcessing(false);
setProgress(100);
return response.data;
} catch (err) {
setError(err.response?.data?.message || err.message);
setIsProcessing(false);
throw err;
}
}, []);
return {
removeBackground,
isProcessing,
progress,
error,
};
};
/**
* Drag and Drop Zone Component
*/
const DropZone = ({ onDrop, disabled }) => {
const [isDragging, setIsDragging] = useState(false);
const fileInputRef = useRef(null);
const handleDragEnter = useCallback((e) => {
e.preventDefault();
e.stopPropagation();
setIsDragging(true);
}, []);
const handleDragLeave = useCallback((e) => {
e.preventDefault();
e.stopPropagation();
setIsDragging(false);
}, []);
const handleDragOver = useCallback((e) => {
e.preventDefault();
e.stopPropagation();
}, []);
const handleDrop = useCallback((e) => {
e.preventDefault();
e.stopPropagation();
setIsDragging(false);
const files = e.dataTransfer.files;
if (files && files[0]) {
onDrop(files[0]);
}
}, [onDrop]);
const handleFileSelect = useCallback((e) => {
const files = e.target.files;
if (files && files[0]) {
onDrop(files[0]);
}
}, [onDrop]);
return (
<div
className={`dropzone ${isDragging ? 'dragging' : ''} ${disabled ? 'disabled' : ''}`}
onDragEnter={handleDragEnter}
onDragLeave={handleDragLeave}
onDragOver={handleDragOver}
onDrop={handleDrop}
onClick={() => !disabled && fileInputRef.current?.click()}
style={{
border: '2px dashed #ccc',
borderRadius: '8px',
padding: '40px',
textAlign: 'center',
cursor: disabled ? 'not-allowed' : 'pointer',
backgroundColor: isDragging ? '#f0f0f0' : 'white',
transition: 'all 0.3s ease',
}}
>
<input
ref={fileInputRef}
type="file"
accept="image/*"
onChange={handleFileSelect}
style={{ display: 'none' }}
disabled={disabled}
/>
<div className="dropzone-content">
<svg width="64" height="64" viewBox="0 0 24 24" fill="none" stroke="currentColor">
<path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" />
<polyline points="17 8 12 3 7 8" />
<line x1="12" y1="3" x2="12" y2="15" />
</svg>
<p style={{ marginTop: '16px', fontSize: '18px', fontWeight: '500' }}>
{disabled ? 'Processing...' : 'Drop image here or click to browse'}
</p>
<p style={{ marginTop: '8px', fontSize: '14px', color: '#666' }}>
Supports PNG, JPG, WebP (max 50MB)
</p>
</div>
</div>
);
};
/**
* Image Preview Component
*/
const ImagePreview = ({ originalUrl, processedUrl, maskUrl }) => {
const [viewMode, setViewMode] = useState('processed');
return (
<div className="image-preview" style={{ marginTop: '20px' }}>
<div className="preview-controls" style={{ marginBottom: '16px' }}>
<button
onClick={() => setViewMode('original')}
style={{
padding: '8px 16px',
marginRight: '8px',
backgroundColor: viewMode === 'original' ? '#007bff' : '#f0f0f0',
color: viewMode === 'original' ? 'white' : 'black',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
}}
>
Original
</button>
<button
onClick={() => setViewMode('processed')}
style={{
padding: '8px 16px',
marginRight: '8px',
backgroundColor: viewMode === 'processed' ? '#007bff' : '#f0f0f0',
color: viewMode === 'processed' ? 'white' : 'black',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
}}
>
Processed
</button>
{maskUrl && (
<button
onClick={() => setViewMode('mask')}
style={{
padding: '8px 16px',
backgroundColor: viewMode === 'mask' ? '#007bff' : '#f0f0f0',
color: viewMode === 'mask' ? 'white' : 'black',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
}}
>
Mask
</button>
)}
</div>
<div className="preview-image" style={{ position: 'relative', display: 'inline-block' }}>
{viewMode === 'original' && originalUrl && (
<img src={originalUrl} alt="Original" style={{ maxWidth: '100%', height: 'auto' }} />
)}
{viewMode === 'processed' && processedUrl && (
<img src={processedUrl} alt="Processed" style={{ maxWidth: '100%', height: 'auto' }} />
)}
{viewMode === 'mask' && maskUrl && (
<img src={maskUrl} alt="Mask" style={{ maxWidth: '100%', height: 'auto' }} />
)}
</div>
</div>
);
};
/**
* Progress Bar Component
*/
const ProgressBar = ({ progress }) => {
return (
<div className="progress-bar" style={{ marginTop: '20px' }}>
<div
style={{
width: '100%',
height: '8px',
backgroundColor: '#f0f0f0',
borderRadius: '4px',
overflow: 'hidden',
}}
>
<div
style={{
width: `${progress}%`,
height: '100%',
backgroundColor: '#007bff',
transition: 'width 0.3s ease',
}}
/>
</div>
<p style={{ marginTop: '8px', textAlign: 'center', fontSize: '14px' }}>
{progress}% Complete
</p>
</div>
);
};
/**
* Main BackgroundRemover Component
*/
const BackgroundRemover = ({ apiKey = API_KEY }) => {
const [originalImage, setOriginalImage] = useState(null);
const [processedImage, setProcessedImage] = useState(null);
const [maskImage, setMaskImage] = useState(null);
const [selectedQuality, setSelectedQuality] = useState('high');
const [selectedModel, setSelectedModel] = useState('auto');
const [returnMask, setReturnMask] = useState(false);
const { removeBackground, isProcessing, progress, error } = useBackgroundFX(apiKey);
const handleFileDrop = useCallback(async (file) => {
// Validate file
if (!file.type.startsWith('image/')) {
alert('Please upload an image file');
return;
}
if (file.size > 50 * 1024 * 1024) {
alert('File size must be less than 50MB');
return;
}
// Create preview
const reader = new FileReader();
reader.onload = (e) => {
setOriginalImage(e.target.result);
};
reader.readAsDataURL(file);
// Process image
try {
const result = await removeBackground(file, {
quality: selectedQuality,
model: selectedModel,
return_mask: returnMask,
});
setProcessedImage(result.image);
if (result.mask) {
setMaskImage(result.mask);
}
} catch (err) {
console.error('Processing failed:', err);
}
}, [removeBackground, selectedQuality, selectedModel, returnMask]);
const handleDownload = useCallback((url, filename) => {
const link = document.createElement('a');
link.href = url;
link.download = filename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}, []);
const handleReset = useCallback(() => {
setOriginalImage(null);
setProcessedImage(null);
setMaskImage(null);
}, []);
return (
<div className="background-remover" style={{ maxWidth: '800px', margin: '0 auto', padding: '20px' }}>
<h1 style={{ textAlign: 'center', marginBottom: '32px' }}>
BackgroundFX Pro - React Example
</h1>
{/* Settings */}
<div className="settings" style={{ marginBottom: '24px' }}>
<div style={{ marginBottom: '16px' }}>
<label style={{ marginRight: '16px' }}>
Quality:
<select
value={selectedQuality}
onChange={(e) => setSelectedQuality(e.target.value)}
style={{ marginLeft: '8px', padding: '4px 8px' }}
>
<option value="low">Low</option>
<option value="medium">Medium</option>
<option value="high">High</option>
<option value="ultra">Ultra</option>
</select>
</label>
<label style={{ marginRight: '16px' }}>
Model:
<select
value={selectedModel}
onChange={(e) => setSelectedModel(e.target.value)}
style={{ marginLeft: '8px', padding: '4px 8px' }}
>
<option value="auto">Auto</option>
<option value="rembg">RemBG</option>
<option value="u2net">U2Net</option>
<option value="sam2">SAM2</option>
</select>
</label>
<label>
<input
type="checkbox"
checked={returnMask}
onChange={(e) => setReturnMask(e.target.checked)}
style={{ marginRight: '8px' }}
/>
Return Mask
</label>
</div>
</div>
{/* Drop Zone */}
{!originalImage && (
<DropZone onDrop={handleFileDrop} disabled={isProcessing} />
)}
{/* Progress Bar */}
{isProcessing && <ProgressBar progress={progress} />}
{/* Error Message */}
{error && (
<div
style={{
marginTop: '16px',
padding: '12px',
backgroundColor: '#fee',
border: '1px solid #fcc',
borderRadius: '4px',
color: '#c00',
}}
>
Error: {error}
</div>
)}
{/* Image Preview */}
{(originalImage || processedImage) && (
<ImagePreview
originalUrl={originalImage}
processedUrl={processedImage}
maskUrl={maskImage}
/>
)}
{/* Action Buttons */}
{processedImage && (
<div className="actions" style={{ marginTop: '24px', textAlign: 'center' }}>
<button
onClick={() => handleDownload(processedImage, 'processed.png')}
style={{
padding: '10px 20px',
marginRight: '8px',
backgroundColor: '#28a745',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
}}
>
Download Result
</button>
{maskImage && (
<button
onClick={() => handleDownload(maskImage, 'mask.png')}
style={{
padding: '10px 20px',
marginRight: '8px',
backgroundColor: '#17a2b8',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
}}
>
Download Mask
</button>
)}
<button
onClick={handleReset}
style={{
padding: '10px 20px',
backgroundColor: '#dc3545',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
}}
>
Process Another
</button>
</div>
)}
{/* Instructions */}
<div className="instructions" style={{ marginTop: '40px', padding: '20px', backgroundColor: '#f8f9fa', borderRadius: '8px' }}>
<h3>How to use:</h3>
<ol>
<li>Select quality and model settings</li>
<li>Drag and drop an image or click to browse</li>
<li>Wait for processing to complete</li>
<li>Preview and download your results</li>
</ol>
<h3>Integration:</h3>
<pre style={{ backgroundColor: '#fff', padding: '12px', borderRadius: '4px', overflow: 'auto' }}>
{`npm install axios
import BackgroundRemover from './BackgroundRemover';
<BackgroundRemover apiKey="your-api-key" />`}
</pre>
</div>
</div>
);
};
export default BackgroundRemover;
export { useBackgroundFX, DropZone, ImagePreview, ProgressBar };