1# WaterFlow
2
3
4瀑布流容器,由“行”和“列”分割的单元格所组成,通过容器自身的排列规则,将不同大小的“项目”自上而下,如瀑布般紧密布局。
5
6
7> **说明:**
8>
9> 该组件从API Version 9 开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。
10
11
12## 子组件
13
14
15仅支持[FlowItem](ts-container-flowitem.md)子组件,支持渲染控制类型([if/else](../../../quick-start/arkts-rendering-control-ifelse.md)、[ForEach](../../../quick-start/arkts-rendering-control-foreach.md)、[LazyForEach](../../../quick-start/arkts-rendering-control-lazyforeach.md)和[Repeat](../../../quick-start/arkts-new-rendering-control-repeat.md))。
16
17>  **说明:**
18>
19>  WaterFlow子组件的visibility属性设置为None时不显示,但该子组件周围的columnsGap、rowsGap、margin仍会生效。
20
21## 接口
22
23WaterFlow(options?:  WaterFlowOptions)
24
25**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
26
27**系统能力:** SystemCapability.ArkUI.ArkUI.Full
28
29**参数:**
30
31| 参数名 | 类型 | 必填 | 说明 |
32| -------- | -------- | -------- | -------- |
33| options |  [WaterFlowOptions](#waterflowoptions对象说明)| 否 | 瀑布流组件参数。 |
34
35
36## WaterFlowOptions对象说明
37
38**系统能力:** SystemCapability.ArkUI.ArkUI.Full
39
40| 名称     | 类型                                        | 必填 | 说明                                     |
41| ---------- | ----------------------------------------------- | ------ | -------------------------------------------- |
42| footer |  [CustomBuilder](ts-types.md#custombuilder8) | 否   | 设置WaterFlow尾部组件。<br/>**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 |
43| scroller | [Scroller](ts-container-scroll.md#scroller) | 否   | 可滚动组件的控制器,与可滚动组件绑定。<br/>**说明:** <br/>不允许和其他滚动类组件,如:[List](ts-container-list.md)、[Grid](ts-container-grid.md)、[Scroll](ts-container-scroll.md)等绑定同一个滚动控制对象。<br/>**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。 |
44| sections<sup>12+</sup> |  [WaterFlowSections](#waterflowsections12) | 否   | 设置FlowItem分组,实现同一个瀑布流组件内部各分组使用不同列数混合布局。<br/>**说明:** <br/>1. 使用分组混合布局时会忽略columnsTemplate和rowsTemplate属性。<br/>2. 使用分组混合布局时不支持单独设置footer,可以使用最后一个分组作为尾部组件。<br/>**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。  |
45| layoutMode<sup>12+</sup> |[WaterFlowLayoutMode](#waterflowlayoutmode12枚举说明) | 否 | 设置WaterFlow的布局模式,根据使用场景选择更切合的模式。<br/>**说明:** <br/>默认值:[ALWAYS_TOP_DOWN](#waterflowlayoutmode12枚举说明)。<br/>**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
46
47
48## WaterFlowSections<sup>12+</sup>
49
50瀑布流分组信息。
51
52### constructor
53
54constructor()
55
56创建一个瀑布流分组。
57
58**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
59
60**系统能力:** SystemCapability.ArkUI.ArkUI.Full
61
62### splice<sup>12+</sup>
63
64splice(start: number, deleteCount?: number, sections?: Array\<SectionOptions\>): boolean
65
66移除或者替换已存在的分组和/或添加新分组。
67
68**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
69
70**系统能力:** SystemCapability.ArkUI.ArkUI.Full
71
72**参数:**
73
74| 参数名   | 类型                            | 必填   | 说明                   |
75| ---- | ----------------------------- | ---- | -------------------- |
76| start | number | 是    | 从0开始计算的索引,会转换为整数,表示要开始改变分组的位置。<br/>**说明:** <br/>1. 如果索引是负数,则从末尾开始计算,使用`start + WaterFlowSections.length()`。<br/>2. 如果 `start < -WaterFlowSections.length()`,则使用0。<br/>3. 如果 `start >= WaterFlowSections.length()`,则在最后添加新分组。 |
77| deleteCount | number | 否    | 表示要从start开始删除的分组数量。<br/>**说明:** <br/>1. 如果省略了deleteCount,或者其值大于或等于由start指定的位置到WaterFlowSections末尾的分组数量,那么从start到WaterFlowSections末尾的所有分组将被删除。<br/>2. 如果deleteCount是0或者负数,则不会删除任何分组。 |
78| sections | Array<[SectionOptions](#sectionoptions12对象说明)> | 否    | 表示要从start开始加入的分组。如果不指定,`splice()`将只从瀑布流中删除分组。 |
79
80**返回值:** 
81
82| 类型                                                         | 说明                                                         |
83| ------------------------------------------------------------ | ------------------------------------------------------------ |
84| boolean | 分组是否修改成功,要加入的分组中有任意分组的itemsCount不是正整数时返回false。 |
85
86
87### push<sup>12+</sup>
88
89push(section: SectionOptions): boolean
90
91将指定分组添加到瀑布流末尾。
92
93**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
94
95**系统能力:** SystemCapability.ArkUI.ArkUI.Full
96
97**参数:**
98
99| 参数名   | 类型                            | 必填   | 说明                   |
100| ---- | ----------------------------- | ---- | -------------------- |
101| section | [SectionOptions](#sectionoptions12对象说明) | 是    | 添加到瀑布流末尾的分组。 |
102
103**返回值:** 
104
105| 类型                                                         | 说明                                                         |
106| ------------------------------------------------------------ | ------------------------------------------------------------ |
107| boolean | 分组是否添加成功,新分组的itemsCount不是正整数时返回false。 |
108
109### update<sup>12+</sup>
110
111update(sectionIndex: number, section: SectionOptions): boolean
112
113修改指定索引分组的配置信息。
114
115**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
116
117**系统能力:** SystemCapability.ArkUI.ArkUI.Full
118
119**参数:**
120
121| 参数名   | 类型                            | 必填   | 说明                   |
122| ---- | ----------------------------- | ---- | -------------------- |
123| sectionIndex | number | 是    | 从0开始计算的索引,会转换为整数,表示要修改的分组的位置。<br/>**说明:** <br/>1. 如果索引是负数,则从末尾开始计算,使用`sectionIndex + WaterFlowSections.length()`。<br/>2. 如果`sectionIndex < -WaterFlowSections.length()`,则使用0。<br/>3. 如果`sectionIndex >= WaterFlowSections.length()`,则在最后添加新分组。 |
124| section | [SectionOptions](#sectionoptions12对象说明) | 是    | 新的分组信息。 |
125
126**返回值:** 
127
128| 类型                                                         | 说明                                                         |
129| ------------------------------------------------------------ | ------------------------------------------------------------ |
130| boolean | 分组是否更新成功,新分组的itemsCount不是正整数时返回false。 |
131
132### values<sup>12+</sup>
133
134values(): Array\<SectionOptions\>
135
136获取瀑布流中所有分组配置信息。
137
138**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
139
140**系统能力:** SystemCapability.ArkUI.ArkUI.Full
141
142**返回值:** 
143
144| 类型                                                         | 说明                                                         |
145| ------------------------------------------------------------ | ------------------------------------------------------------ |
146| Array<[SectionOptions](#sectionoptions12对象说明)> | 瀑布流中所有分组配置信息。 |
147
148### length<sup>12+</sup>
149
150length(): number
151
152获取瀑布流中分组数量。
153
154**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
155
156**系统能力:** SystemCapability.ArkUI.ArkUI.Full
157
158**返回值:** 
159
160| 类型                                                         | 说明                                                         |
161| ------------------------------------------------------------ | ------------------------------------------------------------ |
162| number | 瀑布流中分组数量。 |
163
164## SectionOptions<sup>12+</sup>对象说明
165
166FlowItem分组配置信息。
167
168**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
169
170**系统能力:** SystemCapability.ArkUI.ArkUI.Full
171
172| 名称 | 类型 | 必填 | 说明 |
173|------|-----|-----|-----|
174| itemsCount | number | 是 | 分组中FlowItem数量,必须是正整数。若splice、push、update方法收到的分组中有分组的itemsCount小于0,则不会执行该方法。 |
175| crossCount | number | 否 | 纵向布局时为列数,横向布局时为行数,默认值:1。小于1的按默认值处理。 |
176| columnsGap | [Dimension](ts-types.md#dimension10) | 否 | 该分组的列间距,不设置时使用瀑布流的columnsGap,设置非法值时使用0vp。 |
177| rowsGap | [Dimension](ts-types.md#dimension10) | 否 | 该分组的行间距,不设置时使用瀑布流的rowsGap,设置非法值时使用0vp。 |
178| margin | [Margin](ts-types.md#margin) \| [Dimension](ts-types.md#dimension10) | 否 | 该分组的外边距参数为Length类型时,四个方向外边距同时生效。<br>默认值:0<br>单位:vp<br>margin设置百分比时,上下左右外边距均以瀑布流的width作为基础值。 |
179| onGetItemMainSizeByIndex | [GetItemMainSizeByIndex](#getitemmainsizebyindex12) | 否 | 瀑布流组件布局过程中获取指定index的FlowItem的主轴大小,纵向瀑布流时为高度,横向瀑布流时为宽度,单位vp。<br/>**说明:** <br/>1. 同时使用onGetItemMainSizeByIndex和FlowItem的宽高属性时,主轴大小以onGetItemMainSizeByIndex返回结果为准,onGetItemMainSizeByIndex会覆盖FlowItem的主轴长度。<br/>2. 使用onGetItemMainSizeByIndex可以提高瀑布流跳转到指定位置或index时的效率,避免混用设置onGetItemMainSizeByIndex和未设置的分组,会导致布局异常。<br/>3. onGetItemMainSizeByIndex返回负数时FlowItem高度为0。 |
180
181
182## GetItemMainSizeByIndex<sup>12+</sup>
183
184type GetItemMainSizeByIndex = (index: number) => number
185
186根据index获取指定Item的主轴大小。
187
188**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
189
190**系统能力:** SystemCapability.ArkUI.ArkUI.Full
191
192**参数:**
193
194| 参数名   | 类型                            | 必填   | 说明                   |
195| ---- | ----------------------------- | ---- | -------------------- |
196| index | number | 是    | FlowItem在WaterFlow中的索引。 |
197
198**返回值:** 
199
200| 类型                                                         | 说明                                                         |
201| ------------------------------------------------------------ | ------------------------------------------------------------ |
202| number | 指定index的FlowItem的主轴大小,纵向瀑布流时为高度,横向瀑布流时为宽度,单位vp。 |
203
204## WaterFlowLayoutMode<sup>12+</sup>枚举说明
205
206**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
207
208**系统能力:** SystemCapability.ArkUI.ArkUI.Full
209
210| 名称 | 值 | 说明 |
211| ------ | ------ | -------------------- |
212| ALWAYS_TOP_DOWN | 0 | 默认的从上到下的布局模式。视窗内的FlowItem依赖视窗上方所有FlowItem的布局信息。因此跳转或切换列数时,需要计算出上方所有的FlowItem的布局信息。 |
213| SLIDING_WINDOW | 1 | 移动窗口式的布局模式。只考虑视窗内的布局信息,对视窗上方的FlowItem没有依赖关系,因此向后跳转或切换列数时只需要布局视窗内的FlowItem。有频繁切换列数的场景的应用建议使用该模式。 <br/>**说明:** <br/>1. 无动画跳转到较远的位置时,会以目标位置为基准,向前或向后布局FlowItem。这之后如果滑回跳转前的位置,内容的布局效果可能和之前不一致。 这个效果会导致跳转后回滑到顶部时,顶部节点可能不对齐。所以该布局模式下会在滑动到顶部后自动调整布局,保证顶部对齐。在有多个分组的情况下,会在滑动结束时调整在视窗内的分组。<br/> 2. 该模式不支持使用滚动条,就算设置了滚动条也无法显示。 <br/> 3. 不支持[scroller](#waterflowoptions对象说明)的[scrollTo](ts-container-scroll.md#scrollto)接口。 <br/> 4. [scroller](#waterflowoptions对象说明)的[currentOffset](ts-container-scroll.md#currentoffset)接口返回的总偏移量在触发跳转或数据更新后不准确,在回滑到顶部时会重新校准。 <br/> 5. 如果在同一帧内调用跳转(如无动画的[scrollToIndex](ts-container-scroll.md#scrolltoindex)、[scrollEdge](ts-container-scroll.md#scrolledge))和输入偏移量(如滑动手势或滚动动画),两者都会生效。 <br/> 6. 调用无动画的[scrollToIndex](ts-container-scroll.md#scrolltoindex)进行跳转,如果跳转到较远位置(超过视窗内的FlowItem数量的位置)时,由于移动窗口模式对总偏移量没有估算,此时总偏移量没有变化,所以不会触发[onDidScroll](ts-container-scroll.md#ondidscroll12)事件。 |
214
215
216## 属性
217
218除支持[通用属性](ts-universal-attributes-size.md)和[滚动组件通用属性](ts-container-scrollable-common.md#属性)外,还支持以下属性:
219
220### columnsTemplate
221
222columnsTemplate(value: string)
223
224设置当前瀑布流组件布局列的数量,不设置时默认1列。
225
226例如, '1fr 1fr 2fr' 是将父组件分3列,将父组件允许的宽分为4等份,第一列占1份,第二列占1份,第三列占2份。
227
228可使用columnsTemplate('repeat(auto-fill,track-size)')根据给定的列宽track-size自动计算列数,其中repeat、auto-fill为关键字,track-size为可设置的宽度,支持的单位包括px、vp、%或有效数字,默认单位为vp,使用方法参见示例2。
229
230**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
231
232**系统能力:** SystemCapability.ArkUI.ArkUI.Full
233
234**参数:** 
235
236| 参数名 | 类型   | 必填 | 说明                                           |
237| ------ | ------ | ---- | ---------------------------------------------- |
238| value  | string | 是   | 当前瀑布流组件布局列的数量。<br/>默认值:'1fr' |
239
240### rowsTemplate
241
242rowsTemplate(value: string)
243
244设置当前瀑布流组件布局行的数量,不设置时默认1行。
245
246例如, '1fr 1fr 2fr'是将父组件分三行,将父组件允许的高分为4等份,第一行占1份,第二行占一份,第三行占2份。
247
248可使用rowsTemplate('repeat(auto-fill,track-size)')根据给定的行高track-size自动计算行数,其中repeat、auto-fill为关键字,track-size为可设置的高度,支持的单位包括px、vp、%或有效数字,默认单位为vp。
249
250**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
251
252**系统能力:** SystemCapability.ArkUI.ArkUI.Full
253
254**参数:** 
255
256| 参数名 | 类型   | 必填 | 说明                                           |
257| ------ | ------ | ---- | ---------------------------------------------- |
258| value  | string | 是   | 当前瀑布流组件布局行的数量。<br/>默认值:'1fr' |
259
260### itemConstraintSize
261
262itemConstraintSize(value: ConstraintSizeOptions)
263
264设置约束尺寸,子组件布局时,进行尺寸范围限制。
265
266**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
267
268**系统能力:** SystemCapability.ArkUI.ArkUI.Full
269
270**参数:** 
271
272| 参数名 | 类型                                                       | 必填 | 说明       |
273| ------ | ---------------------------------------------------------- | ---- | ---------- |
274| value  | [ConstraintSizeOptions](ts-types.md#constraintsizeoptions) | 是   | 约束尺寸。设置小于0的值,参数不生效。 <br/>**说明:**<br/>1.同时设置itemConstraintSize和FlowItem的[constraintSize](ts-universal-attributes-size.md#constraintsize)属性时,minWidth/minHeight会取其中的最大值,maxWidth/maxHeight会取其中的最小值,调整后的值作为FlowItem的constraintSize处理。2.只设置itemConstraintSize时,相当于对WaterFlow所有子组件设置了相同的constraintSize。3.itemConstraintSize通过以上两种方式转换成FlowItem的constraintSize后的生效规则与通用属性[constraintSize](./ts-universal-attributes-size.md#constraintsize)相同。|
275
276### columnsGap
277
278columnsGap(value: Length)
279
280设置列与列的间距。 
281
282**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
283
284**系统能力:** SystemCapability.ArkUI.ArkUI.Full
285
286**参数:** 
287
288| 参数名 | 类型                         | 必填 | 说明                          |
289| ------ | ---------------------------- | ---- | ----------------------------- |
290| value  | [Length](ts-types.md#length) | 是   | 列与列的间距。 <br/>默认值:0 |
291
292### rowsGap
293
294rowsGap(value: Length)
295
296设置行与行的间距。 
297
298**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
299
300**系统能力:** SystemCapability.ArkUI.ArkUI.Full
301
302**参数:** 
303
304| 参数名 | 类型                         | 必填 | 说明                          |
305| ------ | ---------------------------- | ---- | ----------------------------- |
306| value  | [Length](ts-types.md#length) | 是   | 行与行的间距。 <br/>默认值:0 |
307
308### layoutDirection
309
310layoutDirection(value: FlexDirection)
311
312设置布局的主轴方向。
313
314**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
315
316**系统能力:** SystemCapability.ArkUI.ArkUI.Full
317
318**参数:** 
319
320| 参数名 | 类型                                                | 必填 | 说明                                              |
321| ------ | --------------------------------------------------- | ---- | ------------------------------------------------- |
322| value  | [FlexDirection](ts-appendix-enums.md#flexdirection) | 是   | 布局的主轴方向。<br/>默认值:FlexDirection.Column |
323
324layoutDirection优先级高于rowsTemplate和columnsTemplate。根据layoutDirection设置情况,分为以下三种设置模式:
325
326- layoutDirection设置纵向布局(FlexDirection.ColumnFlexDirection.ColumnReverse327
328  此时columnsTemplate有效(如果未设置,取默认值)。例如columnsTemplate设置为"1fr 1fr"、rowsTemplate设置为"1fr 1fr 1fr"时,瀑布流组件纵向布局,辅轴均分成横向2列。
329
330- layoutDirection设置横向布局(FlexDirection.RowFlexDirection.RowReverse331
332  此时rowsTemplate有效(如果未设置,取默认值)。例如columnsTemplate设置为"1fr 1fr"、rowsTemplate设置为"1fr 1fr 1fr"时,瀑布流组件横向布局,辅轴均分成纵向3列。
333
334- layoutDirection未设置布局方向
335
336  布局方向为layoutDirection的默认值:FlexDirection.Column,此时columnsTemplate有效。例如columnsTemplate设置为"1fr 1fr"、rowsTemplate设置为"1fr 1fr 1fr"时,瀑布流组件纵向布局,辅轴均分成横向2列。
337
338### enableScrollInteraction<sup>10+</sup>
339
340enableScrollInteraction(value: boolean)
341
342设置是否支持滚动手势,当设置为false时,无法通过手指或者鼠标滚动,但不影响控制器的滚动接口。
343
344**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
345
346**系统能力:** SystemCapability.ArkUI.ArkUI.Full
347
348**参数:** 
349
350| 参数名 | 类型    | 必填 | 说明                                |
351| ------ | ------- | ---- | ----------------------------------- |
352| value  | boolean | 是   | 是否支持滚动手势。<br/>默认值:true |
353
354### nestedScroll<sup>10+</sup>
355
356nestedScroll(value: NestedScrollOptions)
357
358设置向前向后两个方向上的嵌套滚动模式,实现与父组件的滚动联动。
359
360**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
361
362**系统能力:** SystemCapability.ArkUI.ArkUI.Full
363
364**参数:** 
365
366| 参数名 | 类型                                                         | 必填 | 说明           |
367| ------ | ------------------------------------------------------------ | ---- | -------------- |
368| value  | [NestedScrollOptions](ts-container-scrollable-common.md#nestedscrolloptions10对象说明) | 是   | 嵌套滚动选项。 |
369
370### friction<sup>10+</sup>
371
372friction(value: number | Resource)
373
374设置摩擦系数,手动划动滚动区域时生效,只对惯性滚动过程有影响,对惯性滚动过程中的链式效果有间接影响。设置为小于等于0的值时,按默认值处理。
375
376**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
377
378**系统能力:** SystemCapability.ArkUI.ArkUI.Full
379
380**参数:** 
381
382| 参数名 | 类型                                                 | 必填 | 说明                                                      |
383| ------ | ---------------------------------------------------- | ---- | --------------------------------------------------------- |
384| value  | number&nbsp;\|&nbsp;[Resource](ts-types.md#resource) | 是   | 摩擦系数。<br/>默认值:非可穿戴设备为0.6,可穿戴设备为0.9。<br/>从API version 11开始,非可穿戴设备默认值为0.7。<br/>从API version 12开始,非可穿戴设备默认值为0.75。 |
385
386### cachedCount<sup>11+</sup>
387
388cachedCount(value: number)
389
390设置预加载的FlowItem的数量,只在LazyForEach中生效。设置该属性后会缓存cachedCount个FlowItem。[LazyForEach](../../../quick-start/arkts-rendering-control-lazyforeach.md)超出显示和缓存范围的FlowItem会被释放。设置为小于0的值时,按默认值显示。
391
392**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。
393
394**系统能力:** SystemCapability.ArkUI.ArkUI.Full
395
396**参数:** 
397
398| 参数名 | 类型   | 必填 | 说明                                     |
399| ------ | ------ | ---- | ---------------------------------------- |
400| value  | number | 是   | 预加载的FlowItem的数量。 <br/> 默认值:1 |
401
402### cachedCount<sup>14+</sup>
403
404cachedCount(value: number, show: boolean)
405
406设置预加载的FlowItem数量,并配置是否显示预加载节点。
407
408配合[裁剪](ts-universal-attributes-sharp-clipping.md#clip12)或[内容裁剪](ts-container-scrollable-common.md#clipcontent14)属性可以显示出预加载节点。
409
410[LazyForEach](../../../quick-start/arkts-rendering-control-lazyforeach.md)和开启了virtualScroll开关的[Repeat](../../../quick-start/arkts-new-rendering-control-repeat.md)超出显示和缓存范围的FlowItem会被释放。
411
412**原子化服务API:** 从API version 14开始,该接口支持在原子化服务中使用。
413
414**系统能力:** SystemCapability.ArkUI.ArkUI.Full
415
416**参数:** 
417
418| 参数名 | 类型   | 必填 | 说明                                     |
419| ------ | ------ | ---- | ---------------------------------------- |
420| value  | number | 是   | 预加载的FlowItem的数量。 <br/> 默认值:1 |
421| show  | boolean | 是   | 被预加载的FlowItem是否需要显示。 <br/> 默认值:false |
422
423## 事件
424
425除支持[通用事件](ts-universal-events-click.md)和[滚动组件通用事件](ts-container-scrollable-common.md#事件)外,还支持以下事件:
426
427### onReachStart
428
429onReachStart(event: () => void)
430
431瀑布流组件到达起始位置时触发。
432
433**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
434
435**系统能力:** SystemCapability.ArkUI.ArkUI.Full
436
437### onReachEnd
438
439onReachEnd(event: () => void)
440
441瀑布流组件到底末尾位置时触发。
442
443**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
444
445**系统能力:** SystemCapability.ArkUI.ArkUI.Full
446
447### onScrollFrameBegin<sup>10+</sup>
448
449onScrollFrameBegin(event: (offset: number, state: ScrollState) => { offsetRemain: number; })
450
451瀑布流开始滑动时触发,事件参数传入即将发生的滑动量,事件处理函数中可根据应用场景计算实际需要的滑动量并作为事件处理函数的返回值返回,瀑布流将按照返回值的实际滑动量进行滑动。
452
453触发该事件的条件:手指拖动WaterFlow、WaterFlow惯性划动时每帧开始时触发;WaterFlow超出边缘回弹、使用滚动控制器和拖动滚动条的滚动不会触发。
454
455**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
456
457**系统能力:** SystemCapability.ArkUI.ArkUI.Full
458
459**参数:** 
460
461| 参数名 | 类型                                                    | 必填 | 说明                       |
462| ------ | ------------------------------------------------------- | ---- | -------------------------- |
463| offset | number                                                  | 是   | 即将发生的滑动量,单位vp。 |
464| state  | [ScrollState](ts-container-list.md#scrollstate枚举说明) | 是   | 当前滑动状态。             |
465
466**返回值:** 
467
468| 类型                     | 说明                 |
469| ------------------------ | -------------------- |
470| { offsetRemain: number } | 实际滑动量,单位vp。 |
471
472### onScrollIndex<sup>11+</sup>
473
474onScrollIndex(event: (first: number, last: number) => void)
475
476当前瀑布流显示的起始位置/终止位置的子组件发生变化时触发。瀑布流初始化时会触发一次。
477
478瀑布流显示区域上第一个子组件/最后一个组件的索引值有变化就会触发。
479
480**原子化服务API:** 从API version 11开始,该接口支持在原子化服务中使用。
481
482**系统能力:** SystemCapability.ArkUI.ArkUI.Full
483
484**参数:** 
485
486| 参数名 | 类型   | 必填 | 说明                                  |
487| ------ | ------ | ---- | ------------------------------------- |
488| first  | number | 是   | 当前显示的瀑布流起始位置的索引值。 |
489| last   | number | 是   | 当前显示的瀑布流终止位置的索引值。    |
490
491## 示例
492
493### 示例1
494WaterFlow的基本使用。
495```ts
496// WaterFlowDataSource.ets
497
498// 实现IDataSource接口的对象,用于瀑布流组件加载数据
499export class WaterFlowDataSource implements IDataSource {
500  private dataArray: number[] = []
501  private listeners: DataChangeListener[] = []
502
503  constructor() {
504    for (let i = 0; i < 100; i++) {
505      this.dataArray.push(i)
506    }
507  }
508
509  // 获取索引对应的数据
510  public getData(index: number): number {
511    return this.dataArray[index]
512  }
513
514  // 通知控制器数据重新加载
515  notifyDataReload(): void {
516    this.listeners.forEach(listener => {
517      listener.onDataReloaded()
518    })
519  }
520
521  // 通知控制器数据增加
522  notifyDataAdd(index: number): void {
523    this.listeners.forEach(listener => {
524      listener.onDataAdd(index)
525    })
526  }
527
528  // 通知控制器数据变化
529  notifyDataChange(index: number): void {
530    this.listeners.forEach(listener => {
531      listener.onDataChange(index)
532    })
533  }
534
535  // 通知控制器数据删除
536  notifyDataDelete(index: number): void {
537    this.listeners.forEach(listener => {
538      listener.onDataDelete(index)
539    })
540  }
541
542  // 通知控制器数据位置变化
543  notifyDataMove(from: number, to: number): void {
544    this.listeners.forEach(listener => {
545      listener.onDataMove(from, to)
546    })
547  }
548
549  //通知控制器数据批量修改
550  notifyDatasetChange(operations: DataOperation[]): void {
551    this.listeners.forEach(listener => {
552      listener.onDatasetChange(operations);
553    })
554  }
555
556  // 获取数据总数
557  public totalCount(): number {
558    return this.dataArray.length
559  }
560
561  // 注册改变数据的控制器
562  registerDataChangeListener(listener: DataChangeListener): void {
563    if (this.listeners.indexOf(listener) < 0) {
564      this.listeners.push(listener)
565    }
566  }
567
568  // 注销改变数据的控制器
569  unregisterDataChangeListener(listener: DataChangeListener): void {
570    const pos = this.listeners.indexOf(listener)
571    if (pos >= 0) {
572      this.listeners.splice(pos, 1)
573    }
574  }
575
576  // 增加数据
577  public add1stItem(): void {
578    this.dataArray.splice(0, 0, this.dataArray.length)
579    this.notifyDataAdd(0)
580  }
581
582  // 在数据尾部增加一个元素
583  public addLastItem(): void {
584    this.dataArray.splice(this.dataArray.length, 0, this.dataArray.length)
585    this.notifyDataAdd(this.dataArray.length - 1)
586  }
587
588  // 在指定索引位置增加一个元素
589  public addItem(index: number): void {
590    this.dataArray.splice(index, 0, this.dataArray.length)
591    this.notifyDataAdd(index)
592  }
593
594  // 删除第一个元素
595  public delete1stItem(): void {
596    this.dataArray.splice(0, 1)
597    this.notifyDataDelete(0)
598  }
599
600  // 删除第二个元素
601  public delete2ndItem(): void {
602    this.dataArray.splice(1, 1)
603    this.notifyDataDelete(1)
604  }
605
606  // 删除最后一个元素
607  public deleteLastItem(): void {
608    this.dataArray.splice(-1, 1)
609    this.notifyDataDelete(this.dataArray.length)
610  }
611
612  // 在指定索引位置删除一个元素
613  public deleteItem(index: number): void {
614    this.dataArray.splice(index, 1)
615    this.notifyDataDelete(index)
616  }
617
618  // 重新加载数据
619  public reload(): void {
620    this.dataArray.splice(1, 1)
621    this.dataArray.splice(3, 2)
622    this.notifyDataReload()
623  }
624}
625```
626
627```ts
628// Index.ets
629import { WaterFlowDataSource } from './WaterFlowDataSource'
630
631@Entry
632@Component
633struct WaterFlowDemo {
634  @State minSize: number = 80
635  @State maxSize: number = 180
636  @State fontSize: number = 24
637  @State colors: number[] = [0xFFC0CB, 0xDA70D6, 0x6B8E23, 0x6A5ACD, 0x00FFFF, 0x00FF7F]
638  scroller: Scroller = new Scroller()
639  dataSource: WaterFlowDataSource = new WaterFlowDataSource()
640  private itemWidthArray: number[] = []
641  private itemHeightArray: number[] = []
642
643  // 计算FlowItem宽/高
644  getSize() {
645    let ret = Math.floor(Math.random() * this.maxSize)
646    return (ret > this.minSize ? ret : this.minSize)
647  }
648
649  // 设置FlowItem的宽/高数组
650  setItemSizeArray() {
651    for (let i = 0; i < 100; i++) {
652      this.itemWidthArray.push(this.getSize())
653      this.itemHeightArray.push(this.getSize())
654    }
655  }
656
657  aboutToAppear() {
658    this.setItemSizeArray()
659  }
660
661  @Builder
662  itemFoot() {
663    Column() {
664      Text(`Footer`)
665        .fontSize(10)
666        .backgroundColor(Color.Red)
667        .width(50)
668        .height(50)
669        .align(Alignment.Center)
670        .margin({ top: 2 })
671    }
672  }
673
674  build() {
675    Column({ space: 2 }) {
676      WaterFlow() {
677        LazyForEach(this.dataSource, (item: number) => {
678          FlowItem() {
679            Column() {
680              Text("N" + item).fontSize(12).height('16')
681              // 存在对应的jpg文件才会显示图片
682              Image('res/waterFlowTest(' + item % 5 + ').jpg')
683                .objectFit(ImageFit.Fill)
684                .width('100%')
685                .layoutWeight(1)
686            }
687          }
688          .onAppear(() => {
689            // 即将触底时提前增加数据
690            if (item + 20 == this.dataSource.totalCount()) {
691              for (let i = 0; i < 100; i++) {
692                this.dataSource.addLastItem()
693              }
694            }
695          })
696          .width('100%')
697          .height(this.itemHeightArray[item % 100])
698          .backgroundColor(this.colors[item % 5])
699        }, (item: string) => item)
700      }
701      .columnsTemplate("1fr 1fr")
702      .columnsGap(10)
703      .rowsGap(5)
704      .backgroundColor(0xFAEEE0)
705      .width('100%')
706      .height('100%')
707      .onReachStart(() => {
708        console.info('waterFlow reach start')
709      })
710      .onScrollStart(() => {
711        console.info('waterFlow scroll start')
712      })
713      .onScrollStop(() => {
714        console.info('waterFlow scroll stop')
715      })
716      .onScrollFrameBegin((offset: number, state: ScrollState) => {
717        console.info('waterFlow scrollFrameBegin offset: ' + offset + ' state: ' + state.toString())
718        return { offsetRemain: offset }
719      })
720    }
721  }
722}
723```
724
725![zh-cn_image_WaterFlow.gif](figures/waterflow-perf-demo.gif)
726
727### 示例2
728auto-fill的使用。
729```ts
730//index.ets
731import { WaterFlowDataSource } from './WaterFlowDataSource'
732
733@Entry
734@Component
735struct WaterFlowDemo {
736  @State minSize: number = 80
737  @State maxSize: number = 180
738  @State colors: number[] = [0xFFC0CB, 0xDA70D6, 0x6B8E23, 0x6A5ACD, 0x00FFFF, 0x00FF7F]
739  dataSource: WaterFlowDataSource = new WaterFlowDataSource()
740  private itemWidthArray: number[] = []
741  private itemHeightArray: number[] = []
742
743  // 计算FlowItem宽/高
744  getSize() {
745    let ret = Math.floor(Math.random() * this.maxSize)
746    return (ret > this.minSize ? ret : this.minSize)
747  }
748
749  // 设置FlowItem宽/高数组
750  setItemSizeArray() {
751    for (let i = 0; i < 100; i++) {
752      this.itemWidthArray.push(this.getSize())
753      this.itemHeightArray.push(this.getSize())
754    }
755  }
756
757  aboutToAppear() {
758    this.setItemSizeArray()
759  }
760
761  build() {
762    Column({ space: 2 }) {
763      WaterFlow() {
764        LazyForEach(this.dataSource, (item: number) => {
765          FlowItem() {
766            Column() {
767              Text("N" + item).fontSize(12).height('16')
768              Image('res/waterFlowTest(' + item % 5 + ').jpg')
769            }
770          }
771          .width('100%')
772          .height(this.itemHeightArray[item % 100])
773          .backgroundColor(this.colors[item % 5])
774        }, (item: string) => item)
775      }
776      .columnsTemplate('repeat(auto-fill,80)')
777      .columnsGap(10)
778      .rowsGap(5)
779      .padding({left:5})
780      .backgroundColor(0xFAEEE0)
781      .width('100%')
782      .height('100%')
783    }
784  }
785}
786```
787
788![waterflow_auto-fill.png](figures/waterflow_auto-fill.png)
789
790
791### 示例3
792WaterFlowSections的使用。
793```ts
794// Index.ets
795import { WaterFlowDataSource } from './WaterFlowDataSource'
796
797@Reusable
798@Component
799struct ReusableFlowItem {
800  @State item: number = 0
801
802  // 从复用缓存中加入到组件树之前调用,可在此处更新组件的状态变量以展示正确的内容
803  aboutToReuse(params: Record<string, number>) {
804    this.item = params.item;
805    console.info('Reuse item:' + this.item)
806  }
807
808  aboutToAppear() {
809    console.info('new item:' + this.item)
810  }
811
812  build() {
813    Image('res/waterFlowTest(' + this.item % 5 + ').jpg')
814        .overlay('N' + this.item, { align: Alignment.Top })
815        .objectFit(ImageFit.Fill)
816        .width('100%')
817        .layoutWeight(1)
818  }
819}
820
821@Entry
822@Component
823struct WaterFlowDemo {
824  minSize: number = 80
825  maxSize: number = 180
826  fontSize: number = 24
827  colors: number[] = [0xFFC0CB, 0xDA70D6, 0x6B8E23, 0x6A5ACD, 0x00FFFF, 0x00FF7F]
828  scroller: Scroller = new Scroller()
829  dataSource: WaterFlowDataSource = new WaterFlowDataSource()
830  dataCount: number = this.dataSource.totalCount()
831  private itemHeightArray: number[] = []
832  @State sections: WaterFlowSections = new WaterFlowSections()
833  sectionMargin: Margin = { top: 10, left: 5, bottom: 10, right: 5 }
834  oneColumnSection: SectionOptions = {
835    itemsCount: 4,
836    crossCount: 1,
837    columnsGap: '5vp',
838    rowsGap: 10,
839    margin: this.sectionMargin,
840    onGetItemMainSizeByIndex: (index: number) => {
841      return this.itemHeightArray[index % 100]
842    }
843  }
844  twoColumnSection: SectionOptions = {
845    itemsCount: 2,
846    crossCount: 2,
847    onGetItemMainSizeByIndex: (index: number) => {
848      return 100
849    }
850  }
851  lastSection: SectionOptions = {
852    itemsCount: 20,
853    crossCount: 2,
854    onGetItemMainSizeByIndex: (index: number) => {
855      return this.itemHeightArray[index % 100]
856    }
857  }
858
859  // 计算FlowItem高度
860  getSize() {
861    let ret = Math.floor(Math.random() * this.maxSize)
862    return (ret > this.minSize ? ret : this.minSize)
863  }
864
865  // 设置FlowItem的高度数组
866  setItemSizeArray() {
867    for (let i = 0; i < 100; i++) {
868      this.itemHeightArray.push(this.getSize())
869    }
870  }
871
872  aboutToAppear() {
873    this.setItemSizeArray()
874    // 初始化瀑布流分组信息
875    let sectionOptions: SectionOptions[] = []
876    let count = 0
877    let oneOrTwo = 0
878    while (count < this.dataCount) {
879      if (this.dataCount - count < 20) {
880        this.lastSection.itemsCount = this.dataCount - count
881        sectionOptions.push(this.lastSection)
882        break;
883      }
884      if (oneOrTwo++ % 2 == 0) {
885        sectionOptions.push(this.oneColumnSection)
886        count += this.oneColumnSection.itemsCount
887      } else {
888        sectionOptions.push(this.twoColumnSection)
889        count += this.twoColumnSection.itemsCount
890      }
891    }
892    this.sections.splice(0, 0, sectionOptions)
893  }
894
895  build() {
896    Column({ space: 2 }) {
897      Row() {
898        Button('splice')
899          .height('5%')
900          .onClick(() => {
901            // 将所有分组替换成一个新分组,注意保证LazyForEach中数据数量和新分组itemsCount保持一致
902            let totalCount: number = this.dataSource.totalCount()
903            let newSection: SectionOptions = {
904              itemsCount: totalCount,
905              crossCount: 2,
906              onGetItemMainSizeByIndex: (index: number) => {
907                return this.itemHeightArray[index % 100]
908              }
909            }
910            let oldLength: number = this.sections.length()
911            this.sections.splice(0, oldLength, [newSection])
912          })
913          .margin({ top: 10, left: 20 })
914        Button('update')
915          .height('5%')
916          .onClick(() => {
917            // 在第二个分组增加4个FlowItem,注意保证LazyForEach中数据数量和所有分组itemsCount的和保持一致
918            let newSection: SectionOptions = {
919              itemsCount: 6,
920              crossCount: 3,
921              columnsGap: 5,
922              rowsGap: 10,
923              margin: this.sectionMargin,
924              onGetItemMainSizeByIndex: (index: number) => {
925                return this.itemHeightArray[index % 100]
926              }
927            }
928            this.dataSource.addItem(this.oneColumnSection.itemsCount)
929            this.dataSource.addItem(this.oneColumnSection.itemsCount + 1)
930            this.dataSource.addItem(this.oneColumnSection.itemsCount + 2)
931            this.dataSource.addItem(this.oneColumnSection.itemsCount + 3)
932            const result: boolean = this.sections.update(1, newSection)
933            console.info('update:' + result)
934          })
935          .margin({ top: 10, left: 20 })
936        Button('delete')
937          .height('5%')
938          .onClick(() => {
939            // 先点击update再点击delete
940            let newSection: SectionOptions = {
941              itemsCount: 2,
942              crossCount: 2,
943              columnsGap: 5,
944              rowsGap: 10,
945              margin: this.sectionMargin,
946              onGetItemMainSizeByIndex: (index: number) => {
947                return this.itemHeightArray[index % 100]
948              }
949            }
950            this.dataSource.deleteItem(this.oneColumnSection.itemsCount)
951            this.dataSource.deleteItem(this.oneColumnSection.itemsCount)
952            this.dataSource.deleteItem(this.oneColumnSection.itemsCount)
953            this.dataSource.deleteItem(this.oneColumnSection.itemsCount)
954            this.sections.update(1, newSection)
955          })
956          .margin({ top: 10, left: 20 })
957        Button('values')
958          .height('5%')
959          .onClick(() => {
960            const sections: Array<SectionOptions> = this.sections.values();
961            for (const value of sections) {
962              console.log(JSON.stringify(value));
963            }
964            console.info('count:' + this.sections.length())
965          })
966          .margin({ top: 10, left: 20 })
967      }.margin({ bottom: 20 })
968
969      WaterFlow({ scroller: this.scroller, sections: this.sections }) {
970        LazyForEach(this.dataSource, (item: number) => {
971          FlowItem() {
972            ReusableFlowItem({ item: item })
973          }
974          .width('100%')
975          // 以onGetItemMainSizeByIndex为准
976          // .height(this.itemHeightArray[item % 100])
977          .backgroundColor(this.colors[item % 5])
978        }, (item: string) => item)
979      }
980      .columnsTemplate('1fr 1fr') // 瀑布流使用sections参数时该属性无效
981      .columnsGap(10)
982      .rowsGap(5)
983      .backgroundColor(0xFAEEE0)
984      .width('100%')
985      .height('100%')
986      .layoutWeight(1)
987      .onScrollIndex((first: number, last: number) => {
988        // 即将触底时提前增加数据
989        if (last + 20 >= this.dataSource.totalCount()) {
990          for (let i = 0; i < 100; i++) {
991            this.dataSource.addLastItem()
992          }
993          // 更新数据源后同步更新sections,修改最后一个section的FlowItem数量
994          const sections: Array<SectionOptions> = this.sections.values();
995          let newSection: SectionOptions = sections[this.sections.length() - 1];
996          newSection.itemsCount += 100;
997          this.sections.update(-1, newSection);
998        }
999      })
1000    }
1001  }
1002}
1003```
1004
1005![waterflowSections.png](figures/waterflowSections.png)
1006
1007### 示例4
1008双指缩放改变列数。
1009
1010```ts
1011// Index.ets
1012import { WaterFlowDataSource } from './WaterFlowDataSource'
1013
1014@Reusable
1015@Component
1016struct ReusableFlowItem {
1017  @State item: number = 0
1018
1019  // 从复用缓存中加入到组件树之前调用,可在此处更新组件的状态变量以展示正确的内容
1020  aboutToReuse(params: Record<string, number>) {
1021    this.item = params.item;
1022    console.info('Reuse item:' + this.item)
1023  }
1024
1025  aboutToAppear() {
1026    console.info('item:' + this.item)
1027  }
1028
1029  build() {
1030    Column() {
1031      Text("N" + this.item).fontSize(12).height('16')
1032      Image('res/waterFlow (' + this.item % 5 + ').JPG')
1033        .objectFit(ImageFit.Fill)
1034        .width('100%')
1035        .layoutWeight(1)
1036    }
1037  }
1038}
1039
1040@Entry
1041@Component
1042struct WaterFlowDemo {
1043  minSize: number = 80
1044  maxSize: number = 180
1045  colors: number[] = [0xFFC0CB, 0xDA70D6, 0x6B8E23, 0x6A5ACD, 0x00FFFF, 0x00FF7F]
1046  @State columns: number = 2
1047  dataSource: WaterFlowDataSource = new WaterFlowDataSource()
1048  private itemWidthArray: number[] = []
1049  private itemHeightArray: number[] = []
1050
1051  // 计算FlowItem宽/高
1052  getSize() {
1053    let ret = Math.floor(Math.random() * this.maxSize)
1054    return (ret > this.minSize ? ret : this.minSize)
1055  }
1056
1057  // 设置FlowItem的宽/高数组
1058  setItemSizeArray() {
1059    for (let i = 0; i < 100; i++) {
1060      this.itemWidthArray.push(this.getSize())
1061      this.itemHeightArray.push(this.getSize())
1062    }
1063  }
1064
1065  aboutToAppear() {
1066    let lastCount = AppStorage.get<number>('columnsCount')
1067    if (typeof lastCount != 'undefined') {
1068      this.columns = lastCount
1069    }
1070    this.setItemSizeArray()
1071  }
1072
1073  build() {
1074    Column({ space: 2 }) {
1075      Row() {
1076        Text('双指缩放改变列数')
1077          .height('5%')
1078          .margin({ top: 10, left: 20 })
1079      }
1080
1081      WaterFlow() {
1082        LazyForEach(this.dataSource, (item: number) => {
1083          FlowItem() {
1084            ReusableFlowItem({ item: item })
1085          }
1086          .width('100%')
1087          .height(this.itemHeightArray[item % 100])
1088          .backgroundColor(this.colors[item % 5])
1089        }, (item: string) => item)
1090      }
1091      .columnsTemplate('1fr '.repeat(this.columns))
1092      .columnsGap(10)
1093      .rowsGap(5)
1094      .backgroundColor(0xFAEEE0)
1095      .width('100%')
1096      .height('100%')
1097      .layoutWeight(1)
1098      // 切换列数item位置重排动画
1099      .animation({
1100        duration: 300,
1101        curve: Curve.Smooth
1102      })
1103      .priorityGesture(
1104        PinchGesture()
1105          .onActionEnd((event: GestureEvent) => {
1106            console.info('end scale:' + event.scale)
1107            // 手指分开,减少列数以放大item,触发阈值可以自定义,示例为2
1108            if (event.scale > 2) {
1109              this.columns--
1110            } else if (event.scale < 0.6) {
1111              this.columns++
1112            }
1113            // 可以根据设备屏幕宽度设定最大和最小列数,此处以最小1列最大4列为例
1114            this.columns = Math.min(4, Math.max(1, this.columns));
1115            AppStorage.setOrCreate<number>('columnsCount', this.columns)
1116          })
1117      )
1118    }
1119  }
1120}
1121```
1122
1123![pinch](figures/waterflow-pinch.gif)
1124
1125### 示例5
1126
1127```ts
1128//index.ets
1129//该示例实现了WaterFlow组件开启边缘渐隐效果并设置边缘渐隐长度
1130import { LengthMetrics } from '@kit.ArkUI'
1131import { WaterFlowDataSource } from './WaterFlowDataSource'
1132@Entry
1133@Component
1134struct WaterFlowDemo {
1135  @State minSize: number = 80
1136  @State maxSize: number = 180
1137  @State colors: number[] = [0xFFC0CB, 0xDA70D6, 0x6B8E23, 0x6A5ACD, 0x00FFFF, 0x00FF7F]
1138  dataSource: WaterFlowDataSource = new WaterFlowDataSource()
1139  scroller: Scroller = new Scroller()
1140  private itemWidthArray: number[] = []
1141  private itemHeightArray: number[] = []
1142
1143  // 计算FlowItem宽/高
1144  getSize() {
1145    let ret = Math.floor(Math.random() * this.maxSize)
1146    return (ret > this.minSize ? ret : this.minSize)
1147  }
1148
1149  // 设置FlowItem宽/高数组
1150  setItemSizeArray() {
1151    for (let i = 0; i < 100; i++) {
1152      this.itemWidthArray.push(this.getSize())
1153      this.itemHeightArray.push(this.getSize())
1154    }
1155  }
1156
1157  aboutToAppear() {
1158    this.setItemSizeArray()
1159  }
1160
1161  build() {
1162    Column({ space: 2 }) {
1163
1164      WaterFlow({ scroller:this.scroller }) {
1165        LazyForEach(this.dataSource, (item: number) => {
1166          FlowItem() {
1167            Column() {
1168              Text("N" + item).fontSize(12).height('16')
1169            }
1170          }
1171          .width('100%')
1172          .height(this.itemHeightArray[item % 100])
1173          .backgroundColor(this.colors[item % 5])
1174        }, (item: string) => item)
1175      }
1176      .columnsTemplate('repeat(auto-fill,80)')
1177      .columnsGap(10)
1178      .rowsGap(5)
1179      .height('90%')
1180      .fadingEdge(true,{fadingEdgeLength:LengthMetrics.vp(80)})
1181
1182    }
1183  }
1184}
1185```
1186
1187![fadingEdge_waterFlow](figures/fadingEdge_waterFlow.gif)