• 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| edgeEffect     | [EdgeEffect](ts-appendix-enums.md#edgeeffect)            | 设置滑动效果,目前支持的滑动效果参见EdgeEffect的枚举说明。<br/>默认值:EdgeEffect.None |
38| enableScrollInteraction<sup>10+</sup>  |  boolean  |   设置是否支持滚动手势,当设置为false时,无法通过手指或者鼠标滚动,但不影响控制器的滚动接口。<br/>默认值:true      |
39| nestedScroll<sup>10+</sup>                 | [NestedScrollOptions](#nestedscrolloptions10对象说明)         | 嵌套滚动选项。设置向前向后两个方向上的嵌套滚动模式,实现与父组件的滚动联动。 |
40| friction<sup>10+</sup> | number \| [Resource](ts-types.md#resource)    | 设置摩擦系数,手动划动滚动区域时生效,只对惯性滚动过程有影响,对惯性滚动过程中的链式效果有间接影响。<br/>默认值:非可穿戴设备为0.6,可穿戴设备为0.9<br/>**说明:** <br/>设置为小于等于0的值时,按默认值处理 |
41
42## ScrollDirection枚举说明
43| 名称       | 描述                     |
44| ---------- | ------------------------ |
45| Horizontal | 仅支持水平方向滚动。     |
46| Vertical   | 仅支持竖直方向滚动。     |
47| None       | 不可滚动。               |
48| Free<sup>(deprecated) </sup> | 支持竖直或水平方向滚动<br/> 从API version 9开始废弃|
49
50## 事件
51
52| 名称                                                         | 功能描述                                                     |
53| ------------------------------------------------------------ | ------------------------------------------------------------ |
54| onScrollFrameBegin<sup>9+</sup>(event: (offset: number, state: ScrollState) => { offsetRemain }) | 每帧开始滚动时触发,事件参数传入即将发生的滚动量,事件处理函数中可根据应用场景计算实际需要的滚动量并作为事件处理函数的返回值返回,Scroll将按照返回值的实际滚动量进行滚动。<br/>\- offset:即将发生的滚动量。<br/>\- state:当前滚动状态。<br/>- offsetRemain:实际滚动量。<br/>触发该事件的条件 :<br/>1、滚动组件触发滚动时触发,包括键鼠操作等其他触发滚动的输入设置。<br/>2、调用控制器接口时不触发。<br/>3、越界回弹不触发。<br/>**说明:** <br/>支持offsetRemain为负值。<br/>若通过onScrollFrameBegine事件和scrollBy方法实现容器嵌套滚动,需设置子滚动节点的EdgeEffect为None。如Scroll嵌套List滚动时,List组件的edgeEffect属性需设置为EdgeEffect.None。 |
55| onScroll(event: (xOffset: number, yOffset: number) => void)  | 滚动事件回调,&nbsp;返回滚动时水平、竖直方向偏移量。<br/>触发该事件的条件 :<br/>1、滚动组件触发滚动时触发,支持键鼠操作等其他触发滚动的输入设置。<br/>2、通过滚动控制器API接口调用。<br/>3、越界回弹。 |
56| onScrollEdge(event: (side: Edge) => void)                    | 滚动到边缘事件回调。<br/>触发该事件的条件 :<br/>1、滚动组件滚动到边缘时触发,支持键鼠操作等其他触发滚动的输入设置。<br/>2、通过滚动控制器API接口调用。<br/>3、越界回弹。 |
57| onScrollEnd<sup>(deprecated) </sup>(event: () => void)       | 滚动停止事件回调。<br>该事件从API version 9开始废弃,使用onScrollStop事件替代。<br/>触发该事件的条件 :<br/>1、滚动组件触发滚动后停止,支持键鼠操作等其他触发滚动的输入设置。<br/>2、通过滚动控制器API接口调用后停止,带过渡动效。 |
58| onScrollStart<sup>9+</sup>(event: () => void)                | 滚动开始时触发。手指拖动Scroll或拖动Scroll的滚动条触发的滚动开始时,会触发该事件。使用[Scroller](#scroller)滚动控制器触发的带动画的滚动,动画开始时会触发该事件。<br/>触发该事件的条件 :<br/>1、滚动组件开始滚动时触发,支持键鼠操作等其他触发滚动的输入设置。<br/>2、通过滚动控制器API接口调用后开始,带过渡动效。 |
59| onScrollStop<sup>9+</sup>(event: () => void)                 | 滚动停止时触发。手拖动Scroll或拖动Scroll的滚动条触发的滚动,手离开屏幕并且滚动停止时会触发该事件。使用[Scroller](#scroller)滚动控制器触发的带动画的滚动,动画停止时会触发该事件。<br/>触发该事件的条件 :<br/>1、滚动组件触发滚动后停止,支持键鼠操作等其他触发滚动的输入设置。<br/>2、通过滚动控制器API接口调用后开始,带过渡动效。 |
60
61>  **说明:**
62>
63>  若通过onScrollFrameBegin事件和scrollBy方法实现容器嵌套滚动,需设置子滚动节点的EdgeEffect为None。如Scroll嵌套List滚动时,List组件的edgeEffect属性需设置为EdgeEffect.None64
65## Scroller
66
67可滚动容器组件的控制器,可以将此组件绑定至容器组件,然后通过它控制容器组件的滚动,同一个控制器不可以控制多个容器组件,目前支持绑定到List、Scroll、ScrollBar、Grid、WaterFlow上。
68
69
70### 导入对象
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
82滑动到指定位置。
83
84**参数:**
85
86| 参数名    | 参数类型                                                     | 必填 | 参数描述                                                     |
87| --------- | ------------------------------------------------------------ | ---- | ------------------------------------------------------------ |
88| xOffset   | number&nbsp;\|&nbsp;string                                   | 是   | 水平滑动偏移。<br/>**说明:** <br/>该参数值不支持设置百分比。<br/>当值小于0时,不做操作,即该参数不生效。<br/>仅滚动轴为x轴时生效。 |
89| yOffset   | number&nbsp;\|&nbsp;string                                   | 是   | 垂直滑动偏移。<br/>**说明:** <br/>该参数值不支持设置百分比。<br/>当值小于0时,不做操作,即该参数不生效。<br/>仅滚动轴为y轴时生效。 |
90| 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曲线。 |
91
92
93### scrollEdge
94
95scrollEdge(value: Edge): void
96
97
98滚动到容器边缘,不区分滚动轴方向,Edge.TopEdge.Start表现相同,Edge.BottomEdge.End表现相同。
99
100**参数:**
101
102| 参数名   | 参数类型 | 必填   | 参数描述      |
103| ----- | ---- | ---- | --------- |
104| value | [Edge](ts-appendix-enums.md#edge) | 是    | 滚动到的边缘位置。 |
105
106
107### scrollPage
108
109scrollPage(value: { next: boolean, direction?: Axis }): void
110
111滚动到下一页或者上一页。
112
113**参数:**
114
115| 参数名       | 参数类型    | 必填   | 参数描述                           |
116| --------- | ------- | ---- | ------------------------------ |
117| next      | boolean | 是    | 是否向下翻页。true表示向下翻页,false表示向上翻页。 |
118| direction<sup>(deprecated) </sup> | [Axis](ts-appendix-enums.md#axis)    | 否    | 设置滚动方向为水平或竖直方向。<br/> 从API version 9开始废弃                |
119
120
121### currentOffset
122
123currentOffset(): { xOffset: number, yOffset: number }
124
125
126返回当前的滚动偏移量。
127
128**返回值**
129
130| 类型                                                       | 描述                                                         |
131| ---------------------------------------------------------- | ------------------------------------------------------------ |
132| {<br/>xOffset:&nbsp;number,<br/>yOffset:&nbsp;number<br/>} | xOffset:&nbsp;水平滑动偏移;<br/>yOffset:&nbsp;竖直滑动偏移。<br/>**说明:** <br/>返回值单位为vp。 |
133
134
135### scrollToIndex
136
137scrollToIndex(value: number, smooth?: boolean, align?: ScrollAlign): void
138
139滑动到指定Index。
140
141开启smooth动效时,会对经过的所有item进行加载和布局计算,当大量加载item时会导致性能问题。
142
143
144>  **说明:**
145>
146>  仅支持Grid、List、WaterFlow组件。
147
148**参数:**
149
150| 参数名                | 参数类型 | 必填 | 参数描述                                                     |
151| --------------------- | -------- | ---- | ------------------------------------------------------------ |
152| value                 | number   | 是   | 要滑动到的目标元素在当前容器中的索引值。      <br/>**说明:** <br/>value值设置成负值或者大于当前容器子组件的最大索引值,视为异常值,本次跳转不生效。                     |
153| smooth<sup>10+ </sup> | boolean  | 否   | 设置滑动到列表项在列表中的索引值时是否有动效,true表示有动效,false表示没有动效。<br/>默认值:false。<br/>**说明:** <br/>当前仅List组件支持该参数。 |
154| align<sup>10+ </sup> | [ScrollAlign](#scrollalign枚举说明)  | 否   | 指定滑动到的元素与当前容器的对齐方式。<br/>List中的默认值为:ScrollAlign.START。Grid中默认值为:ScrollAlign.AUTO<br/>**说明:** <br/>当前仅List、Grid组件支持该参数。 |
155
156### scrollBy<sup>9+</sup>
157
158scrollBy(dx: Length, dy: Length): void
159
160
161滑动指定距离。
162
163
164>  **说明:**
165>
166>  仅支持Scroll、ScrollBar、Grid、List组件。
167
168**参数:**
169
170| 参数名   | 参数类型   | 必填   | 参数描述              |
171| ----- | ------ | ---- | ----------------- |
172| dx | Length | 是    | 水平方向滚动距离,不支持百分比形式。 |
173| dy | Length | 是    | 竖直方向滚动距离,不支持百分比形式。 |
174
175### isAtEnd<sup>10+</sup>
176
177isAtEnd(): boolean
178
179查询组件是否滚动到底部。
180
181>  **说明:**
182>
183>  支持Scroll、List、Grid、WaterFlow组件。
184
185**返回值**
186
187| 类型         | 描述          |
188| ------- | -------- |
189| boolean | true表示组件已经滚动到底部,false表示组件还没滚动到底部。 |
190
191## ScrollAlign枚举说明<sup>10+ </sup>
192
193| 名称     | 描述                             |
194| ------ | ------------------------------ |
195| START   | 首部对齐。指定item首部与List首部对齐。  |
196| CENTER | 居中对齐。指定item主轴方向居中对齐于List。        |
197| END  | 尾部对齐。指定item尾部与List尾部对齐。 |
198| AUTO  | 自动对齐。<br/>若指定item完全处于显示区,不做调整。否则依照滑动距离最短的原则,将指定item首部对齐或尾部对齐于List,使指定item完全处于显示区。|
199
200## NestedScrollOptions<sup>10+ </sup>对象说明
201| 名称   | 类型   | 描述              |
202| ----- | ------ | ----------------- |
203| scrollForward | NestedScrollMode | 可滚动组件往末尾端滚动时的嵌套滚动选项。 |
204| scrollBackward | NestedScrollMode |  可滚动组件往起始端滚动时的嵌套滚动选项。 |
205
206## NestedScrollMode<sup>10+ </sup>枚举说明
207| 名称     | 描述                             |
208| ------ | ------------------------------ |
209| SELF_ONLY   | 只自身滚动,不与父组件联动。  |
210| SELF_FIRST | 自身先滚动,自身滚动到边缘以后父组件滚动。父组件滚动到边缘以后,如果父组件有边缘效果,则父组件触发边缘效果,否则子组件触发边缘效果。        |
211| PARENT_FIRST  | 父组件先滚动,父组件滚动到边缘以后自身滚动。自身滚动到边缘后,如果有边缘效果,会触发自身的边缘效果,否则触发父组件的边缘效果。 |
212| PARALLEL  | 自身和父组件同时滚动,自身和父组件都到达边缘以后,如果自身有边缘效果,则自身触发边缘效果,否则父组件触发边缘效果。|
213
214## 示例
215### 示例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) // 滚动方向纵向
244      .scrollBar(BarState.On) // 滚动条常驻显示
245      .scrollBarColor(Color.Gray) // 滚动条颜色
246      .scrollBarWidth(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(() => { // 点击后下滑指定距离150.0vp
262          this.scroller.scrollBy(0, 150)
263        })
264        .margin({ top: 10, left: 20 })
265      Button('scroll 100')
266        .height('5%')
267        .onClick(() => { // 点击后滑动到指定位置,即下滑100.0vp的距离
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(() => { // 点击后滑动到指定位置,即下滑100.0vp的距离,滑动过程配置有动画
275          let curve = Curves.interpolatingSpring(100, 1, 228, 30) //创建一个阶梯曲线
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(() => { // 点击后回到顶部
283          this.scroller.scrollEdge(Edge.Top)
284        })
285        .margin({ top: 160, left: 20 })
286      Button('next page')
287        .height('5%')
288        .onClick(() => { // 点击后滑到下一页
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![zh-cn_image_0000001174104386](figures/zh-cn_image_0000001174104386.gif)
298
299### 示例2
300```ts
301@Entry
302@Component
303struct NestedScroll {
304  @State listPosition: number = 0; // 0代表滚动到List顶部,1代表中间值,2代表滚动到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### 示例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