بواسطة morbah |
أداة قص الصور المجمعة
ارفع عدة صور مرة واحدة، سيتم قصها وتجميعها في ملف ZIP جاهز للتحميل.
<!-- بداية كود أداة قص الصور المجمعة -->
<!-- 1. التنسيقات (CSS) -->
<style>
#batch-cropper-container {
font-family: 'Cairo', sans-serif;
max-width: 800px;
margin: 20px auto;
padding: 25px;
border: 1px solid #ddd;
border-radius: 10px;
background-color: #f9f9f9;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
direction: rtl;
}
#batch-cropper-container h2 {
text-align: center;
color: #333;
margin-bottom: 20px;
}
.cropper-controls {
display: flex;
flex-direction: column;
gap: 15px;
margin-bottom: 20px;
padding: 20px;
background-color: #fff;
border-radius: 8px;
}
.control-group {
display: flex;
align-items: center;
gap: 10px;
}
.control-group label {
font-weight: bold;
color: #555;
white-space: nowrap;
}
#batch-cropper-container input[type="file"],
#batch-cropper-container input[type="number"] {
padding: 8px;
border: 1px solid #ccc;
border-radius: 5px;
font-size: 14px;
flex-grow: 1;
}
#process-button {
padding: 12px;
font-size: 18px;
font-weight: bold;
color: #fff;
background-color: #007bff;
border: none;
border-radius: 5px;
cursor: pointer;
transition: background-color 0.3s, opacity 0.3s;
}
#process-button:disabled {
background-color: #5a9ed8;
cursor: not-allowed;
opacity: 0.7;
}
#status-message {
text-align: center;
margin: 15px 0;
font-size: 16px;
font-weight: bold;
color: #28a745;
min-height: 24px;
}
#results-area h3 {
text-align: center;
color: #444;
border-bottom: 2px solid #eee;
padding-bottom: 10px;
margin-bottom: 15px;
}
#results-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
gap: 15px;
margin-top: 10px;
padding: 10px;
background-color: #fff;
border-radius: 8px;
min-height: 100px;
border: 1px dashed #ccc;
}
.thumbnail-item {
position: relative;
border: 1px solid #ddd;
border-radius: 5px;
overflow: hidden;
padding: 5px;
text-align: center;
background: #f9f9f9;
}
.thumbnail-item img {
max-width: 100%;
height: auto;
display: block;
}
.thumbnail-item p {
font-size: 12px;
margin: 5px 0 0 0;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
color: #666;
}
</style>
<!-- 2. هيكل الأداة (HTML) -->
<div id="batch-cropper-container">
<h2>أداة قص الصور المجمعة</h2>
<p style="text-align: center; margin-bottom: 20px;">
ارفع عدة صور مرة واحدة، سيتم قصها وتجميعها في ملف ZIP جاهز للتحميل.
</p>
<div class="cropper-controls">
<div class="control-group">
<label for="image-input">1. اختر الصور:</label>
<input type="file" id="image-input" accept="image/*" multiple>
</div>
<div class="control-group">
<label for="crop-amount">2. مقدار القص (بالبكسل):</label>
<input type="number" id="crop-amount" value="129" min="1">
</div>
<button id="process-button">3. ابدأ المعالجة والتنزيل</button>
</div>
<div id="status-message"></div>
<div id="results-area" style="display:none;">
<h3>معاينة النتائج</h3>
<div id="results-grid"></div>
</div>
<!-- Canvas مخفي للمعالجة -->
<canvas id="image-canvas" style="display:none;"></canvas>
</div>
<!-- 3. الشيفرة البرمجية (JavaScript) -->
<!-- تحميل مكتبة JSZip لضغط الملفات -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
<script>
// تعريف عناصر الواجهة
const imageInput = document.getElementById('image-input');
const cropAmountInput = document.getElementById('crop-amount');
const processButton = document.getElementById('process-button');
const statusMessage = document.getElementById('status-message');
const resultsArea = document.getElementById('results-area');
const resultsGrid = document.getElementById('results-grid');
const canvas = document.getElementById('image-canvas');
const ctx = canvas.getContext('2d');
imageInput.addEventListener('change', () => {
if (imageInput.files.length > 0) {
statusMessage.textContent = `${imageInput.files.length} صورة جاهزة للمعالجة.`;
resultsArea.style.display = 'none';
resultsGrid.innerHTML = '';
} else {
statusMessage.textContent = '';
}
});
// دالة لقص صورة واحدة وإرجاعها كـ Blob
function cropImage(file, cropAmount) {
return new Promise((resolve, reject) => {
const reader = new FileReader();
reader.readAsDataURL(file);
reader.onload = (e) => {
const img = new Image();
img.src = e.target.result;
img.onload = () => {
if (cropAmount >= img.naturalHeight) {
return reject(new Error(`قيمة القص (${cropAmount}px) أكبر من ارتفاع الصورة "${file.name}" (${img.naturalHeight}px).`));
}
const newHeight = img.naturalHeight - cropAmount;
canvas.width = img.naturalWidth;
canvas.height = newHeight;
ctx.drawImage(img, 0, 0, img.naturalWidth, newHeight, 0, 0, canvas.width, canvas.height);
// تحويل Canvas إلى Blob (أفضل للذاكرة وللملفات المضغوطة)
canvas.toBlob((blob) => {
resolve({ blob, filename: file.name, originalSrc: img.src });
}, 'image/png');
};
img.onerror = () => reject(new Error(`فشل تحميل الصورة: ${file.name}`));
};
reader.onerror = () => reject(new Error('فشل في قراءة الملف.'));
});
}
// عند الضغط على زر المعالجة
processButton.addEventListener('click', async () => {
const files = imageInput.files;
const cropAmount = parseInt(cropAmountInput.value, 10);
if (files.length === 0) {
statusMessage.textContent = 'الرجاء اختيار صورة واحدة على الأقل!';
statusMessage.style.color = '#d9534f';
return;
}
if (isNaN(cropAmount) || cropAmount <= 0) {
statusMessage.textContent = 'الرجاء إدخال قيمة قص صحيحة!';
statusMessage.style.color = '#d9534f';
return;
}
// تعطيل الزر وإظهار حالة المعالجة
processButton.disabled = true;
processButton.textContent = 'جاري المعالجة...';
resultsGrid.innerHTML = '';
resultsArea.style.display = 'block';
statusMessage.style.color = '#007bff';
const zip = new JSZip();
let processedCount = 0;
for (const file of files) {
try {
statusMessage.textContent = `جاري معالجة الصورة ${processedCount + 1} من ${files.length}: ${file.name}`;
const { blob, filename } = await cropImage(file, cropAmount);
// إضافة الملف المقصوص إلى الـ ZIP
zip.file(filename, blob);
// عرض معاينة للصورة المقصوصة
const objectURL = URL.createObjectURL(blob);
const thumbItem = document.createElement('div');
thumbItem.className = 'thumbnail-item';
thumbItem.innerHTML = `<img src="${objectURL}" alt="${filename}"><p>${filename}</p>`;
resultsGrid.appendChild(thumbItem);
processedCount++;
} catch (error) {
// في حالة حدوث خطأ لصورة معينة
statusMessage.textContent = `خطأ: ${error.message}`;
statusMessage.style.color = '#d9534f';
const errorItem = document.createElement('div');
errorItem.className = 'thumbnail-item';
errorItem.style.borderColor = '#d9534f';
errorItem.innerHTML = `<p style="color:red; font-weight:bold;">فشل</p><p>${file.name}</p>`;
resultsGrid.appendChild(errorItem);
// لا نوقف العملية، بل نتجاوز الصورة التي فشلت
}
}
// التحقق مما إذا تمت معالجة أي صور بنجاح
if (processedCount > 0) {
statusMessage.textContent = `اكتملت المعالجة! جاري تجهيز ملف الـ ZIP...`;
statusMessage.style.color = '#28a745';
// إنشاء اسم الملف مع التاريخ
const today = new Date();
const dateString = `${today.getFullYear()}-${String(today.getMonth() + 1).padStart(2, '0')}-${String(today.getDate()).padStart(2, '0')}`;
const zipFilename = `Adobe_Stock_${dateString}.zip`;
// إنشاء وتنزيل ملف الـ ZIP
zip.generateAsync({ type: 'blob' }).then((content) => {
const link = document.createElement('a');
link.href = URL.createObjectURL(content);
link.download = zipFilename;
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
statusMessage.textContent = `تم تحميل ${processedCount} صورة بنجاح!`;
});
} else {
statusMessage.textContent = `لم يتم معالجة أي صورة بنجاح.`;
statusMessage.style.color = '#d9534f';
}
// إعادة تفعيل الزر
processButton.disabled = false;
processButton.textContent = 'ابدأ المعالجة والتنزيل';
});
</script>
<!-- نهاية كود أداة قص الصور المجمعة -->
انا مهتم بمجال التقنية والربح من الانترنت واتطلع لنشر المزيد من المقالات التي تفيدكم
تعليقات
إرسال تعليق