• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Scroll
2
3可滚动的容器组件,当子组件的布局尺寸超过父组件的尺寸时,内容可以滚动。
4
5>  **说明:**
6>  - 该组件从API version 7开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。
7>  - 该组件嵌套List子组件滚动时,若List不设置宽高,则默认全部加载,在对性能有要求的场景下建议指定List的宽高。
8>  - 该组件滚动的前提是主轴方向大小小于内容大小。
9>  - 该组件回弹的前提是要有滚动。内容小于一屏时,没有回弹效果。
10
11
12## 子组件
13
14支持单个子组件。
15
16
17## 接口
18
19Scroll(scroller?: Scroller)
20
21**参数:**
22
23| 参数名 | 参数类型 | 必填 | 参数描述 |
24| -------- | -------- | -------- | -------- |
25| scroller | [Scroller](#scroller) | 否 | 可滚动组件的控制器。用于与可滚动组件进行绑定。 |
26
27## 属性
28
29除支持[通用属性](ts-universal-attributes-size.md)外,还支持以下属性:
30
31| 名称             | 参数类型                                     | 描述        |
32| -------------- | ---------------------------------------- | --------- |
33| scrollable     | [ScrollDirection](#scrolldirection枚举说明)                        | 设置滚动方向。<br/>默认值:ScrollDirection.Vertical |
34| scrollBar      | [BarState](ts-appendix-enums.md#barstate) | 设置滚动条状态。<br/>默认值:BarState.Auto<br/>**说明:** <br/>如果容器组件无法滚动,则滚动条不显示。如果容器组件的子组件大小为无穷大,则滚动条不支持拖动和伴随滚动。 |
35| scrollBarColor | string&nbsp;\|&nbsp;number&nbsp;\|&nbsp;[Color](ts-appendix-enums.md#color)   | 设置滚动条的颜色。 |
36| scrollBarWidth | string&nbsp;\|&nbsp;number         | 设置滚动条的宽度,不支持百分比设置。<br/>默认值:4<br/>单位:vp<br/>**说明:** <br/>如果滚动条的宽度超过其高度,则滚动条的宽度会变为默认值。 |
37| scrollSnap<sup>10+</sup>     | [ScrollSnapOptions](#scrollsnapoptions10)                     | 设置Scroll组件的限位滚动模式。 |
38| edgeEffect     | [EdgeEffect](ts-appendix-enums.md#edgeeffect)            | 设置滑动效果,目前支持的滑动效果参见EdgeEffect的枚举说明。<br/>默认值:EdgeEffect.None |
39| enableScrollInteraction<sup>10+</sup>  |  boolean  |   设置是否支持滚动手势,当设置为false时,无法通过手指或者鼠标滚动,但不影响控制器的滚动接口。<br/>默认值:true      |
40| nestedScroll<sup>10+</sup>                 | [NestedScrollOptions](#nestedscrolloptions10对象说明)         | 嵌套滚动选项。设置向前向后两个方向上的嵌套滚动模式,实现与父组件的滚动联动。 |
41| friction<sup>10+</sup> | number \| [Resource](ts-types.md#resource)    | 设置摩擦系数,手动划动滚动区域时生效,只对惯性滚动过程有影响,对惯性滚动过程中的链式效果有间接影响。<br/>默认值:非可穿戴设备为0.6,可穿戴设备为0.9<br/>**说明:** <br/>设置为小于等于0的值时,按默认值处理 |
42
43## ScrollDirection枚举说明
44| 名称       | 描述                     |
45| ---------- | ------------------------ |
46| Horizontal | 仅支持水平方向滚动。     |
47| Vertical   | 仅支持竖直方向滚动。     |
48| None       | 不可滚动。               |
49| Free<sup>(deprecated) </sup> | 支持竖直或水平方向滚动<br/> 从API version 9开始废弃|
50
51## ScrollSnapOptions<sup>10+</sup>
52| 名称       | 参数类型       | 描述       |
53| ---------- | ---------------------------------------- | -------- |
54| snapAlign  | [ScrollSnapAlign](ts-container-list.md#scrollsnapalign10枚举说明)   | 设置Scroll组件限位滚动时的对齐方式。<br/>**说明:** <br/>1.该属性默认值为ScrollSnapAlign.NONE。<br/>2.该接口仅当snapPagination属性为Dimension时生效,不支持Array\<Dimension\>。 |
55| snapPagination | [Dimension](ts-types.md#dimension10)&nbsp;\|&nbsp;Array\<Dimension\> | 设置Scroll组件限位滚动时的限位点,限位点即为Scroll组件能滑动停靠的偏移量。<br/>**说明:** <br/>1.当属性为Dimension时,表示每页的大小,系统会按照该大小来自动计算每个限位点的位置:如当Dimension为500时,实际的限位点即为[0,500,1000,1500,...]。<br/>2.当属性为Array\<Dimension\>时,每个Dimension代表限位点的位置。每个Dimension的范围为[0,可滑动距离],0和可滑动距离的底部自动成为限位点。<br/>3.当该属性不填或者Dimension为小于等于0的输入时,按异常值,无限位滚动处理。当该属性值为Array\<Dimension\>数组时,数组中的数值必须为单调递增。<br/>4.当输入为百分比时,实际的大小为Scroll组件的视口与百分比数值之积。 |
56| enableSnapToStart | boolean   | 在Scroll组件限位滚动模式下,该属性设置为false后,运行Scroll在开头和第一个限位点间自由滑动。<br/>**说明:** <br/>1.该属性值默认为true。<br/>2.该属性仅当snapPagination属性为Array\<Dimension\>时生效,不支持Dimension。 |
57| enableSnapToEnd | boolean   | 在Scroll组件限位滚动模式下,该属性设置为false后,运行Scroll在最后一个限位点和末尾间自由滑动。<br/>**说明:** <br/>1.该属性值默认为true。<br/>2.该属性仅当snapPagination属性为Array\<Dimension\>时生效,不支持Dimension。 |
58
59## 事件
60
61| 名称                                                         | 功能描述                                                     |
62| ------------------------------------------------------------ | ------------------------------------------------------------ |
63| onScrollFrameBegin<sup>9+</sup>(event: (offset: number, state: [ScrollState](ts-container-list.md#scrollstate枚举说明)) => { offsetRemain: number; }) | 每帧开始滚动时触发,事件参数传入即将发生的滚动量,事件处理函数中可根据应用场景计算实际需要的滚动量并作为事件处理函数的返回值返回,Scroll将按照返回值的实际滚动量进行滚动。<br/>\- offset:即将发生的滚动量,单位vp。<br/>\- state:当前滚动状态。<br/>- offsetRemain:实际滚动量,单位vp。<br/>触发该事件的条件 :<br/>1、滚动组件触发滚动时触发,包括键鼠操作等其他触发滚动的输入设置。<br/>2、调用控制器接口时不触发。<br/>3、越界回弹不触发。<br/>4、拖动滚动条不触发。<br/>**说明:** <br/>支持offsetRemain为负值。<br/>若通过onScrollFrameBegin事件和scrollBy方法实现容器嵌套滚动,需设置子滚动节点的EdgeEffect为None。如Scroll嵌套List滚动时,List组件的edgeEffect属性需设置为EdgeEffect.None。 |
64| onScroll(event: (xOffset: number, yOffset: number) => void)  | 滚动事件回调,&nbsp;返回滚动时水平、竖直方向偏移量,单位vp。<br/>触发该事件的条件 :<br/>1、滚动组件触发滚动时触发,支持键鼠操作等其他触发滚动的输入设置。<br/>2、通过滚动控制器API接口调用。<br/>3、越界回弹。 |
65| onScrollEdge(event: (side: Edge) => void)                    | 滚动到边缘事件回调。<br/>触发该事件的条件 :<br/>1、滚动组件滚动到边缘时触发,支持键鼠操作等其他触发滚动的输入设置。<br/>2、通过滚动控制器API接口调用。<br/>3、越界回弹。 |
66| onScrollEnd<sup>(deprecated) </sup>(event: () => void)       | 滚动停止事件回调。<br>该事件从API version 9开始废弃,使用onScrollStop事件替代。<br/>触发该事件的条件 :<br/>1、滚动组件触发滚动后停止,支持键鼠操作等其他触发滚动的输入设置。<br/>2、通过滚动控制器API接口调用后停止,带过渡动效。 |
67| onScrollStart<sup>9+</sup>(event: () => void)                | 滚动开始时触发。手指拖动Scroll或拖动Scroll的滚动条触发的滚动开始时,会触发该事件。使用[Scroller](#scroller)滚动控制器触发的带动画的滚动,动画开始时会触发该事件。<br/>触发该事件的条件 :<br/>1、滚动组件开始滚动时触发,支持键鼠操作等其他触发滚动的输入设置。<br/>2、通过滚动控制器API接口调用后开始,带过渡动效。 |
68| onScrollStop<sup>9+</sup>(event: () => void)                 | 滚动停止时触发。手拖动Scroll或拖动Scroll的滚动条触发的滚动,手离开屏幕并且滚动停止时会触发该事件。使用[Scroller](#scroller)滚动控制器触发的带动画的滚动,动画停止时会触发该事件。<br/>触发该事件的条件 :<br/>1、滚动组件触发滚动后停止,支持键鼠操作等其他触发滚动的输入设置。<br/>2、通过滚动控制器API接口调用后开始,带过渡动效。 |
69
70>  **说明:**
71>
72>  若通过onScrollFrameBegin事件和scrollBy方法实现容器嵌套滚动,需设置子滚动节点的EdgeEffect为None。如Scroll嵌套List滚动时,List组件的edgeEffect属性需设置为EdgeEffect.None73
74## Scroller
75
76可滚动容器组件的控制器,可以将此组件绑定至容器组件,然后通过它控制容器组件的滚动,同一个控制器不可以控制多个容器组件,目前支持绑定到List、Scroll、ScrollBar、Grid、WaterFlow上。
77
78
79### 导入对象
80
81```
82scroller: Scroller = new Scroller()
83```
84
85
86### scrollTo
87
88scrollTo(value: { xOffset: number | string, yOffset: number | string, animation?: { duration?: number, curve?: Curve | ICurve } | boolean }): void
89
90
91滑动到指定位置。
92
93**参数:**
94
95| 参数名    | 参数类型                                                     | 必填 | 参数描述                                                     |
96| --------- | ------------------------------------------------------------ | ---- | ------------------------------------------------------------ |
97| xOffset   | number&nbsp;\|&nbsp;string                                   | 是   | 水平滑动偏移。<br/>**说明:** <br/>该参数值不支持设置百分比。<br/>该参数值不支持设置百分比。<br/>当值小于0时,不带动画的滚动,按0处理。带动画的滚动,滚动到起始位置后停止。<br/>仅滚动轴为x轴时生效。 |
98| yOffset   | number&nbsp;\|&nbsp;string                                   | 是   | 垂直滑动偏移。<br/>**说明:** <br/>该参数值不支持设置百分比。<br/>该参数值不支持设置百分比。<br/>当值小于0时,不带动画的滚动,按0处理。带动画的滚动,滚动到起始位置后停止。<br/>仅滚动轴为y轴时生效。 |
99| animation | {duration?:&nbsp;number, curve?:&nbsp;[Curve](ts-appendix-enums.md#curve)&nbsp;\|&nbsp;[ICurve](../apis/js-apis-curve.md#icurve)<sup>10+ </sup>}&nbsp;\|&nbsp;boolean<sup>10+ </sup> | 否   | 动画配置:<br/>-&nbsp;duration:&nbsp;滚动时长设置。<br/>-&nbsp;curve:&nbsp;滚动曲线设置。<br/>- boolean:&nbsp;使能默认弹簧动效。<br/>默认值: <br/>{<br/>duration:&nbsp;1000,<br/>curve:&nbsp;Curve.Ease<br/>}<br/>boolean:&nbsp;false<br/>**说明:** <br/>设置为小于0的值时,按默认值显示。<br/>当前List、Scroll、Grid、WaterFlow均支持boolean类型和ICurve曲线。 |
100
101
102### scrollEdge
103
104scrollEdge(value: Edge): void
105
106
107滚动到容器边缘,不区分滚动轴方向,Edge.TopEdge.Start表现相同,Edge.BottomEdge.End表现相同。
108
109**参数:**
110
111| 参数名   | 参数类型 | 必填   | 参数描述      |
112| ----- | ---- | ---- | --------- |
113| value | [Edge](ts-appendix-enums.md#edge) | 是    | 滚动到的边缘位置。 |
114
115
116### scrollPage
117
118scrollPage(value: { next: boolean, direction?: Axis }): void
119
120滚动到下一页或者上一页。
121
122**参数:**
123
124| 参数名       | 参数类型    | 必填   | 参数描述                           |
125| --------- | ------- | ---- | ------------------------------ |
126| next      | boolean | 是    | 是否向下翻页。true表示向下翻页,false表示向上翻页。 |
127| direction<sup>(deprecated) </sup> | [Axis](ts-appendix-enums.md#axis)    | 否    | 设置滚动方向为水平或竖直方向。<br/> 从API version 9开始废弃                |
128
129
130### currentOffset
131
132currentOffset(): { xOffset: number, yOffset: number }
133
134
135返回当前的滚动偏移量。
136
137**返回值**
138
139| 类型                                                       | 描述                                                         |
140| ---------------------------------------------------------- | ------------------------------------------------------------ |
141| {<br/>xOffset:&nbsp;number,<br/>yOffset:&nbsp;number<br/>} | xOffset:&nbsp;水平滑动偏移;<br/>yOffset:&nbsp;竖直滑动偏移。<br/>**说明:** <br/>返回值单位为vp。 |
142
143
144### scrollToIndex
145
146scrollToIndex(value: number, smooth?: boolean, align?: ScrollAlign): void
147
148滑动到指定Index。
149
150开启smooth动效时,会对经过的所有item进行加载和布局计算,当大量加载item时会导致性能问题。
151
152
153>  **说明:**
154>
155>  仅支持Grid、List、WaterFlow组件。
156
157**参数:**
158
159| 参数名                | 参数类型 | 必填 | 参数描述                                                     |
160| --------------------- | -------- | ---- | ------------------------------------------------------------ |
161| value | number   | 是   | 要滑动到的目标元素在当前容器中的索引值。      <br/>**说明:** <br/>value值设置成负值或者大于当前容器子组件的最大索引值,视为异常值,本次跳转不生效。                     |
162| smooth | boolean  | 否   | 设置滑动到列表项在列表中的索引值时是否有动效,true表示有动效,false表示没有动效。<br/>默认值:false。<br/>**说明:** <br/>当前仅List组件支持该参数。 |
163| align | [ScrollAlign](#scrollalign10枚举说明)  | 否   | 指定滑动到的元素与当前容器的对齐方式。<br/>List中的默认值为:ScrollAlign.START。Grid中默认值为:ScrollAlign.AUTO<br/>**说明:** <br/>当前仅List、Grid组件支持该参数。 |
164
165### scrollBy<sup>9+</sup>
166
167scrollBy(dx: Length, dy: Length): void
168
169
170滑动指定距离。
171
172
173>  **说明:**
174>
175>  支持Scroll、List、Grid、WaterFlow组件。
176
177**参数:**
178
179| 参数名   | 参数类型   | 必填   | 参数描述              |
180| ----- | ------ | ---- | ----------------- |
181| dx | Length | 是    | 水平方向滚动距离,不支持百分比形式。 |
182| dy | Length | 是    | 竖直方向滚动距离,不支持百分比形式。 |
183
184### isAtEnd<sup>10+</sup>
185
186isAtEnd(): boolean
187
188查询组件是否滚动到底部。
189
190>  **说明:**
191>
192>  支持Scroll、List、Grid、WaterFlow组件。
193
194**返回值**
195
196| 类型         | 描述          |
197| ------- | -------- |
198| boolean | true表示组件已经滚动到底部,false表示组件还没滚动到底部。 |
199
200## ScrollAlign<sup>10+</sup>枚举说明
201
202| 名称     | 描述                             |
203| ------ | ------------------------------ |
204| START   | 首部对齐。指定item首部与List首部对齐。  |
205| CENTER | 居中对齐。指定item主轴方向居中对齐于List。        |
206| END  | 尾部对齐。指定item尾部与List尾部对齐。 |
207| AUTO  | 自动对齐。<br/>若指定item完全处于显示区,不做调整。否则依照滑动距离最短的原则,将指定item首部对齐或尾部对齐于List,使指定item完全处于显示区。|
208
209## NestedScrollOptions<sup>10+</sup>对象说明
210| 名称   | 类型   | 描述              |
211| ----- | ------ | ----------------- |
212| scrollForward | NestedScrollMode | 可滚动组件往末尾端滚动时的嵌套滚动选项。 |
213| scrollBackward | NestedScrollMode |  可滚动组件往起始端滚动时的嵌套滚动选项。 |
214
215## NestedScrollMode<sup>10+</sup>枚举说明
216| 名称     | 描述                             |
217| ------ | ------------------------------ |
218| SELF_ONLY   | 只自身滚动,不与父组件联动。  |
219| SELF_FIRST | 自身先滚动,自身滚动到边缘以后父组件滚动。父组件滚动到边缘以后,如果父组件有边缘效果,则父组件触发边缘效果,否则子组件触发边缘效果。        |
220| PARENT_FIRST  | 父组件先滚动,父组件滚动到边缘以后自身滚动。自身滚动到边缘后,如果有边缘效果,会触发自身的边缘效果,否则触发父组件的边缘效果。 |
221| PARALLEL  | 自身和父组件同时滚动,自身和父组件都到达边缘以后,如果自身有边缘效果,则自身触发边缘效果,否则父组件触发边缘效果。|
222
223## 示例
224### 示例1
225
226```ts
227// xxx.ets
228import Curves from '@ohos.curves'
229
230@Entry
231@Component
232struct ScrollExample {
233  scroller: Scroller = new Scroller()
234  private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
235
236  build() {
237    Stack({ alignContent: Alignment.TopStart }) {
238      Scroll(this.scroller) {
239        Column() {
240          ForEach(this.arr, (item: number) => {
241            Text(item.toString())
242              .width('90%')
243              .height(150)
244              .backgroundColor(0xFFFFFF)
245              .borderRadius(15)
246              .fontSize(16)
247              .textAlign(TextAlign.Center)
248              .margin({ top: 10 })
249          }, (item: string) => item)
250        }.width('100%')
251      }
252      .scrollable(ScrollDirection.Vertical) // 滚动方向纵向
253      .scrollBar(BarState.On) // 滚动条常驻显示
254      .scrollBarColor(Color.Gray) // 滚动条颜色
255      .scrollBarWidth(10) // 滚动条宽度
256      .friction(0.6)
257      .edgeEffect(EdgeEffect.None)
258      .onScroll((xOffset: number, yOffset: number) => {
259        console.info(xOffset + ' ' + yOffset)
260      })
261      .onScrollEdge((side: Edge) => {
262        console.info('To the edge')
263      })
264      .onScrollStop(() => {
265        console.info('Scroll Stop')
266      })
267
268      Button('scroll 150')
269        .height('5%')
270        .onClick(() => { // 点击后下滑指定距离150.0vp
271          this.scroller.scrollBy(0, 150)
272        })
273        .margin({ top: 10, left: 20 })
274      Button('scroll 100')
275        .height('5%')
276        .onClick(() => { // 点击后滑动到指定位置,即下滑100.0vp的距离
277          const yOffset: number = this.scroller.currentOffset().yOffset;
278          this.scroller.scrollTo({ xOffset: 0, yOffset: yOffset + 100 })
279        })
280        .margin({ top: 60, left: 20 })
281      Button('scroll 100')
282        .height('5%')
283        .onClick(() => { // 点击后滑动到指定位置,即下滑100.0vp的距离,滑动过程配置有动画
284          let curve = Curves.interpolatingSpring(10, 1, 228, 30) //创建一个阶梯曲线
285          const yOffset: number = this.scroller.currentOffset().yOffset;
286          this.scroller.scrollTo({ xOffset: 0, yOffset: yOffset + 100, animation: { duration: 1000, curve: curve } })
287        })
288        .margin({ top: 110, left: 20 })
289      Button('back top')
290        .height('5%')
291        .onClick(() => { // 点击后回到顶部
292          this.scroller.scrollEdge(Edge.Top)
293        })
294        .margin({ top: 160, left: 20 })
295      Button('next page')
296        .height('5%')
297        .onClick(() => { // 点击后滑到下一页
298          this.scroller.scrollPage({ next: true })
299        })
300        .margin({ top: 210, left: 20 })
301    }.width('100%').height('100%').backgroundColor(0xDCDCDC)
302  }
303}
304```
305
306![zh-cn_image_0000001174104386](figures/zh-cn_image_0000001174104386.gif)
307
308### 示例2
309```ts
310@Entry
311@Component
312struct NestedScroll {
313  @State listPosition: number = 0; // 0代表滚动到List顶部,1代表中间值,2代表滚动到List底部。
314  private arr: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
315  private scrollerForScroll: Scroller = new Scroller()
316  private scrollerForList: Scroller = new Scroller()
317
318  build() {
319    Flex() {
320      Scroll(this.scrollerForScroll) {
321        Column() {
322          Text("Scroll Area")
323            .width("100%")
324            .height("40%")
325            .backgroundColor(0X330000FF)
326            .fontSize(16)
327            .textAlign(TextAlign.Center)
328            .onClick(() => {
329              this.scrollerForList.scrollToIndex(5)
330            })
331
332          List({ space: 20, scroller: this.scrollerForList }) {
333            ForEach(this.arr, (item: number) => {
334              ListItem() {
335                Text("ListItem" + item)
336                  .width("100%")
337                  .height("100%")
338                  .borderRadius(15)
339                  .fontSize(16)
340                  .textAlign(TextAlign.Center)
341                  .backgroundColor(Color.White)
342              }.width("100%").height(100)
343            }, (item: string) => item)
344          }
345          .width("100%")
346          .height("50%")
347          .edgeEffect(EdgeEffect.None)
348          .friction(0.6)
349          .onReachStart(() => {
350            this.listPosition = 0
351          })
352          .onReachEnd(() => {
353            this.listPosition = 2
354          })
355          .onScrollFrameBegin((offset: number) => {
356            if ((this.listPosition == 0 && offset <= 0) || (this.listPosition == 2 && offset >= 0)) {
357              this.scrollerForScroll.scrollBy(0, offset)
358              return { offsetRemain: 0 }
359            }
360            this.listPosition = 1
361            return { offsetRemain: offset };
362          })
363
364          Text("Scroll Area")
365            .width("100%")
366            .height("40%")
367            .backgroundColor(0X330000FF)
368            .fontSize(16)
369            .textAlign(TextAlign.Center)
370        }
371      }
372      .width("100%").height("100%")
373    }.width('100%').height('100%').backgroundColor(0xDCDCDC).padding(20)
374  }
375}
376```
377
378![NestedScroll](figures/NestedScroll.gif)
379
380### 示例3
381```ts
382@Entry
383@Component
384struct StickyNestedScroll {
385  @State arr: number[] = []
386
387  @Styles
388  listCard() {
389    .backgroundColor(Color.White)
390    .height(72)
391    .width("100%")
392    .borderRadius(12)
393  }
394
395  build() {
396    Scroll() {
397      Column() {
398        Text("Scroll Area")
399          .width("100%")
400          .height("40%")
401          .backgroundColor('#0080DC')
402          .textAlign(TextAlign.Center)
403        Tabs({ barPosition: BarPosition.Start }) {
404          TabContent() {
405            List({ space: 10 }) {
406              ForEach(this.arr, (item: number) => {
407                ListItem() {
408                  Text("item" + item)
409                    .fontSize(16)
410                }.listCard()
411              }, (item: string) => item)
412            }.width("100%")
413            .edgeEffect(EdgeEffect.Spring)
414            .nestedScroll({
415              scrollForward: NestedScrollMode.PARENT_FIRST,
416              scrollBackward: NestedScrollMode.SELF_FIRST
417            })
418          }.tabBar("Tab1")
419
420          TabContent() {
421          }.tabBar("Tab2")
422        }
423        .vertical(false)
424        .height("100%")
425      }.width("100%")
426    }
427    .edgeEffect(EdgeEffect.Spring)
428    .friction(0.6)
429    .backgroundColor('#DCDCDC')
430    .scrollBar(BarState.Off)
431    .width('100%')
432    .height('100%')
433  }
434
435  aboutToAppear() {
436    for (let i = 0; i < 30; i++) {
437      this.arr.push(i)
438    }
439  }
440}
441```
442![NestedScroll2](figures/NestedScroll2.gif)
443### 示例4
444```ts
445@Entry
446@Component
447struct Index {
448  scroller: Scroller = new Scroller;
449  private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]
450  build() {
451    Scroll(this.scroller) {
452      Column() {
453        ForEach(this.arr, (item: number) => {
454          Text(item.toString())
455            .width('90%')
456            .height(200)
457            .backgroundColor(0xFFFFFF)
458            .borderWidth(1)
459            .borderColor(Color.Black)
460            .borderRadius(15)
461            .fontSize(16)
462            .textAlign(TextAlign.Center)
463        }, (item: string) => item)
464      }.width('100%').backgroundColor(0xDCDCDC)
465    }
466    .backgroundColor(Color.Yellow)
467    .height('100%')
468    .edgeEffect(EdgeEffect.Spring)
469    .scrollSnap({snapAlign:ScrollSnapAlign.START, snapPagination:400, enableSnapToStart:true, enableSnapToEnd:true})
470  }
471}
472```
473![NestedScrollSnap](figures/NestedScrollSnap.gif)
474