OptiImage Pro – Smart Image Compressor
/* — CSS SEPARATION START — */
@import url(‘https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&display=swap’);:root {
–primary: #F59E0B; /* Amber/Orange for “Compression” vibe */
–bg-dark: #111827;
}body {
font-family: ‘Inter’, sans-serif;
transition: background-color 0.3s, color 0.3s;
}/* Range Slider Styling */
input[type=range] {
-webkit-appearance: none;
background: transparent;
}
input[type=range]::-webkit-slider-thumb {
-webkit-appearance: none;
height: 20px;
width: 20px;
border-radius: 50%;
background: #F59E0B;
margin-top: -8px;
cursor: pointer;
box-shadow: 0 0 5px rgba(0,0,0,0.2);
transition: transform 0.1s;
}
input[type=range]::-webkit-slider-thumb:hover {
transform: scale(1.1);
}
input[type=range]::-webkit-slider-runnable-track {
width: 100%;
height: 6px;
cursor: pointer;
background: #E5E7EB;
border-radius: 3px;
}
.dark input[type=range]::-webkit-slider-runnable-track {
background: #4B5563;
}/* Dropzone Animation */
.dropzone-active {
border-color: #F59E0B !important;
background-color: rgba(245, 158, 11, 0.1) !important;
transform: scale(1.01);
}/* Result Item Animation */
.file-item {
animation: slideIn 0.3s ease-out forwards;
}@keyframes slideIn {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}/* Dark Mode */
.dark body {
background-color: var(–bg-dark);
color: #F9FAFB;
}
.dark .card {
background-color: #1F2937;
border-color: #374151;
}
/* — CSS SEPARATION END — */
tailwind.config = {
darkMode: ‘class’,
theme: {
extend: {
colors: {
primary: ‘#F59E0B’,
primaryDark: ‘#D97706’
}
}
}
}
Reduce file size by up to 80% without visible quality loss. Secure client-side processing.
Compression Quality
80%
Max Compression
Best Quality
Max Width (Optional)
Original Dimensions
1920px (Full HD)
1280px (HD)
800px (Web)
Clear All
Drop your images here Supports JPG, PNG, WEBP (Batch Processing)
Browse Files
Compressed Files 0
// — JAVASCRIPT SEPARATION START —// DOM Elements
const dropzone = document.getElementById(‘dropzone’);
const fileInput = document.getElementById(‘fileInput’);
const resultsContainer = document.getElementById(‘results-container’);
const canvas = document.getElementById(‘compress-canvas’);
const ctx = canvas.getContext(‘2d’);
// Controls
const qualitySlider = document.getElementById(‘quality-slider’);
const qualityVal = document.getElementById(‘quality-val’);
const maxWidthSelect = document.getElementById(‘max-width’);
const fileCountBadge = document.getElementById(‘file-count-badge’);let isDarkMode = false;
let processedCount = 0;// — Event Listeners —// Drag & Drop
dropzone.addEventListener(‘click’, () => fileInput.click());
dropzone.addEventListener(‘dragover’, (e) => {
e.preventDefault();
dropzone.firstElementChild.nextElementSibling.classList.add(‘dropzone-active’);
});
dropzone.addEventListener(‘dragleave’, () => {
dropzone.firstElementChild.nextElementSibling.classList.remove(‘dropzone-active’);
});
dropzone.addEventListener(‘drop’, (e) => {
e.preventDefault();
dropzone.firstElementChild.nextElementSibling.classList.remove(‘dropzone-active’);
if (e.dataTransfer.files.length) handleFiles(e.dataTransfer.files);
});fileInput.addEventListener(‘change’, (e) => {
if (e.target.files.length) handleFiles(e.target.files);
});// Quality Slider
qualitySlider.addEventListener(‘input’, (e) => {
qualityVal.innerText = `${e.target.value}%`;
});// — Main Logic —function handleFiles(files) {
resultsContainer.classList.remove(‘hidden’);
Array.from(files).forEach(file => {
if (!file.type.match(‘image.*’)) return;
processImage(file);
});
}function processImage(file) {
const reader = new FileReader();
reader.readAsDataURL(file);
// Create UI Shell first (Loading state)
const uiId = ‘file-‘ + Date.now() + Math.random().toString(16).slice(2);
createFileItemSkeleton(uiId, file);reader.onload = (event) => {
const img = new Image();
img.src = event.target.result;
img.onload = () => {
compressAndRender(img, file, uiId);
};
};
}function compressAndRender(img, originalFile, uiId) {
const quality = parseInt(qualitySlider.value) / 100;
const maxWidth = parseInt(maxWidthSelect.value);// Calculate new dimensions
let width = img.width;
let height = img.height;if (maxWidth > 0 && width > maxWidth) {
const ratio = maxWidth / width;
width = maxWidth;
height = height * ratio;
}// Draw to Canvas
canvas.width = width;
canvas.height = height;
ctx.fillStyle = ‘#FFFFFF’; // Fill transparent backgrounds for JPG
ctx.fillRect(0, 0, width, height);
ctx.drawImage(img, 0, 0, width, height);// Export
// We default to jpeg for better compression, or keep original type if png/webp is preferred but supported
const mimeType = originalFile.type === ‘image/png’ ? ‘image/jpeg’ : originalFile.type;
canvas.toBlob((blob) => {
if (!blob) {
console.error(‘Compression failed’);
return;
}
updateFileItemUI(uiId, originalFile, blob, img.src);
}, mimeType, quality);
}function createFileItemSkeleton(id, file) {
const div = document.createElement(‘div’);
div.id = id;
div.className = ‘file-item card bg-white dark:bg-gray-800 rounded-lg p-4 shadow-sm border border-gray-100 dark:border-gray-700 flex items-center gap-4’;
div.innerHTML = `
`;
resultsContainer.prepend(div);
processedCount++;
updateBadge();
}function updateFileItemUI(id, originalFile, compressedBlob, previewSrc) {
const div = document.getElementById(id);
if(!div) return;const savedBytes = originalFile.size – compressedBlob.size;
const savedPercent = ((savedBytes / originalFile.size) * 100).toFixed(0);
const isNegative = savedBytes < 0; // Did file get bigger? (possible with low quality input + high quality setting)const url = URL.createObjectURL(compressedBlob);
const dlName = `opti_${originalFile.name.substring(0, originalFile.name.lastIndexOf('.'))}.jpg`;div.innerHTML = `
${originalFile.name}
${formatBytes(originalFile.size)}
${formatBytes(compressedBlob.size)}
${isNegative ? ‘+’ : ‘-‘}${Math.abs(savedPercent)}%
Save
`;
}function formatBytes(bytes, decimals = 1) {
if (bytes === 0) return ‘0 Bytes’;
const k = 1024;
const dm = decimals < 0 ? 0 : decimals;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(dm)) + ' ' + sizes[i];
}function clearAll() {
resultsContainer.innerHTML = '';
resultsContainer.classList.add('hidden');
resultsContainer.innerHTML = `
Compressed Files 0 `;
fileInput.value = ”;
processedCount = 0;
updateBadge();
}function updateBadge() {
const badge = document.getElementById(‘file-count-badge’);
if(badge) badge.innerText = processedCount;
}// — Dark Mode —
function toggleDarkMode() {
isDarkMode = !isDarkMode;
document.documentElement.classList.toggle(‘dark’);
const icon = document.getElementById(‘theme-icon’);
if(isDarkMode) {
icon.classList.remove(‘fa-moon’);
icon.classList.add(‘fa-sun’);
} else {
icon.classList.remove(‘fa-sun’);
icon.classList.add(‘fa-moon’);
}
}
// — JAVASCRIPT SEPARATION END —
Using Image Compressor The Image Compressor utility is designed for image and graphics tasks. Check the on-page controls for available formats, limits and options. Always review the output for accuracy before sharing or publishing it.
Practical tips Keep your original files or source information as a backup. Choose suitable options using the controls provided by this tool. Check the generated result and retry with different settings if needed. Explore related tools Image Resizer are available for related tasks.