文章目录前言一、滚动物理模型回弹与摩擦1.1 真实世界的类比1.2 系统默认值差异二、edgeEffect三种边缘效果详解2.1 EdgeEffect 枚举2.2 Spring —— 越界拖拽 松手回弹2.3 Fade —— 边缘渐隐2.4 None —— 硬截止2.5 EdgeEffectOptionsalwaysEnabled三、friction摩擦系数与惯性滑行3.1 API 说明3.2 实战滑块 快捷档3.3 friction 与 edgeEffect 的协同四、scrollBar滚动条显隐与品牌化4.1 BarState 三种模式4.2 颜色与宽度4.3 设计建议五、Demo 架构与代码走读5.1 四场景结构5.2 Scroller onDidScroll5.3 onScrollEdge边界事件六、品牌预设一套参数定义滚动手感6.1 轻快信息流6.2 沉稳长文阅读6.3 极简沉浸展示6.4 封装建议七、嵌套滚动与 edgeEffect 避坑7.1 双回弹问题7.2 高度约束7.3 横向 vs 纵向八、与其他滚动 API 的关系九、场景选型速查表十、调试清单十一、总结前言滚动是移动端最高频的交互之一。用户很少意识到「滚动手感」的存在——直到它出了问题列表滑到尽头「哐」地一声硬停像撞上了墙快速甩动后惯性滑行太远停不下来像在冰面上打滑滚动条又粗又灰和 App 主色调格格不入嵌套滚动时内外两层同时回弹体验混乱。这些问题背后是同一组 ArkUI 滚动物理属性在起作用edgeEffect边缘效果决定「到边界时怎么办」friction摩擦系数决定「滑起来有多涩、惯性有多长」scrollBar系列属性决定滚动条的显隐与视觉风格。HarmonyOS 的Scroll、List、Grid、WaterFlow等可滚动组件均支持这套 API。本文结合项目 Demo从物理直觉、API 详解、四场景实战、品牌预设到嵌套滚动避坑做一次完整解析。读完后你应该能回答EdgeEffect.Spring和None分别在什么场景用friction调大还是调小列表会滑得更远alwaysEnabled解决的是什么问题怎样用一套参数组合出符合品牌调性的「滚动手感」一、滚动物理模型回弹与摩擦1.1 真实世界的类比把滚动容器想象成一条轨道上的小车概念物理类比ArkUI 对应边缘回弹拉到橡皮筋尽头松手弹回edgeEffect(EdgeEffect.Spring)硬截止撞到挡板立刻停止edgeEffect(EdgeEffect.None)渐隐提示边缘模糊暗示「到头了」edgeEffect(EdgeEffect.Fade)摩擦阻尼地面粗糙度影响减速快慢friction(0.75)滚动条轨道旁的位置指示器scrollBar 颜色/宽度用户手指离开屏幕后列表不会立刻停下而是靠惯性继续滑行一段距离再逐渐减速至静止。这段减速过程主要由friction控制。1.2 系统默认值差异根据官方文档edgeEffect默认值Scroll/Grid/WaterFlow为EdgeEffect.NoneList为EdgeEffect.Springfriction默认值非穿戴设备约0.75API 12穿戴设备约 0.9scrollBar默认值BarState.Auto滚动条宽度默认4vp颜色默认#18243140% 透明度这意味着如果你给Scroll什么都不配它没有回弹摩擦适中而List开箱即有 Spring 回弹。选型时要意识到这一差异避免「List 有回弹、外层 Scroll 没有」的混搭失控。二、edgeEffect三种边缘效果详解2.1 EdgeEffect 枚举declareenumEdgeEffect{Spring,// 弹性回弹越界可拖松手弹回Fade,// 渐隐边缘淡出提示None// 无效果到达边界即停止}2.2 Spring —— 越界拖拽 松手回弹EdgeEffect.Spring是最接近 iOS 系统列表手感的模式。当内容滚动到顶部或底部边界后用户仍可继续拖拽一段距离松手后内容以弹性动画弹回边界。适用场景信息流、社交动态、商品列表需要给用户「内容有弹性、有生命力」的感受与系统应用风格保持一致Demo 中「回弹对比」场景用三列并排 Scroll 直观对比Scroll(){this.ShortBounceContent(edge.mode)}.height(160).edgeEffect(edge.mode,this.edgeOptions()).friction(0.75).scrollBar(BarState.Off)三列分别绑定Spring、Fade、None。在内容高度小于容器的短内容区用力向下拖拽Spring 列会跟手延伸再弹回None 列则纹丝不动。2.3 Fade —— 边缘渐隐EdgeEffect.Fade在滚动到边界时以渐隐方式提示用户「已到头」没有明显的物理回弹位移。适合不希望产生越界位移、但仍需边界反馈的场景例如横向卡片轮播的两侧提示。2.4 None —— 硬截止EdgeEffect.None到达边界后立即停止无任何视觉或位移反馈。适合嵌套滚动的内层子列表避免内外双回弹沉浸式阅读、全屏内容展示精确控制滚动位置的场景如刻度选择器2.5 EdgeEffectOptionsalwaysEnabled第二个参数options控制内容不足一屏时是否仍启用边缘效果constEDGE_ALWAYS_ON:EdgeEffectOptions{alwaysEnabled:true}constEDGE_ALWAYS_OFF:EdgeEffectOptions{alwaysEnabled:false}.edgeEffect(EdgeEffect.Spring,{alwaysEnabled:true})alwaysEnabled行为true默认内容高度 容器高度时仍可拖拽并回弹false仅当内容溢出可滚动时边缘效果才生效Demo 中用 Toggle 开关实时切换这一选项。当你发现「列表明明没几条数据却拖不动、没有回弹」时首先检查alwaysEnabled是否为false以及内容是否真的溢出了容器。三、friction摩擦系数与惯性滑行3.1 API 说明.friction(value:number|Resource)作用范围主要影响手势在滚动区域内的滑动感受对惯性滚动过程中的减速有间接影响取值建议 Demo 区间0.2 – 1.2≤ 0 时回退默认值效果方向值越小如 0.35→ 摩擦低甩动后滑行距离更远减速慢手感「轻盈」值越大如 0.95→ 摩擦高甩动后很快停下手感「沉稳」3.2 实战滑块 快捷档Demo「摩擦阻尼」场景提供 Slider 连续调节和三个快捷按钮Slider({value:this.friction,min:0.2,max:1.2,step:0.05,style:SliderStyle.OutSet}).onChange((value:number):void{this.frictionvalue})// 快捷档// 轻盈 0.35 | 默认 0.75 | 沉稳 0.95验证方法在列表区快速甩动flick观察惯性停止位置。0.35 时列表会「飞」出去很远才缓缓停下0.95 时几乎立刻制动。配合onScrollStop监听惯性结束.onScrollStop(():void{this.statusText惯性停止 · 最终偏移${Math.round(this.scrollOffsetY)}vp})3.3 friction 与 edgeEffect 的协同两者独立配置但用户感知上是「一体」的轻快信息流Springfriction(0.55)—— 边界有回弹中间滑得远沉稳阅读器Springfriction(0.95)—— 有回弹但不「飘」极简沉浸Nonefriction(0.75)—— 无回弹系统默认摩擦不要只调一个而忽略另一个。单独把friction调到极低即使用None用户仍会觉得列表「滑过头」单独开Spring却不调摩擦回弹感有了但中间段惯性可能仍不符合预期。四、scrollBar滚动条显隐与品牌化4.1 BarState 三种模式declareenumBarState{Off,// 不显示Auto,// 触摸滚动时显示约 2 秒后消失On// 常驻显示}模式体验推荐场景Off无滚动条最干净沉浸式内容、短视频流、聊天Auto需要时短暂出现大多数列表系统默认策略On始终可见长文阅读、文档、进度可感知场景4.2 颜色与宽度.scrollBar(BarState.Auto).scrollBarColor(#6366F1)// 品牌主色.scrollBarWidth(4)// 单位 vp默认 4Demo「滚动条定制」场景提供Off / Auto / On 三档切换宽度 Slider2 – 10vp五色品牌色点选#6366F1/#10B981/#F59E0B/#EF4444/#475569细滚动条3vp 品牌色让滚动指示器成为 UI 的一部分而非系统默认的灰色细线。4.3 设计建议颜色取品牌主色的 60% – 80% 透明度或使用 Demo 中的实色方案宽度信息流用 2 – 3vp 保持克制文档阅读可用 5 – 6vp 提升可发现性显隐社交类 App 普遍Off或Auto工具类、阅读类可考虑On五、Demo 架构与代码走读5.1 四场景结构Demo 用ScrollDemoCase枚举驱动四个场景enumScrollDemoCase{EDGE_BOUNCE0,// 回弹对比FRICTION1,// 摩擦阻尼SCROLLBAR2,// 滚动条定制BRAND_PRESET3// 品牌预设}顶部横向 Chip 切换场景下方为对应的交互区和实时状态面板摩擦系数、滚动条模式、偏移量、触顶/触底。5.2 Scroller onDidScroll追踪滚动偏移时Demo 使用Scroller配合onDidScrollAPI 12 推荐替代已废弃的onScrollprivatedemoScroller:ScrollernewScroller()privatesyncScrollOffset():void{constoffset:OffsetResultthis.demoScroller.currentOffset()this.scrollOffsetYoffset.yOffset}Scroll(this.demoScroller){this.ScrollCardList()}.onDidScroll(():void{this.syncScrollOffset()})onDidScroll在每帧滚动完成后触发通过currentOffset()获取绝对偏移比逐帧累加 delta 更可靠。5.3 onScrollEdge边界事件.onScrollEdge((side:Edge):void{this.edgeHintsideEdge.Top?触顶:触底})可用于埋点统计用户是否浏览到底部、触发加载更多、或配合回弹做自定义 UI如「已到顶部」提示。注意onScrollEdge在滚动到达边界时触发与 Spring 的越界拖拽阶段不同。六、品牌预设一套参数定义滚动手感与其每次手写四个属性不如为产品定义几套滚动手感预设在全局或页面级统一套用。Demo 提供三套6.1 轻快信息流{edge:EdgeEffect.Spring,friction:0.55,bar:BarState.Auto,barColor:#6366F1,barWidth:3}低摩擦 细滚动条 Spring 回弹。适合微博、新闻、商品 Feed——用户快速划过边界有弹性的「呼吸感」。6.2 沉稳长文阅读{edge:EdgeEffect.Spring,friction:0.95,bar:BarState.On,barColor:#475569,barWidth:6}高摩擦 常驻粗滚动条。每屏内容更可控用户能清楚感知「读到哪里了」。6.3 极简沉浸展示{edge:EdgeEffect.None,friction:0.75,bar:BarState.Off}无回弹、无滚动条、系统默认摩擦。适合相册、封面故事、引导页——滚动只是手段不应抢戏。6.4 封装建议在工程中可抽象为配置对象interfaceScrollFeelPreset{edge:EdgeEffect friction:numberbar:BarState barColor:stringbarWidth:number}// 封装为 Styles 或自定义 modifierfunctionapplyScrollFeel(scroll:ScrollAttribute,preset:ScrollFeelPreset):void{scroll.edgeEffect(preset.edge,{alwaysEnabled:true}).friction(preset.friction).scrollBar(preset.bar).scrollBarColor(preset.barColor).scrollBarWidth(preset.barWidth)}设计团队定好预设后开发只需applyScrollFeel(scroll, PRESET_LIGHT)保证全 App 手感一致。七、嵌套滚动与 edgeEffect 避坑7.1 双回弹问题当List嵌套在Scroll内若两者都设EdgeEffect.Spring用户滑到内层 List 底部时可能先触发 List 回弹再触发外层 Scroll 回弹——手感像「弹簧套弹簧」。官方建议嵌套场景中将内层可滚动组件的edgeEffect设为NoneScroll(){List(){// ...}.edgeEffect(EdgeEffect.None)// 内层关闭回弹}.edgeEffect(EdgeEffect.Spring)// 外层保留回弹7.2 高度约束无论配了多少滚动特效Scroll / List 必须有明确的高度约束layoutWeight(1)或固定height否则组件按内容撑开不会出现滚动视口所有edgeEffect和friction都不会生效。这是比物理参数更优先的「前置条件」。7.3 横向 vs 纵向Scroll通过.scrollable(ScrollDirection.Horizontal)切换方向。edgeEffect和friction同样生效但边缘判断变为左右边界。横向轮播通常用Fade或None纵向列表用Spring。八、与其他滚动 API 的关系Demo 聚焦edgeEffectfrictionscrollBar但完整滚动体验还涉及API作用与本文关系nestedScroll父子滚动联动、优先响应方嵌套时配合edgeEffect.NoneonScrollFrameBegin每帧拦截偏移量自定义阻尼曲线时用scrollSnap滚动吸附到锚点与 friction 共同决定「停在哪」enablePaging逐页翻动摩擦高 分页 幻灯片感Fling初速度系统根据手势计算friction 间接影响最终距离对于大多数业务列表调好 edgeEffect friction scrollBar 三者已足够。只有需要自定义「非物理」滚动行为时才深入onScrollFrameBegin。九、场景选型速查表业务场景edgeEffectfrictionscrollBar理由社交信息流Spring0.5 – 0.6Auto 品牌色细条轻快、有生命力电商商品列表Spring0.65 – 0.75Auto平衡速度与可控性长文 / 协议Spring0.9 – 1.0On 粗条沉稳、位置可感知聊天消息Spring0.75Off简洁不干扰气泡全屏相册None0.75Off沉浸无多余反馈嵌套内层 ListNone默认Off避免双回弹横向卡片Fade / None0.7Off不需要纵向回弹设置页None0.85Auto条目少硬截止即可十、调试清单当滚动手感「不对」时按此顺序排查容器有高度吗—layoutWeight(1)或固定height是否缺失内容溢出了吗— 条目太少且alwaysEnabled: false时回弹不触发嵌套冲突— 内外层是否都开了 Spring组件默认值—Scroll默认NoneList默认Springfriction 是否极端— 低于 0.3 或高于 1.1 时手感异常明显是否在 PC 模拟器测— 鼠标拖拽与手指 flick 的惯性不同务必真机验证滚动条遮挡内容—scrollBarWidth过大时检查右侧留白Demo 顶部状态栏实时显示摩擦、滚动条、偏移、边缘四项指标可作为调试参照。十一、总结HarmonyOS ArkUI 的滚动物理并不复杂核心就三组属性edgeEffect → 到边界时弹回来渐隐还是硬停 friction → 滑中间时惯性长还是急刹 scrollBar → 视觉层 显不显什么颜色多粗三者独立配置、组合使用就能塑造从「轻快信息流」到「沉稳阅读器」的差异化手感。不必追求与 iOS 完全一致——关键是同一 App 内保持一致让用户形成肌肉记忆。本文 Demo 以单文件Index.ets承载四个场景建议运行到真机后在「回弹对比」三列区用力拖拽短内容感受 Spring / Fade / None 差异在「摩擦阻尼」快速甩动列表对比 0.35 与 0.95 的滑行距离在「滚动条定制」切换 On/Auto/Off观察品牌色细条效果在「品牌预设」一键切换轻快 / 沉稳 / 极简找到最接近你产品的组合滚动是隐形的 UX。用户不会称赞「这列表摩擦系数真棒」但会在回弹生硬、惯性失控时立刻流失。把edgeEffect和friction当作产品调性的一部分来配置而不是上线后再补——这才是 Scroll 滚动特效的真正价值。相关文件entry/src/main/ets/pages/Index.ets编译命令node/Applications/DevEco-Studio.app/Contents/tools/hvigor/bin/hvigorw.js assembleHap-pproductdefault-pbuildModedebug --no-daemon