📦معرفی و نصب

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>
ⓘ نکته مهم: تصویر باید داخل یک container div باشد. Cropper.js تصویر اصلی را پنهان کرده و یک canvas جایگزین آن می‌سازد. همیشه display:block به تصویر بدهید.

✂️دمو زنده — برش پایه

Cropper.js را به‌صورت زنده امتحان کنید. Crop box را جابجا کنید، اندازه آن را تغییر دهید یا از دکمه‌ها استفاده کنید.

▶ دمو: تمام قابلیت‌های Cropper.js
نمونه تصویر

👁 Preview زنده:

👤 نمایش دایره‌ای:

چرخش و آینه:

زوم و حرکت:

Aspect Ratio:

خروجی:

گزینه‌ها (Options)

گزینهنوعپیش‌فرضتوضیح
aspectRationumberNaNنسبت ابعاد — NaN=آزاد، 1=مربع، 16/9=...
viewMode0/1/2/30محدودیت حرکت cropbox
dragModestring'crop''crop'، 'move'، 'none'
previewstring/element''selector برای live preview
autoCropAreanumber0.8اندازه اولیه cropbox (0 تا 1)
movablebooleantrueقابلیت حرکت تصویر
rotatablebooleantrueقابلیت چرخش
scalablebooleantrueقابلیت scale/flip
zoomablebooleantrueقابلیت zoom
zoomOnWheelbooleantruezoom با scroll ماوس
guidesbooleantrueخطوط راهنما (rule of thirds)
backgroundbooleantrueپس‌زمینه checkered
modalbooleantrueنمایش overlay تیره
responsivebooleantrueسازگاری با resize
checkOrientationbooleantrueاصلاح خودکار orientation EXIF
minCropBoxWidthnumber0حداقل عرض cropbox
minCropBoxHeightnumber0حداقل ارتفاع cropbox
readyfunctionnullcallback وقتی آماده شد
cropfunctionnullcallback در هر تغییر
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 — نسبت ابعاد

مقدارنسبتکاربرد
11:1تصویر پروفایل، آواتار مربعی
16/916:9ویدیو، Banner، Hero section
4/34:3تصویر استاندارد
3/23:2عکس‌های DSLR
2/32:3تصویر عمودی (پورتریت)
NaNآزادهر نسبتی (پیش‌فرض)

ViewMode — محدودیت حرکت

مقداررفتار
0cropbox می‌تواند از تصویر خارج شود (پیش‌فرض)
1cropbox محدود به داخل تصویر است ← توصیه شده
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 box را جابجا کنید
event demo

ابعاد 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 + '٪';
});
💡 نکته مهم: هنگام ارسال FormData با Fetch یا Axios، هرگز Content-Type: multipart/form-data را دستی تنظیم نکنید. مرورگر به‌صورت خودکار این هدر را با boundary صحیح تنظیم می‌کند. اگر دستی بگذارید، boundary درست نخواهد بود و سرور نمی‌تواند فایل را parse کند.
▶ دمو: شبیه‌سازی آپلود با Progress

📁آپلود و برش تصویر کاربر

رایج‌ترین کاربرد: کاربر تصویر آپلود می‌کند، 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);
}
▶ دمو: آپلود تصویر پروفایل کامل

👤

انتخاب تصویر پروفایل

پیش‌نمایش کارت:

avatar
مصطفی موسوی
توسعه‌دهنده Front-End
💡 نکات پروژه واقعی:
  • از URL.createObjectURL() به جای FileReader استفاده کنید — سریع‌تر و بدون encode base64
  • بعد از استفاده از URL، URL.revokeObjectURL(url) فراخوانی کنید تا حافظه آزاد شود
  • اندازه خروجی را با width و height محدود کنید (معمولاً 400-800px کافی است)
  • برای JPEG کیفیت 0.85-0.92 معمولاً بهترین تعادل کیفیت/حجم است

🚀زمین بازی کد (Playground)

با Cropper.js روی یک تصویر نمونه کار کن: ناحیهٔ برش را بکش، گزینه‌ها (aspectRatio، viewMode) را تغییر بده و متدها (چرخش/زوم/برش) را امتحان کن. نسخهٔ ۱.۶.۲ آفلاین بارگذاری می‌شود.

💡 تصویر نمونه به‌صورت آفلاین داخل کد جاسازی شده است. خروجی متدها در پنل کنسول نمایش داده می‌شود.