ScanRamz — دليل استخدام الماسح

ماسح باركود و QR بالكاميرا من المتصفح: سطرين في صفحتك وخلاص. الإصدار 0.2

البداية السريعة

حط عنصرًا فاضيًا في المكان اللي عاوز الماسح يظهر فيه، وحمّل الحزمة، ونادي ScanRamz.create:

<div id="scanner" style="width:100%;max-width:480px"></div>

<script src="https://aiscan.afaq-digital.com/sdk/scanramz.js"></script>
<script>
  const scanner = ScanRamz.create({
    license: "SR-XXXX-XXXX-XXXX",          // مفتاح الترخيص بتاعك
    container: "#scanner",
    onScan: (result) => {
      console.log(result.format, result.text);   // مثال: "code128" "2300228790"
    },
    onError: (error) => console.warn(error.code, error.message),
  });

  scanner.start();   // بيطلب إذن الكاميرا وبيبدأ المسح
</script>
شرطين لازم يتحققوا: الصفحة لازم تكون https:// (المتصفحات ما بتفتحش الكاميرا على http، ما عدا localhost)، والمستخدم لازم يوافق على إذن الكاميرا أول مرة.

الحزمة ملف واحد فيه كل حاجة — مفيش ملفات تانية ولا CSS ولا مكتبات. والماسح بياخد عرض العنصر الحاوي؛ لو العنصر ليه ارتفاع محدد بيملاه، وإلا بياخد شكل الكاميرا.

الخيارات — ScanRamz.create(options)

الخيارالنوعالافتراضيالشرح
containerنص أو عنصر— (إلزامي)العنصر اللي الماسح هيتبني جوّاه: "#scanner" أو العنصر نفسه
licenseنص—مفتاح الترخيص
aitrue / falsetrueموديل الذكاء الاصطناعي لتحديد مكان الرمز. شوف قسم الذكاء الاصطناعي
formatsمصفوفةكل الأنواعالأنواع المطلوبة بس، مثل ["code128", "qr"]. أنواع أقل = مسح أسرع
onScanدالة—بتتنادى لما رمز يتقرا — مرة واحدة لكل رمز (شوف repeatDelayMs)
onErrorدالة—بتستقبل { code, message }. شوف الأخطاء
onReadyدالة—الماسح جاهز
onStateChangeدالة—الحالة اتغيرت: "idle"، "starting"، "scanning"، "paused"، "destroyed"
onQuotaدالة—حصة المسحات قربت تخلص (باقي 10% أو أقل) أو خلصت. بتستقبل نفس كائن getUsage()
repeatDelayMsرقم3000نفس الرمز ما يتحسبش مسحًا جديدًا إلا لو غاب عن الكاميرا المدة دي (بالمللي ثانية)
selectنص"auto"لما يظهر أكتر من رمز: "auto" كل رمز يتبعت، "tap" المستخدم يضغط على اللي عاوزه، "tapIfMultiple" يضغط بس لو فيه أكتر من رمز. شوف اختيار الرمز بالضغط
overlaytrue / falsetrueرسم إطار أخضر حوالين الرمز المقروء
autoZoomtrue / falsetrueزوم تلقائي على الرمز البعيد
vibratetrue / falsetrueاهتزاز الموبايل عند كل مسح جديد
beeptrue / falsefalseصفارة قصيرة عند كل مسح جديد
collecttrue / falsetrueالسماح بإرسال عيّنات صور مصغّرة لتحسين الماسح — بتتبعت بس لو اشتراكك موافق على الجمع. false بيقفلها في الصفحة دي. شوف الخصوصية
lang"ar" / "en""ar"لغة الرسائل اللي بتظهر جوه الماسح

الدوال

كلها على الكائن اللي ScanRamz.create بيرجّعه. ولا واحدة منها بترمي استثناء — أي خطأ بييجي في onError.

scanner.start() — تشغيل

بتشغّل الكاميرا وبتبدأ المسح. بترجّع Promise قيمته true لو اشتغلت وfalse لو فشلت (والسبب في onError). لازم تتنادى من ضغطة زر على iPhone لو الصفحة لسه ما أخدتش إذن الكاميرا.

const ok = await scanner.start();
if (!ok) alert("الكاميرا ما اشتغلتش");

scanner.stop() — إيقاف

بتوقف المسح وبتقفل الكاميرا (لمبة الكاميرا بتطفي). تقدر تنادي start() تاني بعدها.

scanner.pause() و scanner.resume() — إيقاف مؤقت

pause() بتوقف القراءة والكاميرا لسه شغّالة والصورة ظاهرة — مفيدة وإنت بتعرض نتيجة أو نافذة تأكيد. وإنت في الوضع ده مفيش مسحات بتتحسب. resume() بترجّع المسح فورًا (من غير طلب إذن تاني).

onScan: (result) => {
  scanner.pause();                      // وقّف لحد ما المستخدم يأكّد
  showConfirm(result.text, () => scanner.resume());
}

scanner.setAI(true | false) — تشغيل/إيقاف الذكاء الاصطناعي

بتشغّل أو توقف موديل الذكاء الاصطناعي في أي وقت، والتغيير فوري. scanner.aiEnabled بترجّع الحالة الحالية.

scanner.setAI(false);          // الكاشف العادي بس
scanner.setAI(true);           // رجّع الذكاء الاصطناعي
console.log(scanner.aiEnabled);

scanner.setFormats([...]) — تحديد الأنواع

بتحدد الأنواع اللي الماسح يدوّر عليها. ابعت null للرجوع لكل الأنواع. scanner.getFormats() بترجّع الأنواع المفعّلة. شوف قايمة الأنواع.

scanner.setFormats(["code128"]);         // ملصقات الأصول بس
scanner.setFormats(["qr", "datamatrix"]);
scanner.setFormats(null);                // الكل

scanner.setSelect("auto" | "tap" | "tapIfMultiple") — اختيار الرمز بالضغط

ملصق جهاز عليه كذا باركود (السيريال، الموديل، MAC…) وإنت عاوز واحدًا بس؟ في وضع "tap" الماسح ما بيبعتش حاجة لوحده: كل الرموز اللي في الصورة بتظهر بإطار أزرق وفوق كل واحد قيمته، والمستخدم يضغط على الرمز (أو على قيمته) فيوصل onScan بالرمز ده بس — وهو بس اللي بيتحسب مسحًا.

القيمةالسلوك
"auto"الافتراضي: كل رمز يتقرا بيتبعت لـ onScan على طول
"tap"مفيش حاجة بتتبعت إلا اللي المستخدم يضغط عليه — حتى لو رمز واحد
"tapIfMultiple"رمز واحد في الصورة بيتبعت لوحده؛ رمزين أو أكتر المستخدم يضغط على واحد
const scanner = ScanRamz.create({
  license: "SR-XXXX-XXXX-XXXX",
  container: "#scanner",
  select: "tap",                        // المستخدم يختار الرمز بالضغط
  onScan: (r) => { document.getElementById("serial").value = r.text; scanner.stop(); },
});

scanner.setSelect("auto");              // تغييره في أي وقت
console.log(scanner.getSelect());

scanner.setTorch(true | false) — الكشاف

بتشغّل أو تطفي كشاف الموبايل. بترجّع Promise بالحالة الفعلية — false لو الكاميرا ما بتدعمش (كاميرات اللابتوب، وبعض متصفحات الموبايل).

scanner.setZoom(x) و scanner.getCapabilities() — الزوم

setZoom بتظبط الزوم يدويًّا وبترجّع القيمة اللي اتطبقت فعلًا. getCapabilities() بترجّع إمكانيات الكاميرا الشغّالة:

const caps = scanner.getCapabilities();
// { torch: true, zoom: { min: 1, max: 4, step: 0.1, current: 1, hardware: true } }
scanner.setZoom(2);

scanner.scanImage(file) — قراءة صورة

بتقرا صورة بدل الكاميرا: ملف مرفوع، Blob، عنصر <img>، أو <canvas>. بترجّع Promise فيه مصفوفة بكل الرموز اللي في الصورة (فاضية لو مفيش). بتشتغل والكاميرا شغّالة أو واقفة، وما بتناديش onScan.

<input type="file" id="photo" accept="image/*">
<script>
  document.getElementById("photo").onchange = async (e) => {
    const results = await scanner.scanImage(e.target.files[0]);
    for (const r of results) console.log(r.format, r.text);
  };
</script>

scanner.getUsage() — الاستهلاك

بترجّع استهلاك الاشتراك: المسحات المستهلكة، الحصة، المتبقي، وتاريخ الانتهاء. المسح بيتحسب مرة لكل رمز (شوف repeatDelayMs)، وكل رمز بترجّعه scanImage بيتحسب مسحًا.

scanner.getUsage();
// { scans: 12, limit: 5000, remaining: 4988, expiresAt: "2027-01-01", licensed: true }

scanner.destroy() — إزالة

بتقفل الكاميرا وبتشيل الماسح من الصفحة وبتحرر الذاكرة. الكائن ما ينفعش يتستخدم بعدها — اعمل واحدًا جديدًا بـ ScanRamz.create. ناديها لما المستخدم يسيب الصفحة في تطبيقات الصفحة الواحدة (React، Vue…).

scanner.state — الحالة

نص: "idle" (واقف)، "starting" (مستني إذن الكاميرا)، "scanning" (بيمسح)، "paused" (متوقف مؤقتًا)، "destroyed".

الذكاء الاصطناعي: بيعمل إيه، وإمتى توقفه

الماسح فيه جزئين: قارئ بيفك الرمز (كود دقيق مضمون)، وموديل ذكاء اصطناعي صغير بيبص على الصورة كلها ويقول "الرمز هنا". الموديل ما بيقراش — بيوجّه القارئ والزوم والفوكس للمكان الصح، فالرمز البعيد أو اللي وسط كلام وزحمة بيتلقط أسرع.

ai: true (الافتراضي)ai: false
الرمز القريب الواضحفوريفوري — مفيش فرق
الرمز البعيد أو الصغيربيتلقط ويتزوّم عليه بسرعةممكن ياخد وقتًا أطول
خلفية مزحومة (كلام، جداول)بيروح للرمز على طولممكن يجرّب مناطق غلط الأول
استهلاك البطاريةأعلى شويةأقل

النتيجة المقروءة واحدة في الحالتين. وقّفه لو أجهزتك ضعيفة جدًّا أو الرموز دايمًا قريبة وواضحة. كل شغل الموديل على جهاز المستخدم نفسه — الصورة ما بتطلعش من الجهاز عشان تتحلل.

شكل النتيجة

اللي بيوصل onScan، وكل عنصر في مصفوفة scanImage:

{
  format: "code128",              // نوع الرمز
  text: "2300228790",             // النص المقروء
  corners: [{ x: 412, y: 300 }, …],   // أركان الرمز في صورة الكاميرا (بالبكسل)
  timeMs: 3.4,                    // زمن القراءة
  ai: false,                      // true لو الموديل هو اللي دلّ على مكان الرمز
  details: { … }                  // تفاصيل فنية (الإصدار، تصحيح الأخطاء…)
}

الأنواع المدعومة

القيم اللي بتتبعت في formats وsetFormats (والقايمة كلها في ScanRamz.FORMATS):

القيمةالنوعبيتشاف فين
"qr"QR Codeروابط، دفع، تذاكر
"microqr"Micro QRقطع إلكترونية صغيرة
"datamatrix"Data Matrixأدوية، قطع صناعية، بطاقات
"aztec"Aztecتذاكر طيران وقطارات
"pdf417"PDF417رخص وبطاقات هوية، بوالص شحن
"code128"Code 128ملصقات الأصول، الشحن، المخازن
"code39"Code 39صناعة، أرقام تسلسلية
"ean13" / "ean8"EANمنتجات السوبر ماركت
"upca" / "upce"UPCمنتجات أمريكية
"itf"ITF (Interleaved 2 of 5)كراتين الشحن

الرموز معكوسة الألوان (أبيض على أسود، زي ملصقات الأجهزة) مدعومة تلقائيًّا لكل الأنواع.

لو عارف نوع رموزك، حدده: formats: ["code128"] بيخلي المسح أسرع وبيمنع قراءة رموز تانية بالغلط.

الأخطاء — onError({ code, message })

الكودالمعنىتعمل إيه
CAMERA_DENIEDالمستخدم رفض إذن الكاميرااشرح له ليه محتاج الكاميرا، وخليه يسمح من إعدادات الموقع في المتصفح
CAMERA_UNSUPPORTEDالمتصفح ما بيدعمش الكاميرا، أو الصفحة مش httpsاتأكد إن الصفحة https؛ أو اعرض رفع صورة بـ scanImage
CAMERA_NOT_FOUNDمفيش كاميرا في الجهازاعرض رفع صورة
CAMERA_FAILEDالكاميرا مشغولة أو فشلتاقفل أي تطبيق تاني بيستخدم الكاميرا وجرّب تاني
IMAGE_FAILEDالصورة اللي اتبعتت لـ scanImage ما اتفتحتشاتأكد إنها ملف صورة سليم
NO_CONTAINERالعنصر الحاوي مش موجوداتأكد من الـ selector وإن الكود بيشتغل بعد تحميل العنصر
DESTROYEDنداء بعد destroy()اعمل ماسحًا جديدًا
LICENSE_REQUIREDمفيش مفتاح ترخيص في licenseحط المفتاح اللي وصلك
LICENSE_INVALIDالمفتاح غير صحيح (أو اتغيّر)راجع المفتاح، أو اطلب المفتاح الجديد
LICENSE_DOMAINالموقع ده مش مسجّل في اشتراككاطلب إضافة الدومين للاشتراك (بما فيه دومين التجربة)
LICENSE_NOT_STARTEDتاريخ بدء الاشتراك لسه ما جاشاستنى تاريخ البدء
LICENSE_EXPIREDمدة الاشتراك انتهتجدّد الاشتراك
LICENSE_SUSPENDEDالاشتراك موقوفتواصل مع مزوّد الخدمة
QUOTA_EXCEEDEDحصة المسحات خلصتجدّد الباقة. onQuota بتنبّهك قبلها لما يفضل 10%
LICENSE_UNREACHABLEتعذّر الاتصال بسيرفر الترخيصاتأكد من الإنترنت؛ الماسح بيكمّل لحد 10 دقايق من غير اتصال

أمثلة كاملة

ماسح بأزرار تشغيل وإيقاف ومفتاح للذكاء الاصطناعي

<div id="scanner" style="width:100%;max-width:480px"></div>
<button id="start">تشغيل</button>
<button id="stop">إيقاف</button>
<label><input type="checkbox" id="ai" checked> الذكاء الاصطناعي</label>
<p id="out"></p>

<script src="https://aiscan.afaq-digital.com/sdk/scanramz.js"></script>
<script>
  const scanner = ScanRamz.create({
    license: "SR-XXXX-XXXX-XXXX",
    container: "#scanner",
    formats: ["code128", "qr"],
    beep: true,
    onScan: (r) => { document.getElementById("out").textContent = r.text; },
    onError: (e) => { document.getElementById("out").textContent = e.message; },
  });
  document.getElementById("start").onclick = () => scanner.start();
  document.getElementById("stop").onclick = () => scanner.stop();
  document.getElementById("ai").onchange = (e) => scanner.setAI(e.target.checked);
</script>

جرد: كل رمز يتبعت لسيرفرك مرة واحدة

const scanner = ScanRamz.create({
  license: "SR-XXXX-XXXX-XXXX",
  container: "#scanner",
  formats: ["code128"],
  onScan: async (r) => {
    await fetch("/api/inventory", {
      method: "POST",
      headers: { "Content-Type": "application/json" },
      body: JSON.stringify({ code: r.text }),
    });
  },
});
scanner.start();

مسح رمز واحد ثم الإغلاق

const scanner = ScanRamz.create({
  license: "SR-XXXX-XXXX-XXXX",
  container: "#scanner",
  onScan: (r) => {
    scanner.stop();                               // اقفل الكاميرا
    document.getElementById("code").value = r.text;   // حط القيمة في خانة الإدخال
  },
});

React / Vue / تطبيق صفحة واحدة

// عند ظهور المكوّن
const scanner = ScanRamz.create({ license: "…", container: element, onScan });
scanner.start();

// عند إزالة المكوّن — مهم: بيقفل الكاميرا
scanner.destroy();

الخصوصية: إيه اللي بيطلع من جهاز المستخدم؟

القراءة كلها بتحصل على جهاز المستخدم: الفيديو والنص المقروء ما بيتبعتوش لأي سيرفر. اللي بيتبعت لسيرفر الترخيص: مفتاح الترخيص، وعدّاد المسحات (كام مسحًا ومن أي نوع) — من غير قيم الرموز.

عيّنات تحسين الماسح (اختيارية ومقفولة افتراضيًّا): لو وافقت في اشتراكك على المساعدة في تحسين الماسح، بيتبعت كل فترة (تقريبًا مرة كل 10 مسحات) صورة واحدة مصغّرة جدًّا ورمادية (حوالي 384 بكسل — الكلام فيها ما بيتقراش) ومعاها مكان الرمز ونوعه. قيمة الرمز المقروءة ما بتتبعتش أبدًا. الصور دي بتتستخدم بس في تدريب الماسح على إنه يلاقي الرموز أسرع.

عاوزتعمل إيه
تقفل العيّنات في صفحة معيّنةcollect: false في ScanRamz.create
تقفلها للاشتراك كلهاطلب من مزوّد الخدمة إلغاء الموافقة
تمسح العيّنات اللي اتجمعت من اشتراككاطلب من مزوّد الخدمة حذفها

مشاكل شائعة

المشكلةالسبب والحل
الكاميرا ما بتفتحشالصفحة مش https، أو الإذن مرفوض قبل كده (يتفتح من إعدادات الموقع في المتصفح)، أو الصفحة جوه <iframe> من غير allow="camera"
شاشة سودا على iPhoneنادي start() من ضغطة زر، مش تلقائيًّا عند تحميل الصفحة
الرمز البعيد ما بيتقراشسيب ai وautoZoom شغّالين، وقرّب لحد ما الإطار الأصفر يبقى أخضر. الإضاءة الضعيفة: setTorch(true)
بيقرا رموزًا مش عاوزهاحدد الأنواع: formats: ["code128"]
onScan بتتنادى مرة واحدة ونفس الرمز ما بيتكررشده المقصود. عشان يتقرا تاني بعّد الكاميرا عنه 3 ثواني، أو صغّر repeatDelayMs
خطأ Content-Security-Policy في الكونسولضيف للسياسة: script-src https://aiscan.afaq-digital.com وworker-src blob:

المتصفحات: الإصدارات الحديثة من Chrome و Edge و Firefox و Safari، على الموبايل والكمبيوتر. الكشاف والزوم الحقيقي (زوم العدسة) متاحين في Chrome على أندرويد؛ في باقي المتصفحات الزوم رقمي.