ios 在 React Native 中获取 ScrollView 的当前滚动位置
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/29503252/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Get current scroll position of ScrollView in React Native
提问by Sang
Is it possible to get the current scroll position, or the current page of a <ScrollView>
component in React Native?
是否可以<ScrollView>
在 React Native 中获取当前滚动位置或组件的当前页面?
So something like:
所以像:
<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>
回答by brad oyler
Try.
尝试。
<ScrollView onScroll={this.handleScroll} />
And then:
进而:
handleScroll: function(event: Object) {
console.log(event.nativeEvent.contentOffset.y);
},
回答by Mark Amery
Disclaimer: what follows is primarily the result of my own experimentation in React Native 0.50. The ScrollView
documentationis currently missing a lot of the information covered below; for instance onScrollEndDrag
is completely undocumented. Since everything here relies upon undocumented behaviour, I can unfortunately make no promises that this information will remain correct a year or even a month from now.
免责声明:以下内容主要是我自己在 React Native 0.50 中的实验结果。该ScrollView
文档目前缺少以下涵盖的许多信息;例如onScrollEndDrag
完全没有记录。由于这里的一切都依赖于未记录的行为,因此我不能保证这些信息在一年甚至一个月后仍然正确。
Also, everything below assumes a purely vertical scrollview whose yoffset we are interested in; translating to xoffsets, if needed, is hopefully an easy exercise for the reader.
此外,下面的所有内容都假设我们感兴趣的y偏移量是纯垂直滚动视图;如果需要,转换为x偏移量对读者来说是一个简单的练习。
Various event handlers on a ScrollView
take an event
and let you get the current scroll position via event.nativeEvent.contentOffset.y
. Some of these handlers have slightly different behaviour between Android and iOS, as detailed below.
ScrollView
take an上的各种事件处理程序event
,让您通过event.nativeEvent.contentOffset.y
. 其中一些处理程序在 Android 和 iOS 之间的行为略有不同,详情如下。
onScroll
onScroll
On Android
在安卓上
Fires every frame while the user is scrolling, on every frame while the scroll view is gliding after the user releases it, on the final frame when the scroll view comes to rest, and also whenever the scroll view's offset changes as a result of its frame changing (e.g. due to rotation from landscape to portrait).
在用户滚动时触发每一帧,在用户释放滚动视图后滑动时在每一帧触发,在滚动视图静止时在最后一帧触发,以及当滚动视图的偏移量因其帧而改变时触发变化(例如由于从横向旋转到纵向)。
On iOS
在 iOS 上
Fires while the user is dragging or while the scroll view is gliding, at some frequency determined by scrollEventThrottle
and at most once per frame when scrollEventThrottle={16}
. Ifthe user releases the scroll view while it has enough momentum to glide, the onScroll
handler will also fire when it comes to rest after gliding. However, if the user drags and then releases the scroll view while it is stationary, onScroll
is notguaranteed to fire for the final position unless scrollEventThrottle
has been set such that onScroll
fires every frame of scrolling.
在用户拖动或滚动视图滑动时触发,频率由 确定,scrollEventThrottle
当 时每帧最多一次scrollEventThrottle={16}
。如果用户在滚动视图有足够的滑行动量时释放它,则onScroll
处理程序也会在滑行后静止时触发。但是,如果用户在静止时拖动然后释放滚动视图,onScroll
则不能保证在最终位置触发,除非scrollEventThrottle
已设置为onScroll
触发每一帧滚动。
There is a performance cost to setting scrollEventThrottle={16}
that can be reduced by setting it to a larger number. However, this means that onScroll
will not fire every frame.
设置的性能成本scrollEventThrottle={16}
可以通过将其设置为更大的数字来降低。但是,这意味着onScroll
不会触发每一帧。
onMomentumScrollEnd
onMomentumScrollEnd
Fires when the scroll view comes to a stop after gliding. Does not fire at all if the user releases the scroll view while it is stationary such that it does not glide.
当滚动视图在滑动后停止时触发。如果用户在滚动视图静止时释放滚动视图使其不会滑动,则根本不会触发。
onScrollEndDrag
onScrollEndDrag
Fires when the user stops dragging the scroll view - regardless of whether the scroll view is left stationary or begins to glide.
当用户停止拖动滚动视图时触发 - 无论滚动视图是保持静止还是开始滑动。
Given these differences in behaviour, the best way to keep track of the offset depends upon your precise circumstances. In the most complicated case (you need to support Android and iOS, including handling changes in the ScrollView
's frame due to rotation, and you don't want to accept the performance penalty on Android from setting scrollEventThrottle
to 16), and you need to handle changes to the contentin the scroll view too, then it's a right damn mess.
鉴于这些行为差异,跟踪偏移的最佳方法取决于您的具体情况。在最复杂的情况下(你需要支持Android和iOS,包括处理ScrollView
由于旋转导致的frame的变化,你不想接受Android上从设置scrollEventThrottle
到16的性能损失),你需要处理滚动视图中的内容也发生了变化,那就是一团糟。
The simplest case is if you only need to handle Android; just use onScroll
:
最简单的情况是,如果您只需要处理 Android;只需使用onScroll
:
<ScrollView
onScroll={event => {
this.yOffset = event.nativeEvent.contentOffset.y
}}
>
To additionally support iOS, ifyou're happy to fire the onScroll
handler every frame and accept the performance implications of that, and ifyou don't need to handle frame changes, then it's only a little bit more complicated:
为了额外支持 iOS,如果您乐于在onScroll
每一帧触发处理程序并接受其性能影响,并且如果您不需要处理帧更改,那么它只是稍微复杂一点:
<ScrollView
onScroll={event => {
this.yOffset = event.nativeEvent.contentOffset.y
}}
scrollEventThrottle={16}
>
To reduce the performance overhead on iOS while still guaranteeing that we record any position that the scroll view settles on, we can increase scrollEventThrottle
and additionally provide an onScrollEndDrag
handler:
为了减少 iOS 上的性能开销,同时仍然保证我们记录滚动视图所定位的任何位置,我们可以增加scrollEventThrottle
并额外提供一个onScrollEndDrag
处理程序:
<ScrollView
onScroll={event => {
this.yOffset = event.nativeEvent.contentOffset.y
}}
onScrollEndDrag={event => {
this.yOffset = event.nativeEvent.contentOffset.y
}}
scrollEventThrottle={160}
>
But if we want to handle frame changes (e.g. because we allow the device to be rotated, changing the available height for the scroll view's frame) and/or content changes, then we must additionally implement both onContentSizeChange
and onLayout
to keep track of the height of both the scroll view's frame and its contents, and thereby continually calculate the maximumpossible offset and infer when the offset has been automatically reduced due to a frame or content size change:
但是如果我们想要处理框架变化(例如,因为我们允许设备旋转,改变滚动视图框架的可用高度)和/或内容变化,那么我们必须另外实现两者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}
>
Yeah, it's pretty horrifying. I'm also not 100% certain that it'll always work right in cases where you simultaneouslychange the size of both the frame and content of the scroll view. But it's the best I can come up with, and until this feature gets added within the framework itself, I think this is the best that anyone can do.
是的,这太可怕了。我也不能 100% 确定它在您同时更改滚动视图的框架和内容的大小的情况下始终正常工作。但这是我能想到的最好的,在这个功能被添加到框架本身之前,我认为这是任何人都能做到的最好的。
回答by cutemachine
Brad Oyler's answer is correct. But you will only receive one event. If you need to get constant updates of the scroll position, you should set the scrollEventThrottle
prop, like so:
Brad Oyler 的回答是正确的。但是您只会收到一个事件。如果您需要不断更新滚动位置,您应该设置scrollEventThrottle
道具,如下所示:
<ScrollView onScroll={this.handleScroll} scrollEventThrottle={16} >
<Text>
Be like water my friend …
</Text>
</ScrollView>
And the event handler:
和事件处理程序:
handleScroll: function(event: Object) {
console.log(event.nativeEvent.contentOffset.y);
},
Be aware that you might run into performance issues. Adjust the throttle accordingly. 16 gives you the most updates. 0 only one.
请注意,您可能会遇到性能问题。相应地调整油门。16 为您提供最多更新。0 只有一个。
回答by Teodors
If you wish to simply get the current position (e.g. when some button is pressed) rather than tracking it whenever the user scrolls, then invoking an onScroll
listener is going to cause performance issues. Currently the most performant way to simply get current scroll position is using react-native-invokepackage. There is an example for this exact thing, but the package does multiple other things.
如果您只想获取当前位置(例如,按下某个按钮时)而不是在用户滚动时跟踪它,那么调用onScroll
侦听器将导致性能问题。目前,简单地获取当前滚动位置的最高效方法是使用react-native-invoke包。这个确切的事情有一个例子,但是这个包做了很多其他的事情。
Read about it here. https://medium.com/@talkol/invoke-any-native-api-directly-from-pure-javascript-in-react-native-1fb6afcdf57d#.68ls1sopd
回答by RY Zheng
The above answers tell how to get the position using different API, onScroll
, onMomentumScrollEnd
etc; If you want to know the page index, you can calculate it using the offset value.
以上答案告诉了如何使用不同的API来获取位置,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
}
};
In iOS, the relationship between ScrollView and the visible region is as follow:
回答by j0n
To get the x/y after scroll ended as the original questions was requesting, the easiest way is probably this:
要在滚动结束后按照原始问题的要求获取 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>
回答by Sabba Keynejad
This is how ended up getting the current scroll position live from the view. All I am doing is using the on scroll event listener and the scrollEventThrottle. I am then passing it as an event to handle scroll function I made and updating my props.
这就是最终从视图中获取当前滚动位置的方式。我所做的就是使用 on scroll 事件侦听器和 scrollEventThrottle。然后我将它作为一个事件传递来处理我制作的滚动功能并更新我的道具。
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} />
)
}
}
回答by Sabba Keynejad
As for the page, I'm working on a higher order component that uses basically the above methods to do exactly this. It actually takes just a bit of time when you get down to the subtleties like initial layout and content changes. I won't claim to have done it 'correctly', but in some sense I'd consider the correct answer to use component that does this carefully and consistently.
至于页面,我正在开发一个高阶组件,该组件基本上使用上述方法来做到这一点。当您深入了解初始布局和内容更改等细微之处时,实际上只需要一点时间。我不会声称已经“正确”地完成了它,但在某种意义上,我会考虑使用仔细和一致地执行此操作的组件的正确答案。
See: react-native-paged-scroll-view. Would love feedback, even if it's that I've done it all wrong!
请参阅:react-native-paged-scroll-view。希望得到反馈,即使我做错了!
回答by Colin Ramsay
I believe contentOffset
will give you an object containing the top-left scroll offset:
我相信contentOffset
会给你一个包含左上角滚动偏移的对象:
http://facebook.github.io/react-native/docs/scrollview.html#contentoffset
http://facebook.github.io/react-native/docs/scrollview.html#contentoffset