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 | نص | — | مفتاح الترخيص |
ai | true / false | true | موديل الذكاء الاصطناعي لتحديد مكان الرمز. شوف قسم الذكاء الاصطناعي |
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" يضغط بس لو فيه أكتر من رمز. شوف اختيار الرمز بالضغط |
overlay | true / false | true | رسم إطار أخضر حوالين الرمز المقروء |
autoZoom | true / false | true | زوم تلقائي على الرمز البعيد |
vibrate | true / false | true | اهتزاز الموبايل عند كل مسح جديد |
beep | true / false | false | صفارة قصيرة عند كل مسح جديد |
collect | true / false | true | السماح بإرسال عيّنات صور مصغّرة لتحسين الماسح — بتتبعت بس لو اشتراكك موافق على الجمع. 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 على أندرويد؛ في باقي المتصفحات الزوم رقمي.