Reactive Native में स्क्रॉल दृश्य की वर्तमान स्क्रॉल स्थिति प्राप्त करें


115

क्या वर्तमान स्क्रॉल स्थिति, या <ScrollView>प्रतिक्रिया मूल में किसी घटक का वर्तमान पृष्ठ प्राप्त करना संभव है ?

तो कुछ इस तरह:

<ScrollView
  horizontal={true}
  pagingEnabled={true}
  onScrollAnimationEnd={() => { 
      // get this scrollview's current page or x/y scroll position
  }}>
  this.state.data.map(function(e, i) { 
      <ImageCt key={i}></ImageCt> 
  })
</ScrollView>

प्रासंगिक: यह जानकारी प्राप्त करने का एक सुविधाजनक तरीका जोड़ने का सुझाव देते हुए एक GitHub मुद्दा
मार्क एमी

जवाबों:


207

प्रयत्न।

<ScrollView onScroll={this.handleScroll} />

और तब:

handleScroll: function(event: Object) {
 console.log(event.nativeEvent.contentOffset.y);
},

5
स्क्रॉल वैल्यू में बदलाव होने पर यह हमेशा फायर नहीं करेगा। यदि उदाहरण के लिए स्क्रॉलव्यू का आकार बदल दिया जाता है, तो यह अब पूरी तरह से स्क्रीन पर पूरी तरह से प्रदर्शित करने में सक्षम हो जाता है, क्योंकि आपको हमेशा ऐसा नहीं लगता कि आपको बताए जाने वाली ऑनस्क्रीन घटना हो।
थॉमस पार्स्लो

19
या event.nativeEvent.contentOffset.xअगर आपको एक क्षैतिज स्क्रॉल दृश्य मिल गया है;) धन्यवाद!
तीये की

2
यह भी हम Android में उपयोग कर सकते हैं।
जनक पुष्पकुमारा

4
निराशा की बात है, जब तक आप scrollEventThrottle16 या उससे कम सेट नहीं करते (हर एक फ्रेम के लिए एक घटना पाने के लिए), इसकी कोई गारंटी नहीं है कि यह अंतिम फ्रेम पर ऑफसेट की रिपोर्ट करेगा - इसका मतलब है कि यह सुनिश्चित करने के लिए कि स्क्रॉलिंग समाप्त होने के बाद आपके पास सही ऑफसेट है, आपको scrollEventThrottle={16}उस के प्रदर्शन प्रभाव को निर्धारित करने और स्वीकार करने की आवश्यकता है।
मार्क एमी

@ ब्रैडॉयलर: क्या अधिकतम मूल्य जानना संभव है event.nativeEvent.contentOffset.y?
इसहाक

55

डिस्क्लेमर: मुख्यतः रिएक्ट नेटिव 0.50 में मेरे अपने प्रयोग का परिणाम है। ScrollViewप्रलेखन वर्तमान में नीचे कवर जानकारी का एक बहुत याद आ रही है; उदाहरण के onScrollEndDragलिए पूरी तरह से undocumented है। चूंकि यहां सब कुछ अनिर्धारित व्यवहार पर निर्भर करता है, मैं दुर्भाग्य से कोई वादा नहीं कर सकता कि यह जानकारी एक साल या अब से एक महीने भी सही रहेगी।

इसके अलावा, नीचे दी गई हर चीज एक विशुद्ध रूप से वर्टिकल स्क्रॉलव्यू मानती है, जिसकी y ऑफसेट हम में रुचि रखते हैं; यदि जरूरत हो तो एक्स ऑफ़सेट्स में अनुवाद करना , उम्मीद है कि पाठक के लिए एक आसान व्यायाम होगा।


एक ScrollViewले पर विभिन्न घटना संचालकों eventऔर आप के माध्यम से वर्तमान स्क्रॉल स्थिति प्राप्त करते हैं event.nativeEvent.contentOffset.y। इनमें से कुछ हैंडलर Android और iOS के बीच थोड़ा अलग व्यवहार करते हैं, जैसा कि नीचे विस्तृत है।

onScroll

Android पर

जब उपयोगकर्ता स्क्रॉल कर रहा होता है, तब हर फ्रेम पर आग लग जाती है, जबकि स्क्रॉल दृश्य को उपयोगकर्ता द्वारा जारी किए जाने के बाद स्क्रॉल दृश्य ग्लाइड होता है, जब स्क्रॉल दृश्य आराम करने के लिए आता है, और जब भी स्क्रॉल का ऑफसेट इसके फ्रेम के परिणामस्वरूप बदलता है। बदलते (उदाहरण के लिए परिदृश्य से चित्र के रोटेशन के कारण)।

IOS पर

जब उपयोगकर्ता घसीट रहा हो या जब स्क्रॉल दृश्य ग्लाइडिंग कर रहा हो, तब कुछ आवृत्ति scrollEventThrottleपर और जब फ्रेम प्रति बार एक बार निर्धारित किया जाता है scrollEventThrottle={16}यदि उपयोगकर्ता स्क्रॉल करने के लिए स्क्रॉल दृश्य जारी करता है, जबकि इसे विभाजित करने के लिए पर्याप्त गति है, तो onScrollग्लाइडिंग के बाद आराम करने के लिए हैंडलर भी आग लगाएगा। हालांकि, उपयोगकर्ता खींच लेता है और उसके बाद पुस्तक दृश्य विज्ञप्ति जबकि यह स्थिर है, onScrollहै आग में अंतिम स्थान के लिए जब तक इसकी गारंटी scrollEventThrottleऐसी निर्धारित किया गया है कि onScrollआग स्क्रॉल के हर फ्रेम।

सेटिंग के लिए एक प्रदर्शन लागत है scrollEventThrottle={16}जिसे बड़ी संख्या में सेट करके कम किया जा सकता है। हालांकि, इसका मतलब है कि onScrollहर फ्रेम में आग नहीं लगेगी।

onMomentumScrollEnd

जब ग्लाइडिंग के बाद स्क्रॉल दृश्य रुक जाता है तो आग लग जाती है। अगर उपयोगकर्ता स्क्रॉल दृश्य को स्थिर करता है तो यह बिल्कुल भी आग नहीं लगाता है, क्योंकि यह स्थिर नहीं है।

onScrollEndDrag

जब उपयोगकर्ता स्क्रॉल दृश्य को खींचना बंद कर देता है, तो यह ध्यान दिए बिना कि स्क्रॉल दृश्य स्थिर रहता है या सरकना शुरू होता है।


व्यवहार में इन अंतरों को देखते हुए, ऑफसेट का ट्रैक रखने का सबसे अच्छा तरीका आपके सटीक परिस्थितियों पर निर्भर करता है। सबसे जटिल मामले में (आपको एंड्रॉइड और आईओएस का समर्थन करने की आवश्यकता है, जिसमें ScrollViewरोटेशन के कारण फ्रेम में परिवर्तन को संभालना शामिल है , और आप एंड्रॉइड पर scrollEventThrottle16 से सेटिंग पर प्रदर्शन जुर्माना स्वीकार नहीं करना चाहते हैं ), और आपको संभालने की आवश्यकता है स्क्रॉल दृश्य में भी सामग्री में परिवर्तन , तो यह एक सही लानत गड़बड़ है।

सबसे सरल मामला है, अगर आपको केवल एंड्रॉइड को संभालने की आवश्यकता है; बस उपयोग करें onScroll:

<ScrollView
  onScroll={event => { 
    this.yOffset = event.nativeEvent.contentOffset.y
  }}
>

अतिरिक्त रूप से iOS का समर्थन करने के लिए, यदि आप onScrollहैंडलर को हर फ्रेम में फायर करते हैं और उस के प्रदर्शन निहितार्थ को स्वीकार करते हैं, और यदि आपको फ़्रेम परिवर्तन को संभालने की आवश्यकता नहीं है, तो यह केवल थोड़ा अधिक जटिल है:

<ScrollView
  onScroll={event => { 
    this.yOffset = event.nativeEvent.contentOffset.y
  }}
  scrollEventThrottle={16}
>

IOS पर प्रदर्शन ओवरहेड को कम करने के लिए अभी भी गारंटी देते हुए कि हम किसी भी स्थिति को रिकॉर्ड करते हैं, जिस पर स्क्रॉल दृश्य निपटता है, हम बढ़ा सकते हैं scrollEventThrottleऔर इसके अलावा एक onScrollEndDragहैंडलर प्रदान कर सकते हैं :

<ScrollView
  onScroll={event => { 
    this.yOffset = event.nativeEvent.contentOffset.y
  }}
  onScrollEndDrag={event => { 
    this.yOffset = event.nativeEvent.contentOffset.y
  }}
  scrollEventThrottle={160}
>

लेकिन अगर हम फ्रेम में बदलाव करना चाहते हैं (उदाहरण के लिए, क्योंकि हम डिवाइस को घुमाने की अनुमति देते हैं, स्क्रॉल दृश्य के फ्रेम के लिए उपलब्ध ऊंचाई बदलकर) और / या सामग्री में परिवर्तन होता है, तो हमें अतिरिक्त रूप से दोनों को लागू करना होगा onContentSizeChangeऔर दोनों onLayoutकी ऊंचाई का ट्रैक रखना होगा। स्क्रॉल दृश्य के फ्रेम और उसकी सामग्री, और इस तरह लगातार अधिकतम संभव ऑफसेट और अनुमान लगाता है जब ऑफसेट स्वचालित रूप से एक फ्रेम या सामग्री आकार परिवर्तन के कारण कम हो गया है:

<ScrollView
  onLayout={event => {
    this.frameHeight = event.nativeEvent.layout.height;
    const maxOffset = this.contentHeight - this.frameHeight;
    if (maxOffset < this.yOffset) {
      this.yOffset = maxOffset;
    }
  }}
  onContentSizeChange={(contentWidth, contentHeight) => {
    this.contentHeight = contentHeight;
    const maxOffset = this.contentHeight - this.frameHeight;
    if (maxOffset < this.yOffset) {
      this.yOffset = maxOffset;
    }
  }}
  onScroll={event => { 
    this.yOffset = event.nativeEvent.contentOffset.y;
  }}
  onScrollEndDrag={event => { 
    this.yOffset = event.nativeEvent.contentOffset.y;
  }}
  scrollEventThrottle={160}
>

हाँ, यह बहुत भयानक है। मैं भी 100% निश्चित नहीं हूं कि यह हमेशा उन मामलों में सही काम करेगा जहां आप एक साथ स्क्रॉल दृश्य के फ्रेम और सामग्री दोनों का आकार बदलते हैं। लेकिन यह सबसे अच्छा है जिसके साथ मैं आ सकता हूं, और जब तक यह सुविधा ढांचे के भीतर नहीं जुड़ जाती , मुझे लगता है कि यह सबसे अच्छा है जो कोई भी कर सकता है।


19

ब्रैड ओएलेर का जवाब सही है। लेकिन आप केवल एक घटना प्राप्त करेंगे। यदि आपको स्क्रॉल स्थिति के निरंतर अपडेट प्राप्त करने की आवश्यकता है, तो आपको scrollEventThrottleप्रोप सेट करना चाहिए , जैसे:

<ScrollView onScroll={this.handleScroll} scrollEventThrottle={16} >
  <Text>
    Be like water my friend 
  </Text>
</ScrollView>

और ईवेंट हैंडलर:

handleScroll: function(event: Object) {
  console.log(event.nativeEvent.contentOffset.y);
},

ध्यान रखें कि आप प्रदर्शन के मुद्दों में भाग सकते हैं। तदनुसार गला घोंटना समायोजित करें। 16 आपको सबसे अधिक अपडेट देता है। 0 केवल एक।


12
यह गलत है। एक के लिए, ScrollEvenThrottle केवल iOS के लिए काम करता है न कि Android के लिए। दूसरा, यह केवल यह नियंत्रित करता है कि आप कितनी बार onScroll कॉल प्राप्त करते हैं। डिफ़ॉल्ट एक बार प्रति फ्रेम है, एक घटना नहीं। 1 आपको अधिक अपडेट देता है और 16 आपको कम देता है। 0 डिफ़ॉल्ट है। यह जवाब पूरी तरह से गलत है। facebook.github.io/react-native/docs/…
Teodors

1
मैंने इसका जवाब दिया इससे पहले कि एंड्रॉइड रिएक्ट नेटिव द्वारा समर्थित था, इसलिए हां, इसका जवाब केवल आईओएस पर लागू होता है। डिफ़ॉल्ट मान शून्य है, जिसके परिणामस्वरूप स्क्रॉल घटना को केवल एक बार दृश्य स्क्रॉल किए जाने के बाद भेजा जाता है।
कटेमासिन

1
कार्य के लिए es6 संकेतन क्या होगा (घटना: वस्तु) {}?
cbartondock

1
बस function(event) { }
कटेमाचिन

1
@Teodors जबकि यह उत्तर एंड्रॉइड को कवर नहीं करता है, आपकी बाकी आलोचना पूरी तरह से भ्रमित है। scrollEventThrottle={16}करता नहीं आप "कम" अपडेट देने के; 1-16 की सीमा में कोई भी संख्या आपको अपडेट की अधिकतम संभव दर देती है। 0 का डिफ़ॉल्ट आपको (iOS पर) एक घटना देता है जब उपयोगकर्ता स्क्रॉल करना शुरू करता है और फिर बाद में कोई अपडेट नहीं किया जाता है (जो कि बहुत बेकार है और संभवतः बग); डिफ़ॉल्ट "प्रति फ्रेम एक बार" नहीं है । आपकी टिप्पणी में उन दो त्रुटियों के अलावा, आपके अन्य दावे कुछ भी विरोधाभास नहीं करते हैं जो उत्तर कहता है (हालांकि आपको लगता है कि वे ऐसा करते हैं)।
मार्क अमेरी

7

यदि आप केवल वर्तमान स्थिति प्राप्त करना चाहते हैं (जैसे कि जब कोई बटन दबाया जाता है) तो उपयोगकर्ता स्क्रॉल करने के बजाय इसे ट्रैक करता है, तो एक onScrollश्रोता को आमंत्रित करने से प्रदर्शन समस्याओं का कारण बनने वाला है। वर्तमान में वर्तमान स्क्रॉल स्थिति प्राप्त करने के लिए सबसे अच्छा तरीका प्रतिक्रिया-मूल-इनवोक पैकेज का उपयोग करना है। इस सटीक बात के लिए एक उदाहरण है, लेकिन पैकेज कई अन्य चीजें करता है।

इसके बारे में यहां पढ़ें। https://medium.com/@talkol/invoke-any-native-api-directly-from-pure-javascript-in-react-native-1fb6afcdf57d#.68ls1sopd


2
क्या आप जानते हैं कि ऑफ़सेट का अनुरोध करने के लिए एक क्लीनर तरीका (थोड़ा कम हैसी) अब मौजूद है, या क्या यह अभी भी ऐसा करने का सबसे अच्छा तरीका है जिसके बारे में आप जानते हैं? (मुझे आश्चर्य है कि यह जवाब क्यों नहीं दिया गया क्योंकि यह केवल एक ही है जो समझ में आता है)
cglacet

1
पैकेज अब नहीं है, क्या यह कहीं चला गया?
गितुब

6

स्क्रॉल समाप्त होने के बाद x / y प्राप्त करने के लिए क्योंकि मूल प्रश्न अनुरोध कर रहा था, सबसे आसान तरीका शायद यह है:

<ScrollView
   horizontal={true}
   pagingEnabled={true}
   onMomentumScrollEnd={(event) => { 
      // scroll animation ended
      console.log(e.nativeEvent.contentOffset.x);
      console.log(e.nativeEvent.contentOffset.y);
   }}>
   ...content
</ScrollView>

-1; onMomentumScrollEndकेवल तभी ट्रिगर होता है यदि स्क्रॉल दृश्य में कुछ गति होती है जब उपयोगकर्ता इसे जाने देता है। यदि वे स्क्रॉल दृश्य को आगे नहीं बढ़ाते हैं, तो आप कभी भी गति प्राप्त नहीं करेंगे।
मार्क ऐमी

1
मैंने onMomentumScrollEnd का परीक्षण किया और इसे ट्रिगर न करना मेरे लिए असंभव था, मैंने अलग-अलग तरीकों से स्क्रॉल करने की कोशिश की, मैंने स्क्रॉल को रिलीज़ करने की कोशिश की जब स्क्रॉल अंतिम स्थिति में था और यह भी ट्रिगर हो गया था। इसलिए यह उत्तर सही है जहाँ तक मैं इसका परीक्षण कर सकता हूं।
fermmm

6

उपरोक्त उत्तर बताते हैं कि विभिन्न एपीआई onScroll, onMomentumScrollEndआदि का उपयोग करके स्थिति कैसे प्राप्त करें ; यदि आप पृष्ठ सूचकांक जानना चाहते हैं, तो आप ऑफसेट मूल्य का उपयोग करके इसकी गणना कर सकते हैं।

 <ScrollView 
    pagingEnabled={true}
    onMomentumScrollEnd={this._onMomentumScrollEnd}>
    {pages}
 </ScrollView> 

  _onMomentumScrollEnd = ({ nativeEvent }: any) => {
   // the current offset, {x: number, y: number} 
   const position = nativeEvent.contentOffset; 
   // page index 
   const index = Math.round(nativeEvent.contentOffset.x / PAGE_WIDTH);

   if (index !== this.state.currentIndex) {
     // onPageDidChanged
   }
 };

यहां छवि विवरण दर्ज करें

IOS में, स्क्रॉल दृश्य और दृश्य क्षेत्र के बीच संबंध निम्नानुसार है: चित्र https://www.objc.io/issues/3-views/scroll-view/ से आया है

रेफरी: https://www.objc.io/issues/3-views/scroll-view/


यदि आप वर्तमान आइटम का "इंडेक्स" प्राप्त करना चाहते हैं, तो यह सही उत्तर है। आप event.nativeEvent.contentOffset.x / deviceWidth लेते हैं। आपकी सहायताके लिए धन्यवाद!
सारा ऐस कैल्डरॉन

1

यह कैसे वर्तमान स्क्रॉल स्थिति को देखने से लाइव समाप्त हो गया है। मैं जो कुछ भी कर रहा हूं, वह सभी स्क्रॉल ईवेंट श्रोता और स्क्रॉलइवेंटट्रॉटल का उपयोग कर रहा है। फिर मैं इसे एक समारोह के रूप में पारित कर रहा हूं, जो मैंने बनाया है और अपने प्रॉपर को अपडेट करने के लिए स्क्रॉल फ़ंक्शन को संभालने के लिए।

 export default class Anim extends React.Component {
          constructor(props) {
             super(props);
             this.state = {
               yPos: 0,
             };
           }

        handleScroll(event){
            this.setState({
              yPos : event.nativeEvent.contentOffset.y,
            })
        }

        render() {
            return (
          <ScrollView onScroll={this.handleScroll.bind(this)} scrollEventThrottle={16} />
    )
    }
    }

क्या प्रदर्शन पर विचार करते हुए onScroll पर राज्य को अपडेट करना अच्छा है?
ऋषि ’

1

OnScroll का उपयोग अनंत लूप में प्रवेश करता है। इसके बजाय onMomentumScrollEnd या onScrollEndDrag का उपयोग किया जा सकता है


0

पृष्ठ के लिए, मैं एक उच्च क्रम घटक पर काम कर रहा हूं जो मूल रूप से उपरोक्त विधियों का उपयोग करता है। जब आप प्रारंभिक लेआउट और सामग्री में परिवर्तन जैसी बारीकियों में उतरते हैं, तो वास्तव में बस थोड़ा समय लगता है। मैं इसे 'सही ढंग से' करने का दावा नहीं करूंगा, लेकिन कुछ अर्थों में मैं घटक का उपयोग करने के लिए सही उत्तर पर विचार करूंगा जो यह सावधानीपूर्वक और लगातार करता है।

देखें: प्रतिक्रिया-मूल-पृष्ठांकित-स्क्रॉल-दृश्य । प्रतिक्रिया पसंद है, भले ही यह है कि मैंने यह सब गलत किया है!


1
यह कमाल का है। इसे बनाने के लिए धन्यवाद। मुझे यह बहुत उपयोगी लगा।
मार्टी

बहुत प्रसन्न! वास्तव में प्रतिक्रिया की सराहना!

1
एक क्षमादान -1, चूंकि परियोजना खुले तौर पर स्वीकार करती है कि यह अस्वीकार्य है और घटक में "एक जोड़ी बदसूरत कीड़े" हैं
मार्क ऐमी

प्रतिक्रिया के लिए धन्यवाद @MarkAmery :) खुले स्रोत के लिए समय निकालना आसान नहीं है।

-3

मेरा मानना ​​है contentOffsetकि आपको शीर्ष-बाएँ स्क्रॉल ऑफ़सेट वाली एक वस्तु मिलेगी:

http://facebook.github.io/react-native/docs/scrollview.html#contentoffset


4
क्या यह एक सेटर नहीं है, एक भगदड़ नहीं है?
एरोन वांग

1
-1 यह बकवास है; जेएसएक्स प्रॉप के लिए आपके द्वारा जो डॉक्यूमेंटेशन जुड़ा हुआ है, वह जावास्क्रिप्ट प्रॉपर्टी नहीं है, और इसका उपयोग केवल एक ऑफसेट को सेट करने के लिए किया जा सकता है , एक को प्राप्त करने के लिए नहीं।
मार्क एमी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.