آموزش جامع Cropper.js
برش و ویرایش تصویر حرفهای در مرورگر — Drag، Zoom، Rotate، Flip و خروجی Canvas.
📦معرفی و نصب
Cropper.js یک کتابخانه JavaScript است که قابلیت برش تصویر (Image Cropping) را مستقیماً در مرورگر فراهم میکند — بدون نیاز به سرور برای پردازش اولیه. ایدهآل برای آپلود تصویر پروفایل، تصویر محصولات، و هر جایی که کاربر باید تصویر را crop کند.
| قابلیت | توضیح |
|---|---|
| Drag & Drop | حرکت و تنظیم crop box با درگ ماوس |
| Zoom | زوم با wheel ماوس یا touch pinch |
| Rotate | چرخش تصویر (هر تعداد درجه) |
| Flip | آینه افقی/عمودی |
| Aspect Ratio | قفل نسبت ابعاد (1:1، 16:9، 4:3 و...) |
| Live Preview | پیشنمایش زنده در هر شکل دلخواه |
| Canvas Output | دریافت تصویر برششده با کیفیت دلخواه |
| Mobile Support | پشتیبانی از touch و pinch-to-zoom |
نصب
<!-- CSS را در head بگذارید --> <link rel="stylesheet" href="cropper.min.css"> <!-- JS را قبل از استفاده بگذارید --> <script src="cropper.min.js"></script>
ساختار HTML
<!-- تصویر باید داخل یک container باشد --> <div style="max-width: 600px"> <img id="my-image" src="photo.jpg" alt="عکس"> </div> <!-- Preview (اختیاری) --> <div class="preview" style="width:100px;height:100px;overflow:hidden"></div> <script> const cropper = new Cropper( document.getElementById('my-image'), { aspectRatio: 1, // نسبت 1:1 viewMode: 1, // cropbox داخل تصویر preview: '.preview' // پیشنمایش زنده } ); </script>
display:block به تصویر بدهید.
✂️دمو زنده — برش پایه
Cropper.js را بهصورت زنده امتحان کنید. Crop box را جابجا کنید، اندازه آن را تغییر دهید یا از دکمهها استفاده کنید.
👁 Preview زنده:
👤 نمایش دایرهای:
چرخش و آینه:
زوم و حرکت:
Aspect Ratio:
خروجی:
⚙گزینهها (Options)
| گزینه | نوع | پیشفرض | توضیح |
|---|---|---|---|
| aspectRatio | number | NaN | نسبت ابعاد — NaN=آزاد، 1=مربع، 16/9=... |
| viewMode | 0/1/2/3 | 0 | محدودیت حرکت cropbox |
| dragMode | string | 'crop' | 'crop'، 'move'، 'none' |
| preview | string/element | '' | selector برای live preview |
| autoCropArea | number | 0.8 | اندازه اولیه cropbox (0 تا 1) |
| movable | boolean | true | قابلیت حرکت تصویر |
| rotatable | boolean | true | قابلیت چرخش |
| scalable | boolean | true | قابلیت scale/flip |
| zoomable | boolean | true | قابلیت zoom |
| zoomOnWheel | boolean | true | zoom با scroll ماوس |
| guides | boolean | true | خطوط راهنما (rule of thirds) |
| background | boolean | true | پسزمینه checkered |
| modal | boolean | true | نمایش overlay تیره |
| responsive | boolean | true | سازگاری با resize |
| checkOrientation | boolean | true | اصلاح خودکار orientation EXIF |
| minCropBoxWidth | number | 0 | حداقل عرض cropbox |
| minCropBoxHeight | number | 0 | حداقل ارتفاع cropbox |
| ready | function | null | callback وقتی آماده شد |
| crop | function | null | callback در هر تغییر |
const cropper = new Cropper(image, { aspectRatio: 16 / 9, viewMode: 1, dragMode: 'move', preview: '.preview', autoCropArea: 0.8, guides: true, background: false, // پنهان کردن checkered minCropBoxWidth: 100, minCropBoxHeight: 100, ready() { console.log('Cropper آماده شد!'); }, crop(event) { // در هر تغییر cropbox صدا زده میشود console.log(event.detail); // { x, y, width, height, rotate, scaleX, scaleY } } });
📐Aspect Ratio و ViewMode
Aspect Ratio — نسبت ابعاد
| مقدار | نسبت | کاربرد |
|---|---|---|
| 1 | 1:1 | تصویر پروفایل، آواتار مربعی |
| 16/9 | 16:9 | ویدیو، Banner، Hero section |
| 4/3 | 4:3 | تصویر استاندارد |
| 3/2 | 3:2 | عکسهای DSLR |
| 2/3 | 2:3 | تصویر عمودی (پورتریت) |
| NaN | آزاد | هر نسبتی (پیشفرض) |
ViewMode — محدودیت حرکت
| مقدار | رفتار |
|---|---|
| 0 | cropbox میتواند از تصویر خارج شود (پیشفرض) |
| 1 | cropbox محدود به داخل تصویر است ← توصیه شده |
| 2 | مثل 1 + تصویر کوچکتر از canvas نمیشود |
| 3 | مثل 1 + تصویر کوچکتر از cropbox نمیشود |
// تغییر aspect ratio بعد از ایجاد cropper.setAspectRatio(16/9); cropper.setAspectRatio(NaN); // آزاد // تنظیم دقیق cropbox با کد cropper.setCropBoxData({ left: 50, top: 30, width: 400, height: 225 }); // دریافت اطلاعات cropbox const box = cropper.getCropBoxData(); console.log(box); // { left, top, width, height } // دریافت کامل دادههای crop const data = cropper.getData(); console.log(data); // { x, y, width, height, rotate, scaleX, scaleY }
⚡متدهای مهم
| متد | توضیح |
|---|---|
| rotate(deg) | چرخش نسبی به تعداد درجه (+/-) |
| rotateTo(deg) | چرخش مطلق به زاویه مشخص |
| scaleX(x) | آینه افقی: scaleX(-1) / برگشت: scaleX(1) |
| scaleY(y) | آینه عمودی: scaleY(-1) / برگشت: scaleY(1) |
| zoom(ratio) | زوم نسبی: 0.1=+10%، -0.1=-10% |
| zoomTo(ratio, pivot?) | زوم مطلق: 1=100%، 1.5=150% |
| move(x, y) | حرکت تصویر (px) |
| moveTo(x, y?) | حرکت به موقعیت مطلق |
| reset() | برگشت به حالت اولیه |
| clear() | حذف cropbox |
| crop() | نمایش مجدد cropbox بعد از clear() |
| enable() / disable() | فعال/غیرفعال کردن تعامل |
| destroy() | حذف کامل و آزادسازی حافظه |
| replace(url) | جایگزینی تصویر با URL جدید |
| getData(rounded?) | دریافت اطلاعات crop |
| setData(data) | تنظیم cropbox با داده |
| getCroppedCanvas(options) | دریافت canvas برششده |
| getImageData() | اطلاعات تصویر اصلی |
| getCanvasData() | اطلاعات canvas |
// چرخش cropper.rotate(90); // +90 از حالت فعلی cropper.rotateTo(0); // برگشت به زاویه صفر // آینه let scaleX = 1; cropper.scaleX(scaleX *= -1); // toggle // زوم به مرکز cropper.zoomTo(1.5); // 150% cropper.zoomTo(1); // 100% (اصلی) // تنظیم cropbox با setData cropper.setData({ x: 50, y: 30, width: 400, height: 225, rotate: 0, scaleX: 1, scaleY: 1 });
🔔رویدادها (Events)
Cropper.js رویدادهای مختلفی را در مراحل مختلف تعامل کاربر میزند. میتوانید با addEventListener به آنها گوش دهید.
| رویداد | زمان اجرا | event.detail |
|---|---|---|
| ready | وقتی Cropper آماده شد | — |
| cropstart | شروع تغییر cropbox | { originalEvent, action } |
| cropmove | حین حرکت cropbox | { originalEvent, action } |
| cropend | پایان تغییر cropbox | { originalEvent, action } |
| crop | بعد از هر تغییر cropbox | { x, y, width, height, rotate, scaleX, scaleY } |
| zoom | هنگام zoom | { oldRatio, ratio, originalEvent } |
مقادیر action در cropstart/cropmove/cropend
// action میتواند این مقادیر باشد: 'crop' // ساختن cropbox جدید 'move' // حرکت دادن cropbox 'e' // کشیدن لبه east 'w' // کشیدن لبه west 's' // کشیدن لبه south 'n' // کشیدن لبه north 'se' // کشیدن گوشه جنوبشرقی 'all' // حرکت تصویر 'zoom' // zoom
// نمونه: گوش دادن به رویدادها const img = document.getElementById('my-image'); img.addEventListener('ready', () => { console.log('Cropper آماده است!'); }); img.addEventListener('crop', event => { const { x, y, width, height } = event.detail; // بهروزرسانی UI با ابعاد infoEl.textContent = `${Math.round(width)} × ${Math.round(height)} px`; }); img.addEventListener('zoom', event => { if (event.detail.ratio > 3) { event.preventDefault(); // جلوگیری از زوم بیش از 300% } console.log('زوم:', Math.round(event.detail.ratio * 100) + '%'); });
ابعاد Crop:
لاگ رویدادها:
📤خروجی Canvas
با getCroppedCanvas() یک canvas HTML5 دریافت میکنید که میتوانید آن را به dataURL، Blob یا فایل دانلودی تبدیل کنید.
const canvas = cropper.getCroppedCanvas({ width: 800, // عرض خروجی (px) height: 800, // ارتفاع خروجی minWidth: 100, maxWidth: 4096, minHeight: 100, maxHeight: 4096, fillColor: '#fff', // رنگ پسزمینه (برای PNG شفاف) imageSmoothingEnabled: true, imageSmoothingQuality: 'high' // 'low' | 'medium' | 'high' }); // ۱. تبدیل به dataURL (base64) const png = canvas.toDataURL('image/png'); const jpeg = canvas.toDataURL('image/jpeg', 0.9); // 90% کیفیت const webp = canvas.toDataURL('image/webp', 0.85); // 85% کیفیت // ۲. تبدیل به Blob (برای ارسال به سرور) canvas.toBlob(blob => { const url = URL.createObjectURL(blob); console.log('حجم: ' + (blob.size / 1024).toFixed(1) + ' KB'); }, 'image/jpeg', 0.9); // ۳. دانلود مستقیم const a = document.createElement('a'); a.download = 'cropped.png'; a.href = canvas.toDataURL(); a.click();
نتیجه:
🌐ارسال به سرور
پس از برش، تصویر را با FormData و Fetch (یا Axios) به سرور ارسال کنید.
با Fetch API
async function uploadCropped(cropper) { return new Promise((resolve, reject) => { cropper.getCroppedCanvas({ width: 800, height: 800, imageSmoothingQuality: 'high' }).toBlob(async blob => { const formData = new FormData(); formData.append('image', blob, 'cropped.jpg'); formData.append('userId', '12345'); try { const res = await fetch('/api/avatar', { method: 'POST', body: formData // Content-Type نباید دستارسال شود — مرورگر boundary را میگذارد }); const data = await res.json(); resolve(data); } catch(err) { reject(err); } }, 'image/jpeg', 0.92); }); }
با Axios و نمایش Progress
async function uploadWithProgress(cropper, onProgress) { return new Promise(resolve => { cropper.getCroppedCanvas({ width: 800 }).toBlob(async blob => { const fd = new FormData(); fd.append('file', blob, 'avatar.jpg'); const res = await axios.post('/api/upload', fd, { onUploadProgress: e => { const pct = Math.round(e.loaded / e.total * 100); onProgress(pct); } }); resolve(res.data); }, 'image/jpeg', 0.9); }); } // استفاده await uploadWithProgress(myCropper, pct => { progressBar.style.width = pct + '%'; label.textContent = pct + '٪'; });
Content-Type: multipart/form-data را دستی تنظیم نکنید. مرورگر بهصورت خودکار این هدر را با boundary صحیح تنظیم میکند. اگر دستی بگذارید، boundary درست نخواهد بود و سرور نمیتواند فایل را parse کند.
📁آپلود و برش تصویر کاربر
رایجترین کاربرد: کاربر تصویر آپلود میکند، crop میدهد، و نتیجه دریافت میشود.
// بارگذاری تصویر از input document.getElementById('file-input').addEventListener('change', e => { const file = e.target.files[0]; if (!file || !file.type.startsWith('image/')) return; const url = URL.createObjectURL(file); // بهتر از FileReader const img = document.getElementById('crop-img'); if (window.myCropper) { window.myCropper.replace(url); // جایگزین تصویر قبلی } else { img.src = url; window.myCropper = new Cropper(img, { aspectRatio: 1, viewMode: 1, preview: '.preview' }); } });
📁
کلیک کنید یا تصویر را اینجا بکشید
PNG، JPG، WebP، GIF
🌟پروژه کامل: تصویر پروفایل
یک سناریوی واقعی و کامل: کاربر تصویر آپلود میکند، crop مربعی 1:1 میدهد، و تصویر در کارت پروفایل نمایش داده میشود.
// کد کامل پروژه آپلود تصویر پروفایل let profileCropper = null; let scaleXVal = 1, scaleYVal = 1; document.getElementById('avatar-input').addEventListener('change', e => { const file = e.target.files[0]; if (!file) return; const url = URL.createObjectURL(file); const img = document.getElementById('avatar-img'); if (profileCropper) profileCropper.replace(url); else { img.src = url; profileCropper = new Cropper(img, { aspectRatio: 1, // مربع کامل viewMode: 1, dragMode: 'move', autoCropArea:0.9, preview: '.avatar-preview', ready() { document.getElementById('crop-actions').style.display = 'flex'; } }); } document.getElementById('crop-panel').style.display = 'block'; }); function applyAvatar() { const canvas = profileCropper.getCroppedCanvas({ width:300, height:300 }); document.getElementById('profile-avatar').src = canvas.toDataURL('image/jpeg', 0.9); }
👤
انتخاب تصویر پروفایل
پیشنمایش کارت:
- از
URL.createObjectURL()به جایFileReaderاستفاده کنید — سریعتر و بدون encode base64 - بعد از استفاده از URL،
URL.revokeObjectURL(url)فراخوانی کنید تا حافظه آزاد شود - اندازه خروجی را با
widthوheightمحدود کنید (معمولاً 400-800px کافی است) - برای JPEG کیفیت 0.85-0.92 معمولاً بهترین تعادل کیفیت/حجم است
🚀زمین بازی کد (Playground)
با Cropper.js روی یک تصویر نمونه کار کن: ناحیهٔ برش را بکش، گزینهها (aspectRatio، viewMode) را تغییر بده و متدها (چرخش/زوم/برش) را امتحان کن. نسخهٔ ۱.۶.۲ آفلاین بارگذاری میشود.