• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Scroll
2
3The **\<Scroll>** component scrolls the content when the layout size of a component exceeds the size of its parent component.
4
5>  **NOTE**
6>  - This component is supported since API version 7. Updates will be marked with a superscript to indicate their earliest API version.
7>  - When nesting a **\<List>** within this component, specify the width and height for the **\<List>** under scenarios where consistently high performance is required. If the width and height are not specified, this component will load all content of the **\<List>**.
8>  - This component can scroll only when the size on the main axis is less than the content size.
9>  - This component can produce a bounce effect only when there is more than one screen of content.
10
11
12## Child Components
13
14This component supports only one child component.
15
16
17## APIs
18
19Scroll(scroller?: Scroller)
20
21**Parameters**
22
23| Name| Type| Mandatory| Description|
24| -------- | -------- | -------- | -------- |
25| scroller | [Scroller](#scroller) | No| Scroller, which can be bound to scrollable components.|
26
27## Attributes
28
29In addition to the [universal attributes](ts-universal-attributes-size.md), the following attributes are supported.
30
31| Name            | Type                                    | Description       |
32| -------------- | ---------------------------------------- | --------- |
33| scrollable     | [ScrollDirection](#scrolldirection)                        | Scroll direction.<br>Default value: **ScrollDirection.Vertical**|
34| scrollBar      | [BarState](ts-appendix-enums.md#barstate) | Scrollbar status.<br>Default value: **BarState.Auto**<br>**NOTE**<br>If the container component cannot be scrolled, the scrollbar is not displayed. If the size of a child component of a container component is infinite, the scrollbar cannot be dragged or scrolled with the child component.|
35| scrollBarColor | string \| number \| [Color](ts-appendix-enums.md#color)   | Color of the scrollbar.|
36| scrollBarWidth | string \| number         | Width of the scrollbar. This attribute cannot be set in percentage.<br>Default value: **4**<br>Unit: vp<br>**NOTE**<br>If the width of the scrollbar exceeds its height, it will change to the default value.|
37| edgeEffect     | [EdgeEffect](ts-appendix-enums.md#edgeeffect)            | Scroll effect. For details, see **EdgeEffect**.<br>Default value: **EdgeEffect.None**|
38| enableScrollInteraction<sup>10+</sup>  |  boolean  |   Whether to support scroll gestures. When this attribute is set to **false**, scrolling by finger or mouse is not supported, but the scrolling controller API is not affected.<br>Default value: **true**     |
39| nestedScroll<sup>10+</sup>                 | [NestedScrollOptions](#nestedscrolloptions10)         | Nested scrolling options. You can set the nested scrolling mode in the forward and backward directions to implement scrolling linkage with the parent component.|
40| friction<sup>10+</sup> | number \| [Resource](ts-types.md#resource)    | Friction coefficient. It applies only to gestures in the scrolling area, and it affects only indirectly the scroll chaining during the inertial scrolling process.<br>Default value: **0.9** for wearable devices and **0.6** for non-wearable devices<br>**NOTE**<br>A value less than or equal to 0 evaluates to the default value.|
41
42## ScrollDirection
43| Name      | Description                    |
44| ---------- | ------------------------ |
45| Horizontal | Only horizontal scrolling is supported.    |
46| Vertical   | Only vertical scrolling is supported.    |
47| None       | Scrolling is disabled.              |
48| Free<sup>(deprecated) </sup> | Vertical or horizontal scrolling is supported.<br> This API is deprecated since API version 9.|
49
50## Events
51
52| Name                                                        | Description                                                    |
53| ------------------------------------------------------------ | ------------------------------------------------------------ |
54| onScrollFrameBegin<sup>9+</sup>(event: (offset: number, state: ScrollState) => { offsetRemain }) | Triggered when each frame scrolling starts. The input parameters indicate the amount by which the **\<Scroll>** component will scroll. The event handler then works out the amount by which the component needs to scroll based on the real-world situation and returns the result.<br>\- **offset**: amount to scroll by.<br>\- **state**: current scrolling status.<br>- **offsetRemain**: actual amount by which the component scrolls.<br>**NOTE**<br>1. This event is triggered when scrolling is started by the **\<Scroll>** component or other input settings, such as keyboard and mouse operations.<br>2. This event is not triggered when the controller API is called.<br>3. This event does not support the out-of-bounds bounce effect.<br>**NOTE**<br>The value of **offsetRemain** can be a negative value.<br>If the **onScrollFrameBegine** event and **scrollBy** method are used to implement nested scrolling, set the **edgeEffect** attribute of the scrollable child component to **None**. For example, if a **\<List>** is nested in the **\<Scroll>** component, **edgeEffect** of the **\<List>** must be set to **EdgeEffect.None**.|
55| onScroll(event: (xOffset: number, yOffset: number) => void)  | Triggered to return the horizontal and vertical offsets during scrolling when the specified scroll event occurs.<br>**NOTE**<br>1. This event is triggered when scrolling is started by the **\<Scroll>** component or other input settings, such as keyboard and mouse operations.<br>2. This event is triggered when the controller API is called.<br>3. This event supports the out-of-bounds bounce effect.|
56| onScrollEdge(event: (side: Edge) => void)                    | Triggered when scrolling reaches the edge.<br>**NOTE**<br>1. This event is triggered when scrolling reaches the edge after being started by the **\<Scroll>** component or other input settings, such as keyboard and mouse operations.<br>2. This event is triggered when the controller API is called.<br>3. This event supports the out-of-bounds bounce effect.|
57| onScrollEnd<sup>(deprecated) </sup>(event: () => void)       | Triggered when scrolling stops.<br>This event is deprecated since API version 9. Use the **onScrollStop** event instead.<br>**NOTE**<br>1. This event is triggered when scrolling is stopped by the **\<Scroll>** component or other input settings, such as keyboard and mouse operations.<br>2. This event is triggered when the controller API is called, accompanied by a transition animation.|
58| onScrollStart<sup>9+</sup>(event: () => void)                | Triggered when scrolling starts and is initiated by the user's finger dragging the **\<Scroll>** component or its scrollbar. This event is also triggered when the animation contained in the scrolling triggered by [Scroller](#scroller) starts.<br>**NOTE**<br>1. This event is triggered when scrolling is started by the **\<Scroll>** component or other input settings, such as keyboard and mouse operations.<br>2. This event is triggered when the controller API is called, accompanied by a transition animation.|
59| onScrollStop<sup>9+</sup>(event: () => void)                 | Triggered when scrolling stops after the user's finger leaves the screen. This event is also triggered when the animation contained in the scrolling triggered by [Scroller](#scroller) stops.<br>**NOTE**<br>1. This event is triggered when scrolling is stopped by the **\<Scroll>** component or other input settings, such as keyboard and mouse operations.<br>2. This event is triggered when the controller API is called, accompanied by a transition animation.|
60
61>  **NOTE**
62>
63>  If the **onScrollFrameBegin** event and **scrollBy** method are used to implement nested scrolling, set the **edgeEffect** attribute of the scrollable child component to **None**. For example, if a **\<List>** is nested in the **\<Scroll>** component, **edgeEffect** of the **\<List>** must be set to **EdgeEffect.None**.
64
65## Scroller
66
67Implements a controller for a scrollable container component. You can bind this component to a container component and use it to control the scrolling of that component. One controller can control only one container component. The supported container components are **\<List>**, **\<Scroll>**, **\<ScrollBar>**, **\<Grid>**, and **\<WaterFlow>**.
68
69
70### Objects to Import
71
72```
73scroller: Scroller = new Scroller()
74```
75
76
77### scrollTo
78
79scrollTo(value: { xOffset: number | string, yOffset: number | string, animation?: { duration?: number, curve?: Curve | ICurve } | boolean }): void
80
81
82Scrolls to the specified position.
83
84**Parameters**
85
86| Name   | Type                                                    | Mandatory| Description                                                    |
87| --------- | ------------------------------------------------------------ | ---- | ------------------------------------------------------------ |
88| xOffset   | number \| string                                   | Yes  | Horizontal scrolling offset.<br>**NOTE**<br>This parameter cannot be set in percentage.<br>If the value is less than 0, no operation is performed. That is, this parameter does not take effect.<br>This parameter is valid only when the scroll axis is the x-axis.|
89| yOffset   | number \| string                                   | Yes  | Vertical scrolling offset.<br>**NOTE**<br>This parameter cannot be set in percentage.<br>If the value is less than 0, no operation is performed. That is, this parameter does not take effect.<br>This parameter is valid only when the scroll axis is the y-axis.|
90| animation | {duration?: number, curve?: [Curve](ts-appendix-enums.md#curve) \| [ICurve](../apis/js-apis-curve.md#icurve)<sup>10+ </sup>} \| boolean<sup>10+ </sup> | No  | Animation configuration, which includes the following:<br>- **duration**: scrolling duration.<br>- **curve**: scrolling curve.<br>- **boolean**: whether to enable the default spring animation.<br>Default value:<br>{<br>duration: 1000,<br>curve: Curve.Ease<br>}<br>boolean: false<br>**NOTE**<br>A value less than 0 evaluates to the default value.<br>Currently, the **\<List>**, **\<Scroll>**, **\<Grid>**, and **\<WaterFlow>** support the **Boolean** type and **ICurve**.|
91
92
93### scrollEdge
94
95scrollEdge(value: Edge): void
96
97
98Scrolls to the edge of the container, regardless of the scroll axis direction. **Edge.Top** and **Edge.Start** produce the same effect, and **Edge.Bottom** and **Edge.End** produce the same effect.
99
100**Parameters**
101
102| Name  | Type| Mandatory  | Description     |
103| ----- | ---- | ---- | --------- |
104| value | [Edge](ts-appendix-enums.md#edge) | Yes   | Edge position to scroll to.|
105
106
107### scrollPage
108
109scrollPage(value: { next: boolean, direction?: Axis }): void
110
111Scrolls to the next or previous page.
112
113**Parameters**
114
115| Name      | Type   | Mandatory  | Description                          |
116| --------- | ------- | ---- | ------------------------------ |
117| next      | boolean | Yes   | Whether to turn to the next page. The value **true** means to scroll to the next page, and **false** means to scroll to the previous page.|
118| direction<sup>(deprecated) </sup> | [Axis](ts-appendix-enums.md#axis)    | No   | Scrolling direction: horizontal or vertical.<br> This API is deprecated since API version 9.               |
119
120
121### currentOffset
122
123currentOffset(): { xOffset: number, yOffset: number }
124
125
126Obtains the scrolling offset.
127
128**Return value**
129
130| Type                                                      | Description                                                        |
131| ---------------------------------------------------------- | ------------------------------------------------------------ |
132| {<br>xOffset: number,<br>yOffset: number<br>} | **xOffset**: horizontal scrolling offset.<br>**yOffset**: vertical scrolling offset.<br>**NOTE**<br>The unit of the return value is vp.|
133
134
135### scrollToIndex
136
137scrollToIndex(value: number, smooth?: boolean, align?: ScrollAlign): void
138
139Scrolls to the item with the specified index.
140
141When **smooth** is set to **true**, all passed items are loaded and counted in layout calculation. This may result in performance issues if a large number of items are involved.
142
143
144>  **NOTE**
145>
146>  This API only works for the **\<Grid>**, **\<List>**, and **\<WaterFlow>** components.
147
148**Parameters**
149
150| Name               | Type| Mandatory| Description                                                    |
151| --------------------- | -------- | ---- | ------------------------------------------------------------ |
152| value                 | number   | Yes  | Index of the item to be scrolled to in the container.<br>**NOTE**<br>If the value set is a negative value or greater than the maximum index of the items in the container, the value is deemed abnormal, and no scrolling will be performed.                    |
153| smooth<sup>10+ </sup> | boolean  | No  | Whether to enable the smooth animation for scrolling to the item with the specified index. The value **true** means to enable that the smooth animation, and **false** means the opposite.<br>Default value: **false**<br>**NOTE**<br>Currently, only the **\<List>** component supports this parameter.|
154| align<sup>10+ </sup> | [ScrollAlign](#scrollalign10)  | No  | How the list item to scroll to is aligned with the container.<br>Default value when the container is **\<List>**: **ScrollAlign.START**<br> Default value when the container is **\<Grid>**: **ScrollAlign.AUTO**<br>**NOTE**<br>Currently, only the **\<List>** and **\<Grid>** components support this parameter.|
155
156### scrollBy<sup>9+</sup>
157
158scrollBy(dx: Length, dy: Length): void
159
160
161Scrolls by the specified amount.
162
163
164>  **NOTE**
165>
166>  This API only works for the **\<Scroll>**, **\<ScrollBar>**, **\<Grid>**, and **\<List>** components.
167
168**Parameters**
169
170| Name  | Type  | Mandatory  | Description             |
171| ----- | ------ | ---- | ----------------- |
172| dx | Length | Yes   | Amount to scroll by in the horizontal direction. The percentage format is not supported.|
173| dy | Length | Yes   | Amount to scroll by in the vertical direction. The percentage format is not supported.|
174
175### isAtEnd<sup>10+</sup>
176
177isAtEnd(): boolean
178
179Checks whether the component has scrolled to the bottom.
180
181>  **NOTE**
182>
183>  This API is available for the **\<Scroll>**, **\<List>**, **\<Grid>**, and **\<WaterFlow>** components.
184
185**Return value**
186
187| Type        | Description         |
188| ------- | -------- |
189| boolean | The value **true** means that the component has scrolled to the bottom, and **false** means the opposite.|
190
191## ScrollAlign<sup>10+ </sup>
192
193| Name    | Description                            |
194| ------ | ------------------------------ |
195| START   | The start edge of the list item is flush with the start edge of the list. |
196| CENTER | The list item is centered along the main axis of the list.       |
197| END  | The end edge of the list item is flush with the end edge of the list.|
198| AUTO  | The list item is automatically aligned.<br>If the list item is fully contained within the display area, no adjustment is performed. Otherwise, the list item is aligned so that its start or end edge is flush with the start or end edge of the list, whichever requires a shorter scrolling distance.|
199
200## NestedScrollOptions<sup>10+ </sup>
201| Name  | Type  | Description             |
202| ----- | ------ | ----------------- |
203| scrollForward | NestedScrollMode | Nested scrolling option when the component scrolls forward.|
204| scrollBackward | NestedScrollMode |  Nested scrolling option when the component scrolls backward.|
205
206## NestedScrollMode<sup>10+ </sup>
207| Name    | Description                            |
208| ------ | ------------------------------ |
209| SELF_ONLY   | The scrolling is contained within the component, and no scroll chaining occurs, that is, the parent component does not scroll when the component scrolling reaches the boundary. |
210| SELF_FIRST | The component scrolls first, and when it hits the boundary, the parent component scrolls. When the parent component hits the boundary, its edge effect is displayed. If no edge effect is specified for the parent component, the edge effect of the child component is displayed instead.       |
211| PARENT_FIRST  | The parent component scrolls first, and when it hits the boundary, the component scrolls. When the component hits the boundary, its edge effect is displayed. If no edge effect is specified for the component, the edge effect of the parent component is displayed instead.|
212| PARALLEL  | The component and its parent component scroll at the same time. When both the component and its parent component hit the boundary, the edge effect of the component is displayed. If no edge effect is specified for the component, the edge effect of the parent component is displayed instead.|
213
214## Example
215### Example 1
216
217```ts
218// xxx.ets
219import Curves from '@ohos.curves'
220
221@Entry
222@Component
223struct ScrollExample {
224  scroller: Scroller = new Scroller()
225  private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
226
227  build() {
228    Stack({ alignContent: Alignment.TopStart }) {
229      Scroll(this.scroller) {
230        Column() {
231          ForEach(this.arr, (item: number) => {
232            Text(item.toString())
233              .width('90%')
234              .height(150)
235              .backgroundColor(0xFFFFFF)
236              .borderRadius(15)
237              .fontSize(16)
238              .textAlign(TextAlign.Center)
239              .margin({ top: 10 })
240          }, (item: string) => item)
241        }.width('100%')
242      }
243      .scrollable(ScrollDirection.Vertical) // The scrollbar scrolls in the vertical direction.
244      .scrollBar(BarState.On) // The scrollbar is always displayed.
245      .scrollBarColor(Color.Gray) // The scrollbar color is gray.
246      .scrollBarWidth(10) // The scrollbar width is 10.
247      .friction(0.6)
248      .edgeEffect(EdgeEffect.None)
249      .onScroll((xOffset: number, yOffset: number) => {
250        console.info(xOffset + ' ' + yOffset)
251      })
252      .onScrollEdge((side: Edge) => {
253        console.info('To the edge')
254      })
255      .onScrollEnd(() => {
256        console.info('Scroll Stop')
257      })
258
259      Button('scroll 150')
260        .height('5%')
261        .onClick(() => { // Click to scroll down to 150.0 vp.
262          this.scroller.scrollBy(0, 150)
263        })
264        .margin({ top: 10, left: 20 })
265      Button('scroll 100')
266        .height('5%')
267        .onClick(() => { // Click to scroll down by 100.0 vp.
268          const yOffset: number = this.scroller.currentOffset().yOffset;
269          this.scroller.scrollTo({ xOffset: 0, yOffset: yOffset + 100 })
270        })
271        .margin({ top: 60, left: 20 })
272      Button('scroll 100')
273        .height('5%')
274        .onClick(() => {// Click to scroll down by 100.0 vp. An animation is applied to the scrolling.
275          let curve = Curves.interpolatingSpring(100, 1, 228, 30) // Create a step curve.
276          const yOffset: number = this.scroller.currentOffset().yOffset;
277          this.scroller.scrollTo({ xOffset: 0, yOffset: yOffset + 100, animation: { duration: 1000, curve: curve } })
278        })
279        .margin({ top: 110, left: 20 })
280      Button('back top')
281        .height('5%')
282        .onClick(() => { // Click to go back to the top.
283          this.scroller.scrollEdge(Edge.Top)
284        })
285        .margin({ top: 160, left: 20 })
286      Button('next page')
287        .height('5%')
288        .onClick(() => { // Click to go to the next page.
289          this.scroller.scrollPage({ next: true })
290        })
291        .margin({ top: 210, left: 20 })
292    }.width('100%').height('100%').backgroundColor(0xDCDCDC)
293  }
294}
295```
296
297![en-us_image_0000001174104386](figures/en-us_image_0000001174104386.gif)
298
299### Example 2
300```ts
301@Entry
302@Component
303struct NestedScroll {
304  @State listPosition: number = 0; // 0 indicates scrolling to the top of the list, 1 indicates scrolling to the middle of the list, and 2 indicates scrolling to the bottom of the list.
305  private arr: number[] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
306  private scrollerForScroll: Scroller = new Scroller()
307  private scrollerForList: Scroller = new Scroller()
308
309  build() {
310    Flex() {
311      Scroll(this.scrollerForScroll) {
312        Column() {
313          Text("Scroll Area")
314            .width("100%")
315            .height("40%")
316            .backgroundColor(0X330000FF)
317            .fontSize(16)
318            .textAlign(TextAlign.Center)
319            .onClick(() => {
320              this.scrollerForList.scrollToIndex(5)
321            })
322
323          List({ space: 20, scroller: this.scrollerForList }) {
324            ForEach(this.arr, (item: number) => {
325              ListItem() {
326                Text("ListItem" + item)
327                  .width("100%")
328                  .height("100%")
329                  .borderRadius(15)
330                  .fontSize(16)
331                  .textAlign(TextAlign.Center)
332                  .backgroundColor(Color.White)
333              }.width("100%").height(100)
334            }, (item: string) => item)
335          }
336          .width("100%")
337          .height("50%")
338          .edgeEffect(EdgeEffect.None)
339          .friction(0.6)
340          .onReachStart(() => {
341            this.listPosition = 0
342          })
343          .onReachEnd(() => {
344            this.listPosition = 2
345          })
346          .onScrollFrameBegin((offset: number) => {
347            if ((this.listPosition == 0 && offset <= 0) || (this.listPosition == 2 && offset >= 0)) {
348              this.scrollerForScroll.scrollBy(0, offset)
349              return { offsetRemain: 0 }
350            }
351            this.listPosition = 1
352            return { offsetRemain: offset };
353          })
354
355          Text("Scroll Area")
356            .width("100%")
357            .height("40%")
358            .backgroundColor(0X330000FF)
359            .fontSize(16)
360            .textAlign(TextAlign.Center)
361        }
362      }
363      .width("100%").height("100%")
364    }.width('100%').height('100%').backgroundColor(0xDCDCDC).padding(20)
365  }
366}
367```
368
369![NestedScroll](figures/NestedScroll.gif)
370
371### Example 3
372```ts
373@Entry
374@Component
375struct StickyNestedScroll {
376  @State message: string = 'Hello World'
377  @State arr: number[] = []
378
379  @Styles
380  listCard() {
381    .backgroundColor(Color.White)
382    .height(72)
383    .width("100%")
384    .borderRadius(12)
385  }
386
387  build() {
388    Scroll() {
389      Column() {
390        Text("Scroll Area")
391          .width("100%")
392          .height("40%")
393          .backgroundColor('#0080DC')
394          .textAlign(TextAlign.Center)
395        Tabs({ barPosition: BarPosition.Start }) {
396          TabContent() {
397            List({ space: 10 }) {
398              ForEach(this.arr, (item: number) => {
399                ListItem() {
400                  Text("item" + item)
401                    .fontSize(16)
402                }.listCard()
403              }, (item: string) => item)
404            }.width("100%")
405            .edgeEffect(EdgeEffect.Spring)
406            .nestedScroll({
407              scrollForward: NestedScrollMode.PARENT_FIRST,
408              scrollBackward: NestedScrollMode.SELF_FIRST
409            })
410          }.tabBar("Tab1")
411
412          TabContent() {
413          }.tabBar("Tab2")
414        }
415        .vertical(false)
416        .height("100%")
417      }.width("100%")
418    }
419    .edgeEffect(EdgeEffect.Spring)
420    .friction(0.6)
421    .backgroundColor('#DCDCDC')
422    .scrollBar(BarState.Off)
423    .width('100%')
424    .height('100%')
425  }
426
427  aboutToAppear() {
428    for (let i = 0; i < 30; i++) {
429      this.arr.push(i)
430    }
431  }
432}
433```
434![NestedScroll2](figures/NestedScroll2.gif)
435