PanResponder في React Native

من موسوعة حسوب

توحِّد PanResponder العديد من اللمسات ضمن إيماءةٍ واحدة، وتجعل الإيماءة أحاديّة اللّمسة متجاوبةً مع اللّمسات الإضافيّة، ويمكن أن تُستخدم للتعرُّف على الإيماءات الأساسيّة متعدّدة اللّمسات.

تحتوي PanResponder بشكل افتراضيٍّ على معالجٍ باسم InteractionManager لمنع أحداث JS طويلة الأمد من مقاطعة الإيماءات الجارية.

تُقدِّم panresponder غلافًا يمكن التنبؤ به لمعالجات المستجيب (responder handlers) المُقدَّم من نظام مستجيب الإيماءات (gesture responder system)، حيث تعطي لكل معالجٍ كائنًا جديدًا gestureState بجانب كائن الحدَث الأصيل:

onPanResponderMove: (event, gestureState) => {}

الحدَث الأصيل هو عبارة عن حدَث لمسةٍ مصطنَعةٍ (synthetic) على شكل كائن PressEvent.

يتضمَّن الكائن gestureState ما يلي:

  • stateID: مُعرِّف (ID) الكائن gestureState ويبقى ما دامت هنالك لمسة واحدة على الأقل على الشاشة.
  • moveX: الإحداثيات الأفقية الأخيرة على الشاشة للَّمسة المتحركة حديثًا.
  • moveY: الإحداثيات العموديّة الأخيرة على الشاشة للّمسة المتحركة حديثًا.
  • x0: الإحداثيات الأفقية على الشاشة لمكان منحْ (grant) المستجيب.
  • y0: الإحداثيات العموديّة على الشاشة لمكان منحْ المستجيب.
  • dx: المسافة الأفقية المجمَّعة للإيماءة من لحظة بدء اللمسة.
  • dy: المسافة العموديّة المجمَّعة للإيماءة من لحظة بدء اللمسة.
  • vx: السرعة الأفقية الحالية للإيماءة.
  • vy: السرعة العموديّة الحالية للإيماءة.
  • numberActiveTouches: عدد اللمسات الحالية على الشاشة.

نموذج الاستخدام

const ExampleComponent = () => {
 const panResponder = React.useRef(
  PanResponder.create({
   // طلب المكوِّن ليكون المستجيب
   onStartShouldSetPanResponder: (evt, gestureState) => true,
   onStartShouldSetPanResponderCapture: (evt, gestureState) =>
    true,
   onMoveShouldSetPanResponder: (evt, gestureState) => true,
   onMoveShouldSetPanResponderCapture: (evt, gestureState) =>
    true,

   onPanResponderGrant: (evt, gestureState) => {
    // بدأت الإيماءة. أظهر مراجعة مرئية ليعلم المستخدم ما يحدث
    //بالقيمة الصفرية gestureState.d{x,y} سيتم الآن تهيئة
    },
   onPanResponderMove: (evt, gestureState) => {
    // gestureState.move{X,Y}آخر مسافة محدَّثة للحركة
    //المسافة المجمَّعة للإيماءة منذ أن أصبح المكون مستجيبا 
    // gestureState.d{x,y}
    },
   onPanResponderTerminationRequest: (evt, gestureState) =>
    true,
   onPanResponderRelease: (evt, gestureState) => {
    // حررَ المستخدم جميع اللمسات عند ظهور المستجيب
    // مما يدل عمليًا على نجاح الإيماءة
    },
   onPanResponderTerminate: (evt, gestureState) => {
    // أصبح مكوِّن آخر هو المستجيب
    // لذا سيتم إنهاء هذه الإيماءة
    },
   onShouldBlockNativeResponder: (evt, gestureState) => {
    // عند منعَ المكون المكونات الأصيلة من true إرجاع قيمة 
    // true  والقيمة الافتراضية هي، JS أن تصبح مستجيب
    //  فقط Android مدعومة حاليا في نظام 
    return true;
    }
   })
  ).current;

 return <View {...panResponder.panHandlers} />;
};

مثال

تُستخدم panresponder مع الواجهة البرمجية (API)‏ Animated للمساعدة في إنشاء إيماءاتٍ معقّدةٍ في واجهة المستخدم (UI)، يحتوي المثال التالي على المكون المتحرّك View والذي يمكن سحبه بحريّةٍ عبر الشاشة:

  • مثال لمكون الدالة (Function Component)
const ExampleComponent = () => {
 const panResponder = React.useRef(
  PanResponder.create({
   // طلب المكوِّن ليكون المستجيب
   onStartShouldSetPanResponder: (evt, gestureState) => true,
   onStartShouldSetPanResponderCapture: (evt, gestureState) =>
    true,
   onMoveShouldSetPanResponder: (evt, gestureState) => true,
   onMoveShouldSetPanResponderCapture: (evt, gestureState) =>
    true,

   onPanResponderGrant: (evt, gestureState) => {
    // بدأت الإيماءة. أظهر مراجعة مرئية ليعلم المستخدم ما يحدث
    //بالقيمة الصفرية gestureState.d{x,y} سيتم الآن تهيئة
    },
   onPanResponderMove: (evt, gestureState) => {
    // gestureState.move{X,Y}آخر مسافة محدَّثة للحركة
    //المسافة المجمَّعة للإيماءة منذ أن أصبح المكون مستجيبا 
    // gestureState.d{x,y}
    },
   onPanResponderTerminationRequest: (evt, gestureState) =>
    true,
   onPanResponderRelease: (evt, gestureState) => {
    // حررَ المستخدم جميع اللمسات عند ظهور المستجيب
    // مما يدل عمليًا على نجاح الإيماءة
    },
   onPanResponderTerminate: (evt, gestureState) => {
    // أصبح مكوِّن آخر هو المستجيب
    // لذا سيتم إنهاء هذه الإيماءة
    },
   onShouldBlockNativeResponder: (evt, gestureState) => {
    // عند منعَ المكون المكونات الأصيلة من true إرجاع قيمة 
    // true  والقيمة الافتراضية هي، JS أن تصبح مستجيب
    //  فقط Android مدعومة حاليا في نظام 
    return true;
    }
   })
  ).current;

 return <View {...panResponder.panHandlers} />;
};
  • مثال لمكون الصنف (Class Component)
const ExampleComponent = () => {
 const panResponder = React.useRef(
  PanResponder.create({
   // طلب المكوِّن ليكون المستجيب
   onStartShouldSetPanResponder: (evt, gestureState) => true,
   onStartShouldSetPanResponderCapture: (evt, gestureState) =>
    true,
   onMoveShouldSetPanResponder: (evt, gestureState) => true,
   onMoveShouldSetPanResponderCapture: (evt, gestureState) =>
    true,

   onPanResponderGrant: (evt, gestureState) => {
    // بدأت الإيماءة. أظهر مراجعة مرئية ليعلم المستخدم ما يحدث
    //بالقيمة الصفرية gestureState.d{x,y} سيتم الآن تهيئة
    },
   onPanResponderMove: (evt, gestureState) => {
    // gestureState.move{X,Y}آخر مسافة محدَّثة للحركة
    //المسافة المجمَّعة للإيماءة منذ أن أصبح المكون مستجيبا 
    // gestureState.d{x,y}
    },
   onPanResponderTerminationRequest: (evt, gestureState) =>
    true,
   onPanResponderRelease: (evt, gestureState) => {
    // حررَ المستخدم جميع اللمسات عند ظهور المستجيب
    // مما يدل عمليًا على نجاح الإيماءة
    },
   onPanResponderTerminate: (evt, gestureState) => {
    // أصبح مكوِّن آخر هو المستجيب
    // لذا سيتم إنهاء هذه الإيماءة
    },
   onShouldBlockNativeResponder: (evt, gestureState) => {
    // عند منعَ المكون المكونات الأصيلة من true إرجاع قيمة 
    // true  والقيمة الافتراضية هي، JS أن تصبح مستجيب
    //  فقط Android مدعومة حاليا في نظام 
    return true;
    }
   })
  ).current;

 return <View {...panResponder.panHandlers} />;
};

جرِّب مثال PanResponder في RNTester.

التوابع

create()‎

static create(config)

المعاملات

الاسم النوع مطلوب الوصف
config كائن (object) نعم الشرح في الأسفل

يُقدِّم الكائن config إصدارات محسَّنة لجميع ردود نداء المستجيب (responder callbacks)، والذي بدوره لا يُقدِّم PressEvent فقط بل حالة الإيماءات PanResponder أيضًا، وذلك باستبدال الكلمة Responder بالكلمة PanResponder في كل استجابة نموذجيّةٍ onResponder*‎. كمثال على ذلك سيبدو الكائن config كما يلي:

onMoveShouldSetPanResponder: (e, gestureState) => {...}
onMoveShouldSetPanResponderCapture: (e, gestureState) => {...}
onStartShouldSetPanResponder: (e, gestureState) => {...}
onStartShouldSetPanResponderCapture: (e, gestureState) => {...}
onPanResponderReject: (e, gestureState) => {...}
onPanResponderGrant: (e, gestureState) => {...}
onPanResponderStart: (e, gestureState) => {...}
onPanResponderEnd: (e, gestureState) => {...}
onPanResponderRelease: (e, gestureState) => {...}
onPanResponderMove: (e, gestureState) => {...}
onPanResponderTerminate: (e, gestureState) => {...}
onPanResponderTerminationRequest: (e, gestureState) => {...}
onShouldBlockNativeResponder: (e, gestureState) => {...}

وبالنسبة للأحداث التي لها مكافئات في طور الالتقاط (أي طور إنتشار الحدث من العنصر الجذر وحتى العنصر الهدف capture phase)، نُحدِّث gestureState مرةً واحدةً فقط أثناء طور الالتقاط (capture phase) مباشرةً، ويمكن استخدامه في طور الانتشار (bubble phase، أو طور الغليان أو الفوران حيث ينتشر الحدث من العنصر الهدف وحتى الأعلى باتجاه العنصر الجذر) أيضًا.

يجب الانتباه عند التعامل مع الاستجابات onStartShould*‎ لأنها تعكس gestureState لأحداث البدء أو الانتهاء لطوري العقدة (الفقاعة أو الالتقاط). فعندما تصبح العقدة هي المستجيب يمكن الوثوق بأن الإيماءة تعالج كلّ حدث بدءٍ أو انتهاءٍ، ويحدَّث gestureState تبعًا لذلك، قد لا يكون numberActiveTouches دقيقًا تمامًا إلا إذا كنت أنت المستجيب.

مصادر