uni-app 小程序:滚动联动透明导航栏的实现

1、需求场景

页面顶部有一张带渐变背景的产品图,导航栏初始透明 叠在图上(白字白箭头)。 随着用户上滑、内容向上滚出可视区,当主体图的顶部刚要被导航栏遮住时,希望导航栏自动切换为:

  • 背景:白色
  • 标题文字:深色 (#333)
  • 返回箭头:深色

下滑回顶部后再平滑还原为透明态。整个切换要带过渡,不能突变。

2、实现思路

核心三步:

  1. 用页面级生命周期 onPageScroll(e) 拿到 scrollTop
  2. 用一个布尔状态 isNavSolid 控制"透明态 / 实色态",过阈值就置 true,否则 false
  3. 模板上根据这个状态切换 class,CSS 用 transition 让背景色和文字色平滑过渡。

返回箭头是 u-icon 这种第三方组件,它的颜色是通过 prop 传入的,不能直接 CSS transition ,所以要把 color 改成动态绑定。

3、完整示例代码

Template

vue 复制代码
<template>
  <view class="page-container">
    <!-- 自定义透明导航栏,过阈值后切换为白底深字 -->
    <view
      class="custom-nav"
      :class="{ 'is-solid': isNavSolid }"
      :style="{ height: navbarHeight + 'px' }"
    >
      <view
        class="nav-title-content"
        :style="{ height: statusBarHeight + 'px' }"
        @click="goBack"
      >
        <u-icon name="arrow-left" size="40" :color="navIconColor"></u-icon>
        <text class="nav-title">页面标题</text>
      </view>
    </view>

    <!-- 顶部产品图区(渐变背景) -->
    <view class="top-banner" :style="{ paddingTop: navbarHeight + 'px' }">
      <image class="banner-image" :src="imageUrl" mode="widthFix" />
    </view>

    <!-- 其余内容... -->
  </view>
</template>

Script

js 复制代码
export default {
  data() {
    return {
      statusBarHeight: 0,
      navbarHeight: 0,
      isNavSolid: false,
      imageUrl: '',
    };
  },
  computed: {
    navIconColor() {
      return this.isNavSolid ? '#333' : '#fff';
    },
  },
  onLoad() {
    const sysInfo = uni.getSystemInfoSync();
    this.statusBarHeight = sysInfo.statusBarHeight;
    this.navbarHeight = sysInfo.statusBarHeight + 44;
  },
  onPageScroll(e) {
    const threshold = 30;
    this.isNavSolid = e.scrollTop >= threshold;
  },
  methods: {
    goBack() {
      uni.navigateBack({ delta: 1 });
    },
  },
};

Style

scss 复制代码
.custom-nav {
  width: 100%;
  background: transparent;
  display: flex;
  align-items: flex-end;
  justify-content: center;
  position: fixed;
  top: 0;
  left: 0;
  z-index: 999;
  transition: background-color 0.2s ease;

  .nav-title-content {
    display: flex;
    align-items: center;
    width: 100%;
    padding: 0 32rpx;
  }

  .nav-title {
    color: #fff;
    font-size: 32rpx;
    font-weight: 500;
    margin-left: 10rpx;
    transition: color 0.2s ease;
  }

  &.is-solid {
    background: #fff;
    .nav-title {
      color: #333;
    }
  }
}

4、关键点解析

4.1. 为什么用 onPageScroll 而不是 scroll-view + @scroll?

  • onPageScroll 是小程序 / uni-app 的页面级生命周期,监听的是整个页面的原生滚动;
  • 不需要套 <scroll-view>,整张页面用普通 <view> 就行,写起来简单、性能也更好;
  • 平台底层已经做了节流(默认 ~16ms 一次),不需要自己再写 throttle

4.2. 为什么过渡要分开写在 .custom-nav.nav-title 上?

  • 背景色变化是父元素 .custom-nav 自己的事;
  • 文字颜色变化在子元素 .nav-title 上;
  • 两者各自加 transition,互不影响;统一写在父元素上(用 all)会污染所有子元素的属性变化、性能也差。

4.3. u-icon 颜色为什么要动态绑定?

u-iconcolor prop 转成内部样式(通常是 <text>color 或 SVG 的 fill),但它不会自动被外部 CSS transition 接管。所以:

  • <u-icon color="#fff" class="dynamic-color"> + 外面写 .is-solid .dynamic-color { color: #333 } ------ 不生效;
  • <u-icon :color="navIconColor"> + 计算属性返回不同颜色 ------ 生效,但箭头颜色是瞬切(无补间)。

如果要箭头颜色也带补间动画,可以叠两层 u-icon(一白一黑),用 opacity 切换,外面给 opacitytransition。一般场景下瞬切已经够用。

4.4. 阈值(threshold)怎么选?

凭手感不靠谱,我做了个粗略的几何推导:

设:

  • 导航栏总高 H_nav = statusBarHeight + 44(一般 64--88px)
  • 产品图视觉顶部到页面顶部的距离 Y_img(例如布局 top + transform 缩放偏移)
  • 标题文字垂直中线在 Y_title ≈ statusBarHeight + 22

期望触发条件:视觉图顶达到标题文字位置

Y_img - scrollTop = Y_title => threshold = Y_img - Y_title

实测代入数字(Y_img ≈ 100pxY_title ≈ 66px),threshold ≈ 30--35px,就用 30 落地。最终值实机微调即可:

  • 太晚(图都进导航栏一半才变色)→ 调小到 15--20;
  • 太早(一滑就变色,没"过渡感")→ 调大到 40--50。

5、注意事项 / 踩坑

  1. 状态栏高度不能写死 44 :iOS 异形屏 / 安卓不同设备状态栏高度差很多,必须用 uni.getSystemInfoSync().statusBarHeight 动态取。

  2. onPageScroll 一定要写在页面 (pages/xxx) 上 ,写在 components/ 里组件里不会被触发。

  3. 不要在 onPageScroll 里做重操作:每次滚动都会触发,里面只做"对比 + 赋值"这种 O(1) 操作;DOM 查询、网络请求绝对不要放进来。

  4. 位置稳定性 :如果触发的赋值会引发 isNavSolid 在阈值附近反复横跳,可以加滞后区(hysteresis):上滑 ≥ 30 触发实色,下滑 ≤ 20 才回到透明,避免频繁切换。

6、总结

步骤 关键 API / 技巧
监听滚动 onPageScroll(e)
状态控制 一个布尔 isNavSolid + 模板 :class 切换
平滑过渡 CSS transition: background-color, color
第三方组件颜色 :color 动态绑定计算属性
阈值估算 视觉图顶位置 - 标题文字位置

整个交互逻辑 不到 20 行代码,但用户感受到的"质感提升"非常明显,是 ROI 很高的一类细节。


如果喜欢我的文章,欢迎点赞、转发:)

相关推荐
半兽先生2 小时前
vue高性能下拉组件 支持上万数据不卡顿
前端·javascript·vue.js
懂懂tty3 小时前
Vue3 架构
前端·vue.js
invicinble3 小时前
前端框架使用vue-cli( 第二层:工程配置层--总览)
前端·vue.js·前端框架
哆啦A梦15883 小时前
01, 前端vue3框架的快速搭建以及项目工程的讲解
前端·vue3·springboot
念你那丝微笑3 小时前
2026年Vue前端面试准备
前端·vue.js·面试
冴羽yayujs3 小时前
GitHub 前端热榜项目 - 日榜(2026-05-09)
前端·github
Copy_Paste_Coder3 小时前
小程序失败后,换个方向,终于成功搞到收益
前端·javascript·后端
问心无愧05133 小时前
ctf show web入门31
前端·笔记
ZC跨境爬虫3 小时前
跟着 MDN 学 HTML day_31:(AbortSignal 深入解析与高级中止模式)
前端·ui·html·音视频·视频编解码