Taro实现自动显示/隐藏的导航栏

接上文Taro实现兼容H5的沉浸式页面

我们来实现一个自动显示/隐藏的导航栏

目标

  1. 页面向下滚动,导航栏跟随滚动进行隐藏
  2. 页面向上滚动到顶,导航栏逐渐显示
  3. 当次向上滚动距离超过30px,导航栏组件显示

分析

实现第一、二点并不难,有的同学可能会想到sticky布局,但是第三点就没办法了。

我的实现方法还是用fixed布局 +页面滚动监听

思路如下:

1.导航栏是固定在页面顶部的,用fixed布局没问题

2.导航栏需要跟随页面滚动逐渐隐藏/显示,就需要监听页面滚动事件,获取页面滚动距离调整导航栏位置,可以用transform实现,顺便用transition实现延迟动效

3.当次向上滚动距离超过30px,设置导航栏位移距离为0,使其完全展示

代码

组件使用了fower这款css-in-js插件,仅对css书写方法有影响,看不懂可以先了解一下

可嵌入自定义导航栏的沉浸式组件

tsx 复制代码
import { View } from '@fower/taro'
import { isH5 } from '@/utils'
import Taro, { usePageScroll } from '@tarojs/taro'
import { useState } from 'react'

const statusBarHeight = isH5 ? 0 : Taro.getWindowInfo()?.statusBarHeight ?? 0
/**
 * 沉浸式页面顶部
 * @param props
 * @returns
 */
const ImmersionTop = (props: {
  navigationHeight?: number
  backgroundColor?: string
  transitionTime?: number
  children: any
}) => {
  const navigationHeight = props.navigationHeight || 50
  // 导航栏过渡动画时间,默认300ms
  const transitionTime = props.transitionTime || 300
  const totalHeight = statusBarHeight + navigationHeight
  // 若状态栏文字颜色为白色,需要设置背景颜色
  const backgroundColor = props.backgroundColor || 'white'
  const children = props.children
  // 导航栏垂直位移距离
  const [navigationTranslateY, setNavigationTranslateY] = useState(0)
  // 当前页面滚动高度
  const [scrollTop, setScrollTop] = useState(0)
  const SHOW_SCROLL_DELTA_Y = 30
  // 监听页面滚动,根据滚动距离,设置导航栏的transform: translateY()
  usePageScroll((info) => {
    const deltaY = info.scrollTop - scrollTop
    if (deltaY < -SHOW_SCROLL_DELTA_Y) {
      // 单次向上滚动距离超过30px,就显示导航栏
      setNavigationTranslateY(0)
    } else {
      // 保证位移距离在0~navigationHeight之间
      let newTranTranslateY = navigationTranslateY + deltaY
      if (newTranTranslateY < 0) {
        newTranTranslateY = 0
      } else if (newTranTranslateY > navigationHeight) {
        newTranTranslateY = navigationHeight
      }
      setNavigationTranslateY(newTranTranslateY)
    }
    setScrollTop(info.scrollTop)
  })
  // 导航栏固定布局
  const navigationStyles: CSSObject = {
    position: 'fixed',
    left: '0px',
    right: '0px',
    top: statusBarHeight + 'px',
    zIndex: 1,
    height: navigationHeight + 'px',
    // 通过transform: translateY()将导航栏移动到状态栏下方,并设置transform动画
    transform: `translateY(-${navigationTranslateY}px)`,
    transition: `transform ${transitionTime}ms ease-out`,
    display: 'flex',
    alignItems: 'center',
    backgroundColor,
    borderBottom: '1px solid #e5e5e5'
  }
  return (
    <>
      <StatusBarPosition
        height={statusBarHeight}
        backgroundColor={backgroundColor}
      />
      <View css={navigationStyles}>{children}</View>
      {/* 导航栏高度占位,置于正常文档流之中 */}
      <View css={{ height: totalHeight + 'px' }} />
    </>
  )
}

// 状态栏高度占位,固定布局,层级在导航栏之上,避免导航栏遮挡,不会覆盖系统状态信息
const StatusBarPosition = (props: {
  height: number
  backgroundColor: string
}) => {
  const style: CSSObject = {
    position: 'fixed',
    left: '0px',
    right: '0px',
    top: '0px',
    zIndex: 2,
    height: props.height + 'px',
    backgroundColor: props.backgroundColor
  }
  return <View css={style} />
}

export default ImmersionTop

页面导航栏组件

tsx 复制代码
import ImmersionTop from '@/components/ImmersionNavigation'
import { Image, Text, View } from '@fower/taro'
import SettingIcon from '@/assets/icons/index/setting.png'
import { isH5 } from '@/utils'

const SettingButton = () => {
  return (
    <View mr-10px p-10px flex>
      <Image src={SettingIcon} circle-20px />
    </View>
  )
}

export default function NavigationBar({}) {
  const profile =
    'https://pubfile.bluemoon.com.cn/group1/new/scrm/961483605c85131353b062f1c8f60104.jpeg'
  return (
    <ImmersionTop>
      <Image src={profile} circle-40px ml-10px />
      <Text ml-auto mr-auto>
        Y
      </Text>
      {isH5 ? <SettingButton /> : <View circle-30px mr-10px />}
    </ImmersionTop>
  )
}

页面组件

tsx 复制代码
import { View } from '@fower/taro'
import NavigationBar from './components/Navigation'

definePageConfig({
  navigationBarTitleText: '首页',
  navigationStyle: 'custom',
  navigationBarTextStyle: 'black'
})
const List = () =>
  Array.from({ length: 100 }).map((_, index) => (
    <View key={index}>列表项-{index + 1}</View>
  ))
export default function Index() {
  return (
    <View>
      <NavigationBar />
      <View css={{ lineHeight: 2 }}>
        <List />
      </View>
    </View>
  )
}

可以在这里拉取示例代码:gitee.com/qq742037091...

实现效果

结束

相关推荐
热爱编程的小曾27 分钟前
sqli-labs靶场 less 8
前端·数据库·less
gongzemin38 分钟前
React 和 Vue3 在事件传递的区别
前端·vue.js·react.js
Apifox1 小时前
如何在 Apifox 中通过 Runner 运行包含云端数据库连接配置的测试场景
前端·后端·ci/cd
树上有只程序猿1 小时前
后端思维之高并发处理方案
前端
庸俗今天不摸鱼2 小时前
【万字总结】前端全方位性能优化指南(十)——自适应优化系统、遗传算法调参、Service Worker智能降级方案
前端·性能优化·webassembly
黄毛火烧雪下2 小时前
React Context API 用于在组件树中共享全局状态
前端·javascript·react.js
Apifox2 小时前
如何在 Apifox 中通过 CLI 运行包含云端数据库连接配置的测试场景
前端·后端·程序员
一张假钞2 小时前
Firefox默认在新标签页打开收藏栏链接
前端·firefox
高达可以过山车不行2 小时前
Firefox账号同步书签不一致(火狐浏览器书签同步不一致)
前端·firefox
m0_593758102 小时前
firefox 136.0.4版本离线安装MarkDown插件
前端·firefox