كيفية تحويل القيم من نطاق إلى آخر في JavaScript
كتب بواسطة إدريس الدويري
3 min read

تحويل القيم من نطاق (مثلاً: 0 إلى 800) إلى نطاق آخر (مثلاً: 10 إلى 100) هو ممارسة شائعة في معظم التطبيقات، خاصة في البرمجة الإبداعية وألعاب الفيديو.
إنها تقنية تحول قيمة من نطاق المصدر إلى نطاق الهدف مع الحفاظ على موضعها النسبي.
لرؤية شرح مرئي، حرّك شريط التمرير الأول ولاحظ كيف تُحوَّل القيمة إلى النطاق الثاني بينما تحافظ على نفس الموضع النسبي في نطاق المصدر:
source:
target:
صيغة التحويل بسيطة بمجرد أن تفهم ما تعنيه كل مكوّناتها:
الرياضيات وراء تحويل النطاق
دعونا نُفصِّل هذه الصيغة لفهم كيفية عملها:
percentage = (value - sourceStart) / (sourceEnd - sourceStart)
targetLength = (targetEnd - targetStart)
mapped = percentage * targetLength + targetStart
أولاً نحصل على النسبة المئوية للقيمة بالنسبة لنطاق المصدر، ثم نضرب هذه النسبة في طول النطاق الهدف، وأخيرًا نُضيف الإزاحة بالبداية الخاصة بالنطاق الهدف.
الاستخدامات الشائعة
- تحويل النطاقات إلى [0-1]
- توسيع أو تصغير النطاق
- تحويل قيم مدخلات أشرطة التمرير
- تحويل موضع مؤشر الفأرة
- تحويل الوحدات
تطبيق JavaScript
معظم مكتبات الرياضيات تأتي بوظيفة تحويل جاهزة، ولكن إن أردت كتابة دالتك الخاصة، فهكذا قد تبدو في JavaScript:
function map(value, sourceStart, sourceEnd, targetStart, targetEnd) {
return (value - sourceStart) / (sourceEnd - sourceStart) * (targetEnd - targetStart) + targetStart
}
console.log(map(0, -300, 300, 0, 100)) // 50
الحالات الحدّية والتحسينات
مع التطبيق الحالي، إذا كانت قيم البداية والنهاية لنطاق المصدر متساوية، سنواجه مشكلة قسمة على صفر. يمكننا تجنب ذلك من خلال التحقق من النطاق داخل الدالة وتنفيذ الحساب فقط إذا كانت القيم مختلفة، وإلا يمكننا إرجاع قيمة بديلة أو رمي خطأ.
if (sourceStart === sourceEnd) return targetStart // fallback
بالإضافة إلى ذلك، فإن القيم الخارجة عن نطاق المصدر ستنتج عنها قيم خارج نطاق الهدف. هذا قد يكون سلوكًا مرغوبًا، ولكن إذا لم يكن كذلك، يمكننا تقييد الناتج حتى لا يتجاوز حدود النطاق الهدف كما يلي:
return Math.max(targetStart, Math.min(mappedValue, targetEnd))
هذا يعمل فقط إذا كانت targetStart أقل من targetEnd، ولكن لا يمكننا دائمًا التأكد. ماذا لو كانت النطاقات معكوسة؟ مثل [80 - 25] بدلاً من [25 - 80]؟ هذا قد يُفسد الحساب، لذا دعونا نقيد النطاقات للعمل بشكل آمن مع أي ترتيب.
الدالة النهائية ستكون كالتالي:
function map(value, sourceStart, sourceEnd, targetStart, targetEnd, clamped = false) {
if (sourceStart === sourceEnd) return targetStart;
const sourceMin = Math.min(sourceStart, sourceEnd);
const sourceMax = Math.max(sourceStart, sourceEnd);
const targetMin = Math.min(targetStart, targetEnd);
const targetMax = Math.max(targetStart, targetEnd);
const mappedValue = (value - sourceMin) / (sourceMax - sourceMin) * (targetMax - targetMin) + targetMin;
if (!clamped) return mappedValue;
return Math.max(targetMin, Math.min(mappedValue, targetMax));
}
خاتمة
حاول دمج منطق التحويل هذا في مشروعك الإبداعي القادم وانظر ما يمكنك تحقيقه به.