滚动是移动端最高频的交互操作没有之一。HarmonyOS NEXT对滚动体验的控制相当细——边缘效果有三种模式滚动条可以定制颜色和宽度onScrollFrameBegin可以实现吸顶和折叠。这些API看着简单但搭配起来有不少细节。EdgeEffect三种边缘效果List、Scroll、WaterFlow等滚动组件都支持edgeEffect属性Spring — 弹簧回弹List().edgeEffect(EdgeEffect.Spring)拉到边界后可以继续拉松手弹回来。iOS风格的经典效果HarmonyOS默认就是这个。适合大部分内容列表。回弹距离跟拉动力度成正比松手后有过冲动画弹过一点再回正。这种物理感很自然用户拉到边界时明确知道到底了。Fade — 渐隐List().edgeEffect(EdgeEffect.Fade)拉到边界后边缘出现渐隐效果不是回弹。视觉上比Spring内敛适合工具类应用、设置页面这类不需要活泼感的场景。Fade效果在垂直和水平方向都生效水波纹一样向外扩散然后消散。None — 无效果List().edgeEffect(EdgeEffect.None)拉到边界直接停住没有任何视觉反馈。很少用除非业务要求严格的到底即止行为——比如分页数据每页固定条数拉到底就该翻页不该回弹。BarState滚动条显示控制List().scrollBar(BarState.Auto)// 自动显示/隐藏(默认).scrollBar(BarState.On)// 始终显示.scrollBar(BarState.Off)// 始终隐藏Auto滚动时显示停止后渐隐。最常用。On始终显示。适合长文档、代码查看等需要随时看滚动位置的场景。Off隐藏。搭配自定义滚动指示器使用。滚动条样式定制List().scrollBarColor(#1a73e8)// 滚动条颜色.scrollBarWidth(4)// 滚动条宽度(vp)默认滚动条是灰色圆角条3-4vp宽。改成品牌色4vp是常见的定制手法。宽度不建议超过6vp太粗影响内容区域。Scroll组件同样支持这些属性Scroll().scrollBar(BarState.Auto).scrollBarColor(#43a047).scrollBarWidth(4).edgeEffect(EdgeEffect.Fade)List滚动进阶initialIndex — 初始位置List({initialIndex:5}){ForEach(...)}列表初始显示第5项。搭配聊天气泡场景——从最新消息开始显示不用先滚到底。scrollToIndex — 编程式跳转StatescrollIndex:number0;List({initialIndex:this.scrollIndex}){// ...}Button(跳到底部).onClick((){this.scrollIndexthis.itemCount-1;})注意List的initialIndex只在初始化时生效后续修改不会自动跳转。要实现动态跳转需要用ScrollerprivatelistScroller:ScrollernewScroller();List({scroller:this.listScroller}){// ...}Button(跳到顶部).onClick((){this.listScroller.scrollToIndex(0);})Button(跳到底部).onClick((){this.listScroller.scrollToIndex(this.itemCount-1);})Scroller还支持scrollEdge(Edge.Top/Edge.Bottom)直接滚到头尾。divider — 分隔线List().divider({strokeWidth:1,color:#eeeeee,startMargin:12,endMargin:12})比在ListItem里手动画Divider方便而且自动跳过最后一项的分隔线。startMargin/endMargin控制左右留白。onScrollFrameBegin — 滚动拦截这是实现吸顶、折叠头部的核心APIList().onScrollFrameBegin((offset:number){// offset是本次滚动偏移量(正向上滚负向下滚)// 返回{offsetRemain: 实际应用的偏移}this.addLog(scroll offsetoffset.toString());return{offsetRemain:offset};})返回的offsetRemain决定了实际滚动距离。如果返回{offsetRemain: 0}滚动被完全拦截——这就是嵌套Scroll实现吸顶的关键。注意回调频率很高每帧一次不要在里面做重计算或setState。水平滚动Scroll组件默认垂直滚动改成水平Scroll(){Row(){ForEach(items,(item:string){Column(){Text(item)}.width(80).height(80).margin({right:8})},(item:string,index:number)index.toString())}}.scrollable(ScrollDirection.Horizontal).scrollBar(BarState.Auto).edgeEffect(EdgeEffect.Spring)水平滚动必须用ScrollRowList不支持水平模式List的横向布局用ListListItemRow模拟。嵌套滚动垂直Scroll嵌套水平Scroll是常见模式Scroll(){Column(){Text(水平区域)Scroll(){Row(){/* 横向内容 */}}.scrollable(ScrollDirection.Horizontal)Text(垂直内容)Column(){/* 纵向内容 */}}}嵌套滚动默认会冲突——手指竖滑时水平Scroll不响应横滑时垂直Scroll不响应。这是预期的GestureGroup的Exclusive模式自动处理了方向判断。ForEach在List中的注意事项ForEach生成ListItem时数组必须是显式类型的// 错误数组字面量类型不可推断ForEach([{name:a},{name:b}],...)// 正确先声明接口再创建数组interfaceStringItem{name:string;}constitems:StringItem[][{name:a},{name:b}];ForEach(items,(item:StringItem){...})ArkTS的arkts-no-untyped-obj-literals规则要求对象字面量必须有对应的接口声明。这个规则在List场景下特别容易触发因为列表数据通常是对象数组。另一个常见写法是用Array.fromconstitems:number[]Array.from({length:30},(_v:Object,i:number)i);ForEach(items,(item:number){...},(item:number,index:number)index.toString())边缘效果选型指南场景EdgeEffectBarState理由新闻列表SpringAuto活泼感拉到头弹回设置页FadeAuto内敛功能导向聊天记录SpringOff自定义滚动指示代码查看NoneOn精确定位无回弹干扰图片横滑SpringAuto丝滑横滑体验分页数据NoneAuto到底即止引导翻页性能提示edgeEffect(EdgeEffect.Spring)比None略耗性能回弹动画但差异可忽略scrollBar(BarState.Off)比On少渲染一个滚动条组件onScrollFrameBegin每帧回调保持轻量divider比手动Divider组件性能好批量渲染优化cachedCount减少列表项重建但不影响滚动体验本身滚动体验是用的时候无感差的时候要命的典型维度。Spring回弹给身体感Fade给视觉感None给控制感——选对边缘效果比优化滚动速度重要得多。
HarmonyOS 6.0 滚动条与边缘效果
发布时间:2026/7/17 11:57:12
滚动是移动端最高频的交互操作没有之一。HarmonyOS NEXT对滚动体验的控制相当细——边缘效果有三种模式滚动条可以定制颜色和宽度onScrollFrameBegin可以实现吸顶和折叠。这些API看着简单但搭配起来有不少细节。EdgeEffect三种边缘效果List、Scroll、WaterFlow等滚动组件都支持edgeEffect属性Spring — 弹簧回弹List().edgeEffect(EdgeEffect.Spring)拉到边界后可以继续拉松手弹回来。iOS风格的经典效果HarmonyOS默认就是这个。适合大部分内容列表。回弹距离跟拉动力度成正比松手后有过冲动画弹过一点再回正。这种物理感很自然用户拉到边界时明确知道到底了。Fade — 渐隐List().edgeEffect(EdgeEffect.Fade)拉到边界后边缘出现渐隐效果不是回弹。视觉上比Spring内敛适合工具类应用、设置页面这类不需要活泼感的场景。Fade效果在垂直和水平方向都生效水波纹一样向外扩散然后消散。None — 无效果List().edgeEffect(EdgeEffect.None)拉到边界直接停住没有任何视觉反馈。很少用除非业务要求严格的到底即止行为——比如分页数据每页固定条数拉到底就该翻页不该回弹。BarState滚动条显示控制List().scrollBar(BarState.Auto)// 自动显示/隐藏(默认).scrollBar(BarState.On)// 始终显示.scrollBar(BarState.Off)// 始终隐藏Auto滚动时显示停止后渐隐。最常用。On始终显示。适合长文档、代码查看等需要随时看滚动位置的场景。Off隐藏。搭配自定义滚动指示器使用。滚动条样式定制List().scrollBarColor(#1a73e8)// 滚动条颜色.scrollBarWidth(4)// 滚动条宽度(vp)默认滚动条是灰色圆角条3-4vp宽。改成品牌色4vp是常见的定制手法。宽度不建议超过6vp太粗影响内容区域。Scroll组件同样支持这些属性Scroll().scrollBar(BarState.Auto).scrollBarColor(#43a047).scrollBarWidth(4).edgeEffect(EdgeEffect.Fade)List滚动进阶initialIndex — 初始位置List({initialIndex:5}){ForEach(...)}列表初始显示第5项。搭配聊天气泡场景——从最新消息开始显示不用先滚到底。scrollToIndex — 编程式跳转StatescrollIndex:number0;List({initialIndex:this.scrollIndex}){// ...}Button(跳到底部).onClick((){this.scrollIndexthis.itemCount-1;})注意List的initialIndex只在初始化时生效后续修改不会自动跳转。要实现动态跳转需要用ScrollerprivatelistScroller:ScrollernewScroller();List({scroller:this.listScroller}){// ...}Button(跳到顶部).onClick((){this.listScroller.scrollToIndex(0);})Button(跳到底部).onClick((){this.listScroller.scrollToIndex(this.itemCount-1);})Scroller还支持scrollEdge(Edge.Top/Edge.Bottom)直接滚到头尾。divider — 分隔线List().divider({strokeWidth:1,color:#eeeeee,startMargin:12,endMargin:12})比在ListItem里手动画Divider方便而且自动跳过最后一项的分隔线。startMargin/endMargin控制左右留白。onScrollFrameBegin — 滚动拦截这是实现吸顶、折叠头部的核心APIList().onScrollFrameBegin((offset:number){// offset是本次滚动偏移量(正向上滚负向下滚)// 返回{offsetRemain: 实际应用的偏移}this.addLog(scroll offsetoffset.toString());return{offsetRemain:offset};})返回的offsetRemain决定了实际滚动距离。如果返回{offsetRemain: 0}滚动被完全拦截——这就是嵌套Scroll实现吸顶的关键。注意回调频率很高每帧一次不要在里面做重计算或setState。水平滚动Scroll组件默认垂直滚动改成水平Scroll(){Row(){ForEach(items,(item:string){Column(){Text(item)}.width(80).height(80).margin({right:8})},(item:string,index:number)index.toString())}}.scrollable(ScrollDirection.Horizontal).scrollBar(BarState.Auto).edgeEffect(EdgeEffect.Spring)水平滚动必须用ScrollRowList不支持水平模式List的横向布局用ListListItemRow模拟。嵌套滚动垂直Scroll嵌套水平Scroll是常见模式Scroll(){Column(){Text(水平区域)Scroll(){Row(){/* 横向内容 */}}.scrollable(ScrollDirection.Horizontal)Text(垂直内容)Column(){/* 纵向内容 */}}}嵌套滚动默认会冲突——手指竖滑时水平Scroll不响应横滑时垂直Scroll不响应。这是预期的GestureGroup的Exclusive模式自动处理了方向判断。ForEach在List中的注意事项ForEach生成ListItem时数组必须是显式类型的// 错误数组字面量类型不可推断ForEach([{name:a},{name:b}],...)// 正确先声明接口再创建数组interfaceStringItem{name:string;}constitems:StringItem[][{name:a},{name:b}];ForEach(items,(item:StringItem){...})ArkTS的arkts-no-untyped-obj-literals规则要求对象字面量必须有对应的接口声明。这个规则在List场景下特别容易触发因为列表数据通常是对象数组。另一个常见写法是用Array.fromconstitems:number[]Array.from({length:30},(_v:Object,i:number)i);ForEach(items,(item:number){...},(item:number,index:number)index.toString())边缘效果选型指南场景EdgeEffectBarState理由新闻列表SpringAuto活泼感拉到头弹回设置页FadeAuto内敛功能导向聊天记录SpringOff自定义滚动指示代码查看NoneOn精确定位无回弹干扰图片横滑SpringAuto丝滑横滑体验分页数据NoneAuto到底即止引导翻页性能提示edgeEffect(EdgeEffect.Spring)比None略耗性能回弹动画但差异可忽略scrollBar(BarState.Off)比On少渲染一个滚动条组件onScrollFrameBegin每帧回调保持轻量divider比手动Divider组件性能好批量渲染优化cachedCount减少列表项重建但不影响滚动体验本身滚动体验是用的时候无感差的时候要命的典型维度。Spring回弹给身体感Fade给视觉感None给控制感——选对边缘效果比优化滚动速度重要得多。