react中竖直轮播滚动
ScrollView:可以全部统治一个组件吗? (ScrollView: One component to rule them all?)
ScrollView
is one of the most fundamental components of a React Native app, that can act as a standalone component as well as a dependency for a range of other more complex components.
ScrollView
是React Native应用程序的最基本组件之一,它既可以充当独立组件,又可以作为一系列其他更复杂组件的依赖项。
It is impressive thinking about its wide-ranging use-cases. It can be used as a standalone vertical scrolling solution, automatically making your content suitable for any screen height. Other more complex components, like SectionList
and FlatList
, rely on ScrollView
and its props, whereas community driven components like KeyboardAwareScrollView
expand upon ScrollView
’s functionality to solve complicated UX problems with the virtual keyboard.
关于其广泛的用例,令人印象深刻。 它可以用作独立的垂直滚动解决方案,自动使您的内容适合任何屏幕高度。 其他更复杂的组件(如SectionList
和FlatList
)依赖于ScrollView
及其道具,而社区驱动的组件(如KeyboardAwareScrollView
扩展了ScrollView
的功能来解决虚拟键盘的复杂UX问题。
And these use cases just entail vertical scrolling. ScrollView
also supports horizontal scrolling and pinch to zoom — that tracks the current magnification with the zoomScale
prop, along with others to limit its minimum, maximum and zoom behaviour. This makes ScrollView
ideal for panning around maps-based applications, or apps with large canvases like boardgames.
这些用例仅需要垂直滚动。 ScrollView
还支持水平滚动和收缩缩放-使用zoomScale
道具跟踪当前的放大倍率,并限制其最小,最大和缩放行为。 这使ScrollView
非常适合在基于地图的应用程序或带有大型画布的应用程序(如棋盘游戏)之间平移。
The particular use-case this article focusses on, however, is that of a horizontal-only scrollable carousel component that allows the user to swipe through a range of items (or intervals). This is a very common feature in apps today, being utilised to display and structure a range of data:
但是,本文关注的特定用例是只允许用户在一系列项目(或间隔)中滑动的仅水平可滚动轮播组件。 这是当今应用程序中非常常见的功能,可用于显示和构造一系列数据:

We’ll explore a range of props and event handlers ScrollView
offers to make this happen, as well as custom logic for handling carousel state. The demo project referenced in this article is also available on Github, where two styles of carousel are showcased.
我们将探究ScrollView
提供的各种道具和事件处理程序,以实现此目标,以及用于处理轮播状态的自定义逻辑。 Github上也提供了本文引用的演示项目,其中展示了两种样式的轮播。
带有ScrollView的轮播如何工作 (How Carousels with ScrollView Work)
Utilising ScrollView
to create carousels works extremely well, maintaining a native and fluid feel to your app, while keeping the doors open for customisability of the look and behaviour of the carousel itself.
利用ScrollView
创建轮播效果非常好,可以为您的应用程序保持原生和流畅的感觉,同时保持开门状态,以实现轮播本身的外观和行为的可定制性。
First and foremost, a ScrollView
component must be configured to horizontally structure content rather than vertically. By setting the horizontal
prop to true
, the ScrollView
will automatically arrange its child components in columns rather than rows.
首先,必须将ScrollView
组件配置为水平结构而不是垂直结构。 通过将horizontal
prop设置为true
, ScrollView
将自动将其子组件按列而不是行进行排列。
This sets us up nicely to endeavour into other props to optimise the behaviour of the view (more on props in the next section).
这很好地设置了我们来尝试其他道具以优化视图的行为(在下一节中将对道具进行更多介绍)。
<轮播/>组件结构 (<Carousel /> Component Structure)
In order to visualise how the ScrollView
component is used with other components to form a carousel, consider the following illustration:
为了可视化ScrollView
组件与其他组件一起形成轮播的方式,请考虑以下插图:

There is a 3 component foundational setup here:
这里有3个组件的基础设置:
The
<ScrollView />
is housed within a<Container />
component that defines the visible area of the carousel. This is where borders, background colours, etc, can be styled, giving some context to the scrollable area.<ScrollView />
位于定义<Container />
轮播可见区域的<Container />
组件中。 在这里可以设置边框,背景色等的样式,从而为可滚动区域提供一些上下文。The
<ScrollView />
itself. It is important to mention at this stage that the<ScrollView />
itself needs a pre-definedwidth
in itscontentContainerStyle
prop. Without the width pre-defined, we would not be able to scroll through theScrollView
— we’ll explore some code further down to calculate the width based on how many intervals the carousel holds, as well as snapping to the beginning of each interval when scrolling.<ScrollView />
本身。 重要的是在此阶段要提到<ScrollView />
本身在contentContainerStyle
道具中需要预定义的width
。 如果没有预先定义的宽度,我们将无法滚动浏览ScrollView
我们将进一步探索一些代码,以根据轮播持有的间隔时间来计算宽度,并在出现以下情况时捕捉到每个间隔的开始滚动。
Height does not matter so much — it can be set as a fixed value or be automatic based on the carousel content, depending on your design.
高度无关紧要-可以将其设置为固定值,也可以根据轮播内容自动设置高度,具体取决于您的设计。
Each
<Item />
, representing one item of the carousel. This is where your displayable content will be housed, such as a row of stats, an app introduction slide, etc. Depending on your design, one<Item />
may span100%
of the<Container />
width, or it may span a half or even a third of the viewable area — this will be considered in the code further down.每个
<Item />
,代表一个轮播项目。 这是可显示内容的存放位置,例如一排统计信息,应用程序介绍幻灯片等。根据您的设计,一个<Item />
可能跨越<Container />
宽度的100%
,或者可能跨越可视区域的一半甚至三分之一-在下面的代码中将予以考虑。
A <Item />
is not tied to a carousel interval E.g. one item per swipe. There could be multiple items tied into one interval, such as displaying multiple statistics or graphs in one view. This is carousels should be designed, and is considered in the demos further down.
甲 <Item />
不依赖于转盘间隔每轻扫例如一个项目。 可能有多个项目捆绑在一起,例如在一个视图中显示多个统计信息或图形。 这是轮播应该设计的,并在演示中进一步考虑。
The above represents a basic component setup to get the carousel working, but is by no means a complete solution.
以上是启动轮播的基本组件设置,但绝不是完整的解决方案。
Based on scroll events, however, we can introduce more UX to compliment the carousel state such as bullet points fixed below the carousel representing each interval with the active interval bullet point being a darker shade. These additional UX components are often placed outside the <ScrollView />
itself, not being subject to scroll events or swipe-able content:
但是,基于滚动事件,我们可以引入更多的UX来补充圆盘传送带状态,例如固定在圆盘传送带下方的子弹点,代表每个间隔,而活动间隔子弹点则为较暗的阴影。 这些附加的UX组件通常放置在<ScrollView />
本身之外,不受滚动事件或可滑动内容的影响:
// carousel component hierarchy pseudo code<Container>
<ScrollView>
<Item />
<Item />
<Item />
--- next interval
<Item />
<Item />
<Item />
</ScrollView><Bullets />
<Container />
Notice where the next interval is — or the point that will snap to the start of the carousel to separate its stages, that is calculated separately from the Item
widths.
请注意,下一个间隔在哪里-或将与圆盘传送带的起点对齐以分离其各个阶段的点,该点是与 Item
宽度 分开计算的 。
Let’s dive into some of the critical props of ScrollView
next, before utilising some of the event handlers to calculate vital carousel information.
接下来,在利用一些事件处理程序计算重要的轮播信息之前,让我们深入了解ScrollView
一些关键道具。
ScrollView道具和滚动事件 (ScrollView Props and Scroll Events)
There are a couple of key props at our disposal to configure a ScrollView
to be a paginated, horizontal scroller.
我们可以使用几个关键道具来将ScrollView
配置为分页的水平滚动条。
Examine the following example that highlights key props:
检查以下示例,其中重点介绍了主要道具:
// props for a horizontal, paginated ScrollView<ScrollView
horizontal={true}
contentContainerStyle={{ width: `${100 * intervals}%` }}
showsHorizontalScrollIndicator={false}
scrollEventThrottle={200}
decelerationRate="fast"
pagingEnabled
>
{/* Items */}
</ScrollView>
In the above code we are assuming that each <Item />
takes up 100% width of the carousel area, therefore the width of the ScrollView
will be 100% multiplied by the amount of items.
在上面的代码中,我们假设每个 <Item />
占用轮播区域的100%宽度,因此 ScrollView
的宽度 将 乘以 100% 乘以 项目的数量。
The top two props have already been covered, aligning horizontal
content within the Scroll View, as well as defining its width in contentContainerStyle
. Breaking down the others:
上面的两个道具已经被覆盖,它们可以在Scroll View中对齐horizontal
内容,并在contentContainerStyle
定义其宽度。 分解其他:
The
showHorizontalScrollIndicator
set tofalse
hides the scroll bar — the horizontal bar that shows up as the user is scrolling to give context to the current scroll position. This bar is not too necessary in a carousel environment, and can be replaced with the aforementioned bullet point mechanism instead.将
showHorizontalScrollIndicator
设置为false
隐藏滚动条,即在用户滚动以将上下文提供给当前滚动位置时显示的水平条。 在圆盘传送带环境中,该杆不是太必要,可以改为使用上述的项目符号机制。scrollEventThrottle
will become important further down for optimising scroll event performance, setting a delay between scroll events being fired. For carousels at least, it is not required to maintain an accurate scroll position — the data derived from these scroll events will determine which interval the carousel is showing only, which can be calculated with some latency without having an impact on user experience.对于优化滚动事件性能,设置触发滚动事件之间的延迟,
scrollEventThrottle
将变得越来越重要。 至少对于轮播来说,不需要保持准确的滚动位置-从这些滚动事件中得出的数据将确定轮播仅显示哪个间隔,可以在不影响用户体验的情况下以一定的等待时间进行计算。The
pagingEnabled
prop acts as a snapping shortcut, that stops the Scroll View at multiples of its size when scrolling. This is what effectively separates the scroll view into sections, intelligently snapping the scroll position to the nearest threshold as the user swipes.pagingEnabled
用作捕捉快捷方式,在滚动时会以其大小的倍数停止Scroll View。 这就是有效地将滚动视图分为多个部分的功能,可以在用户滑动时智能地将滚动位置捕捉到最近的阈值。decelerationRate
defines the speed at which the scroll stops after the user lifts their finger. It is recommended to usefast
for carousel purposes, shortening the time it takes to transition to the interval in question’s snap position.decelerationRate
定义了用户抬起手指后滚动停止的速度。 建议fast
用于轮播,以缩短过渡到相关问题的快照位置间隔所需的时间。
ScrollView的其他捕捉道具 (Other snapping props of ScrollView)
The pagingEnabled
prop is a very useful shortcut for defining the interval logic for the Scroll View, but it is worth noting that there are a number of other props that can refine this pagination, or snapping, behaviour.
pagingEnabled
是定义Scroll View的时间间隔逻辑的非常有用的快捷方式,但是值得注意的是,还有许多其他属性可以改进此分页或捕捉行为。
For example, pagingEnabled
can also be achieved with the snapToStart
, snapToOffsets
, snapToInterval
and snapToAlignment
props, that can be used together to define custom Scroll View pagination. snapToInterval
is also a shortcut in place of defining an array of numbers with snapToOffsets
.
例如, pagingEnabled
也可以使用snapToStart
, snapToOffsets
, snapToInterval
和snapToAlignment
道具来实现,它们可以一起使用来定义自定义Scroll View分页。 snapToInterval
也是一种使用snapToOffsets
定义数字数组的快捷方式。
snapToStart
and snapToOffsets={[0]}
achieve the same result.
snapToStart
和 snapToOffsets={[0]}
达到相同的结果。
snapToAlignment
is also an interesting prop, that snaps the view to the start
, end
, or center
of a snapping interval. Think of a scenario where multiple Scroll View <Item />
’s are on show simultaneously. Setting snapToAlignment
to center
will automatically snap the active interval to be at the center of the scroll area, consequently giving precedence to the current item being at the center of the view. snapToAlignment
can be used with snap
props and pagingEnabled
.
snapToAlignment
也是一个有趣的道具,它可以将视图捕捉到捕捉间隔的start
, end
或center
。 考虑一下同时显示多个Scroll View <Item />
的情况。 将snapToAlignment
设置为center
将自动将活动间隔捕捉到滚动区域的中心,因此优先于当前项位于视图的中心。 snapToAlignment
可以与snap
道具和pagingEnabled
。
It is worth studying and experimenting with these props to get a more unique feel to a carousel, if you need to deliver a bespoke carousel experience. For the examples discussed in this article, pagingEnabled
alone works very well and keeps syntax to a minimum.
如果您需要提供定制的轮播体验,则值得研究和尝试使用这些道具以获得更独特的轮播感觉。 对于本文讨论的示例,单独使用pagingEnabled
可以很好地工作,并将语法降至最低。
With ScrollView
configuration now understood, lets next implement a <Carousel />
component, and introduce scroll events that will aid in carousel state and further UX additions.
现在已经了解了ScrollView
配置,接下来让我们实现<Carousel />
组件,并引入滚动事件,这些事件将有助于轮播状态和进一步的UX添加。
ScrollView的轮播实现 (Carousel Implementation with ScrollView)
In this section we will walk though creating a fully functional <Carousel />
component, separating the implementation into two steps before importing and using it.
在本节中,我们将逐步创建一个功能齐全的<Carousel />
组件,在导入和使用该组件之前将实现分为两个步骤。
Within the Carousel
component, ScrollView
events will be used to trigger component state updates and initialisation in the following order:
在Carousel
组件内,将使用ScrollView
事件按以下顺序触发组件状态更新和初始化:
Step 1: Initialising the carousel within the
onContentSizeChange
event, that will calculate how many intervals (carousel “pages”) there are, as well as its width.步骤1:在
onContentSizeChange
事件中初始化轮播,它将计算有多少间隔(轮播“页面”)及其宽度。Step 2: Calculating the currently active interval with the
onScroll
event, and reflecting that in surrounding UX.步骤2:使用
onScroll
事件计算当前活动间隔,并将其反映在周围的UX中。
A gist will be presented for the first step, and the final project with the full solution will be linked at the end of the article. To look at this project now, it is available hereon Github. Here is a screencast of the demo, showcasing two styles of carousel:
第一步将提出要点,而带有完整解决方案的最终项目将在本文结尾处链接。 现在来看这个项目,可以在Github上找到 。 这是演示的截屏,展示了两种旋转木马风格:

Both these carousels are derived from the same <Carousel />
component, but render different components for their items, based on a style
prop we will be passing into it.
这两个转盘均来自相同的<Carousel />
组件,但根据我们将传递给它的style
道具,为其商品渲染不同的组件。
导入轮播组件及其道具 (Importing Carousel component and its props)
To understand the <Carousel />
component, let’s firstly examine how we want to embed the component within JSX — and what props it requires:
要了解<Carousel />
组件,首先让我们检查一下我们如何将组件嵌入JSX中-以及它需要哪些道具:
// importing and using `Carousel`import Carousel from './Carousel';export const App = () => (
<View style={styles.container}> <Carouselstyle="slides"itemsPerInterval={1}items={[{
title: 'Welcome, swipe to continue.',
}, {
title: 'About feature X.',
}, {
title: 'About feature Y.',
}]}
/>
</View>
);
As we can see from above, <Carousel />
supports 3 props, that each play a role in configuring the function and content of the carousel:
从上方可以看到, <Carousel />
支持3个道具,每个道具在配置轮播的功能和内容时都起作用:
style
defines what type of<item />
component we will use for each item displayed in the carousel. In the demo project we have two components that represent a carousel item —Stat
andSlide
. Within the carousel itself is aswitch
statement that renders the component corresponding to the style prop, acting as a simple mechanism to change the look or type of carousel to render.style
定义轮播中显示的每个项目将使用的<item />
组件类型。 在演示项目中,我们有两个代表轮播项目的组件Stat
和Slide
。 轮播本身中包含一个switch
语句,该语句用于渲染与样式道具相对应的组件,作为一种简单的机制来更改要渲染的轮播的外观或类型。itemsPerInterval
allows us to configure how many of the<Item />
component to display on one swipe-able area of the carousel — or how many items are displayed at one time. The value passed in here is used within the component to determine how many swipe-able sections the carousel holds. For theslides
style, only 1 item per interval is required. For thestats
style however, 3 items per interval is required, with eachStat
consuming 33% width.itemsPerInterval
允许我们配置要在轮播的一个可滑动区域上显示多少<Item />
组件,或者一次显示多少个项。 此处传递的值在组件内用于确定圆盘传送带可容纳多少可滑动部分。 对于slides
样式,每个间隔仅需要一项。 但是对于stats
样式,每个间隔需要3项,每个Stat
占用33%的宽度。items
is an array of objects consisting of the data of each carousel item. The structure of the objects need to be consistent and conform to the<Item />
component‘s data requirements. ASlide
component here only requires a title, keeping the item requirements simple.items
是由每个轮播项目的数据组成的对象数组。 对象的结构需要一致并符合<Item />
组件的数据要求。 此处的Slide
组件仅需要一个标题,从而使项目要求保持简单。
These props are dealt with at the top of Carousel
:
这些道具在Carousel
的顶部处理:
// extracting Carousel propsconst { items, style } = props;
const itemsPerInterval = props.itemsPerInterval === undefined
? 1
: props.itemsPerInterval;
From here we can initialise the carousel.
从这里我们可以初始化轮播。
轮播初始化 (Carousel initialisation)
The initialisation of the carousel is carried out in one of its ScrollView
event handlers, onContentSizeChange
. This event is actually fired when the Scroll View first renders, allowing us to embed initialisation logic:
轮播的初始化是在其ScrollView
事件处理程序之一onContentSizeChange
。 实际上,在Scroll View首次渲染时会触发此事件,从而允许我们嵌入初始化逻辑:
// calling init() within `onContentSizeChange`...
return (
<View style={styles.container}>
<ScrollView
...onContentSizeChange={(w, h) => init(w)} >
...
</ScrollView>
</View>
)
In the event your Scroll View changes size based on a responsive design, onContentSizeChange
will fire again, and keep the carousel dimensions in sync with init()
.
如果您的Scroll View根据响应式设计更改大小,则 onContentSizeChange
将再次触发,并使轮播尺寸与 init()
保持同步 。
onContentSizeChange
gives us the width and height of the carousel as callback arguments, the width of which is then passed into Carousel’s init()
method. init()
is responsible for defining the carousel width, along with how many intervals will be present:
onContentSizeChange
为我们提供轮播的宽度和高度作为回调参数,然后将其宽度传递到Carousel的init()
方法中。 init()
负责定义轮播宽度以及将出现多少个间隔:
// init() implementationconst [intervals, setIntervals] = React.useState(1);
const [width, setWidth] = React.useState(0);const init = (width: number) => {// initialise width
setWidth(width); // get total items present
const totalItems = items.length; // initialise total intervals setIntervals(Math.ceil(totalItems / itemsPerInterval));
}
By utilising useState
hooks and a simple calculation to determine how many intervals are needed based on how many items the carousel holds, the carousel now has context about its width and snapping intervals required for it to function.
通过使用useState
挂钩和简单的计算来确定所需的间隔时间,该间隔取决于传送带所容纳的物品数量,传送带现在具有有关其宽度和功能所需的捕捉间隔的上下文。
The following Gist outlines the <Carousel />
component at this initialisation stage:
以下要点概述了此初始化阶段的<Carousel />
组件:
介绍电流间隔计算 (Introducing current interval calculation)
To calculate the current carousel interval (also thought of as the carousel “page”), a bit more logic needs to be introduced into the <Carousel />
component. Firstly, an additional useState
hook to store the current interval, as a default value of 1:
要计算当前轮播间隔(也称为轮播“页面”),需要在<Carousel />
组件中引入更多逻辑。 首先,附加的useState
钩子存储当前间隔,默认值为1:
const [interval, setInterval] = React.useState(1);
In order to calculate the active interval, we rely on ScrollView
’s onScroll
event handler. When it fires we will call another method, getInterval()
, to determine the current interval based on the ScrollView
’s current offset:
为了计算活动间隔,我们依靠ScrollView
的onScroll
事件处理程序。 当它触发时,我们将调用另一个方法getInterval()
,以根据ScrollView
的当前偏移量确定当前间隔:
// defining onScroll event and getInterval()const getInterval = (offset: any) => {
for (let i = 1; i <= intervals; i++) {
if (offset < (width / intervals) * i) {
return i;
}
if (i == intervals) {
return i;
}
}
}...<ScrollView
...onScroll={data => {
setInterval(getInterval(data.nativeEvent.contentOffset.x));
}}scrollEventThrottle={200}
>
...
</ScrollView>
getInterval()
is simply a for loop with a couple of conditional statements inside it, checking if the current scroll offset falls between certain interval thresholds. The scrollEventThrottle
prop limits the scroll event to fire every 200 milliseconds, not to put too much of a burden on performance.
getInterval()
只是一个for循环,其中包含几个条件语句,用于检查当前滚动偏移量是否在某些间隔阈值之间。 scrollEventThrottle
道具限制了滚动事件每200毫秒触发一次,而不会对性能造成太大负担。
添加补充用户体验 (Adding supplemental UX)
With the knowledge of knowing what interval the carousel is now on, we can now introduce UX such as bullet points as visual aids to the currently selected interval.
了解了轮播的当前间隔后,我们现在可以将UX(例如项目符号点)引入当前所选间隔作为视觉辅助。
To construct the bullet points dynamically, another for loop can be used to build the correct amount of bullets based on the number of intervals:
为了动态构造项目符号点,可以使用另一个for循环根据间隔数来构建正确数量的项目符号:
// constructing bullet pointslet bullets = [];
for (let i = 1; i <= intervals; i++) {
bullets.push(
<Text
key={i}
style={{
...styles.bullet,
opacity: interval=== i ? 0.5 : 0.1
}}
>
•
</Text>
);
}
Each bullet point is wrapped within a native Text
component, with the currently selected interval
having a less intense opacity. Once constructed, the bullets
array can simply be embedded in JSX:
每个项目符号点都包装在一个本机Text
组件中,当前选择的interval
具有较小的不透明性。 一旦构建完成, bullets
数组就可以简单地嵌入JSX中:
// inserting bullets after ScrollView
... </ScrollView>
<View style={styles.bullets}>{bullets}
</View>
</View>
)
The full implementation of Carousel
can be found here.
可以在此处找到Carousel
的完整实现。
综上所述 (In Summary)
This wraps up the base implementation of the horizontal carousel, giving a solid foundation with the hope developers can build upon to achieve more unique styles and behaviours.
这样就构成了水平轮播的基本实现,为开发人员可以基于此实现更多独特的样式和行为奠定了坚实的基础。
This article has demonstrated how to create native feeling carousels with ScrollView
, and how to build the surrounding carousel logic around the component. We have also looked into how to structure a standalone <Carousel />
component that supports various stage styles with particular components, such as Stat
and Slide
.
本文演示了如何使用ScrollView
创建具有ScrollView
感觉的轮播,以及如何在组件周围构建周围的轮播逻辑。 我们还研究了如何构建一个独立的<Carousel />
组件,该组件支持具有特定组件(例如Stat
和Slide
各种舞台样式。
The demo project ultimately attempts to abstract key logic and styling for the developer to decide, not being tied down to any particular size or layout of a carousel item.
该演示项目最终尝试抽象出关键逻辑和样式,以供开发人员决定,而不是受限于轮播项目的任何特定大小或布局。
The project can be viewed or cloned here on Github, with the playable demo showcasing both the statistics and slideshow styled carousels.
您可以在Github上查看或克隆该项目,可播放的演示同时展示统计数据和幻灯片样式的轮播。
react中竖直轮播滚动