导航栏可以说是前端页面很常见的一个组件。许多组件库也提供了自己的导航栏

在这篇文章里,我们来实现一个随着页面的滚动样式发生改变的导航栏吧。

在这里插入图片描述


一个通用的顶部导航栏应该具备的能力:

  1. 默认的展示效果 -> 左边后退按钮的图标,中间页面的名称,右边是空白的内容

  2. 可以通过插槽来配置具体的展示样式 -> 左,中,右 三个插槽,父组件可以通过三个插槽来指定

  3. 可以接收从外部指定的一个样式(可以在父组件中指定 navigationBar 的 style)

导航栏的样式布局很简单,三个具名插槽。通过父组件传入的参数判断显示的内容。

NavigationBar.vue

<template>
  <div
    class="nav-bar z-index-max"
	:class="[{ 'iphonex-top': isIphoneX }, { 'bottom-line': pageName }]"
    :style="navBarStyle"
  >
    <div class="left" @click="$emit('onLeftClick')">
      <!-- 默认状态 -->
      <img
        v-if="isDefault && isShowBack"
        src="@img/back.svg"
        alt=""
        srcset=""
      />
      <!-- 定制具名插槽 -->
      <slot name="nav-left"></slot>
    </div>
    <div class="center">
      <!-- 默认状态 -->
      <span class="page-title" v-if="isDefault">{{ pageName }}</span>
      <!-- 定制具名插槽 -->
      <slot name="nav-center"></slot>
    </div>
    <div class="right">
      <!-- 定制具名插槽 -->
      <slot name="nav-right"></slot>
    </div>
  </div>
</template>


<script>
export default {
  props: {
    // 是否展示默认状态
    isDefault: {
      default: true,
      type: Boolean,
    },
    // 是否展示默认后退按钮
    isShowBack: {
      default: true,
      type: Boolean,
    },
    // 默认状态下页面标题的名称
    pageName: {
      default: '',
      type: String,
    },
    // navBar样式
    navBarStyle: {
      type: Object,
      default() {
        return {
          backgroundColor: 'white',
        };
      },
    },
  },
  data() {
    return {
      isIphoneX: window.isIphoneX,
    };
  },
};
</script>


<style lang="scss" scoped>
@import "@css/style.scss";
.nav-bar {
  width: 100%;
  height: $navBarHeight;
  line-height: $navBarHeight;
  display: flex;
  justify-content: space-between;
  // 适配手机 stateBar
  padding-top: $stateBarHeight;

  .left,
  .right {
    display: flex;
    height: 100%;
    width: px2rem(26);
    padding: 0 8px;
    img {
      width: 100%;
      align-self: center;
    }
  }

  .center {
    display: flex;
    height: 100%;
    flex-grow: 1;
    .page-title {
      align-self: center;
      margin: 0 auto;
      font-size: $titleSize;
    }
  }
}

.bottom-line {
  border-bottom: 1px solid $lineColor;
}
</style>

调用父组件处

<template>
  <div class="home" @scroll="onScrollChange" ref="home">
    <navgation-bar :isDefault="false" :navBarStyle="navBarStyle">
      <template v-slot:nav-left>
        <img :src="navBarCurrentSlot.leftIcon" />
      </template>
      <template v-slot:nav-center>
        <search
          :bgColor="navBarCurrentSlot.search.bgColor"
          :hintColor="navBarCurrentSlot.search.hintColor"
          :icon="navBarCurrentSlot.search.icon"
        ></search>
      </template>
      <template v-slot:nav-right>
        <img :src="navBarCurrentSlot.rightIcon" />
      </template>
    </navgation-bar>
  </div>
</template>

在这里插入图片描述

<navgation-bar :isShowBack="false" :pageName="'个人中心'"></navgation-bar>

在这里插入图片描述

监听页面的滚动事件 @scroll="onScrollChange"

data() {
    return {
      // navBar 插槽的样式数据,包含页面未开始滑动的时候插槽的样式 和 页面滑动到锚定点之后的插槽样式
      navBarSlotData: {
        // 正常样式
        normal: {
          leftIcon: require('./img/more-white.svg'),
          search: {
            bgColor: '#ffffff',
            hintColor: '#999999',
            icon: require('./img/search.svg')
          },
          rightIcon: require('./img/message-white.svg')
        },
        // 到达锚定点的样式
        highlight: {
            leftIcon: require('./img/more.svg'),
            search: {
                bgColor: '#d7d7d7',
                hintColor: '#eee',
                icon: require('./img/search-white.svg')
            },
            rightIcon: require('./img/message.svg')
        }
      },
      // navBar 当前使用的插槽样式
      navBarCurrentSlot: {},
      // 顶部样式
      navBarStyle: {
          backgroundColor: '',
          position: 'fixed',
      },
      // 页面滚动值
      scrollTopValue: -1,
      // 锚点值(滑动页面超过锚点值的时候,navBar 样式发生改变)
      ANCHOR_SCROLL_TOP: 160
    }
  },
created() {
  // 页面初始化的时候
  this.navBarCurrentSlot = this.navBarSlotData.normal;
},

监听页面的滚动事件,调用 onScrollChange

  1. 获取到当前页面滚动的距离

  2. 计算 navBar 背景颜色(navBar 背景透明度)。当前滚动的距离 / 锚点值 = navBar 背景透明度 opacity

  3. opacity >= 1,当前滚动的距离等于或者已经超过了锚点值,那就让 navBar 的样式变为高亮显示的

  4. opacity < 1,当前 navBar 插槽的样式则为默认的样式

methods: {
    onScrollChange($event) {
      // 获取到当前页面滚动的距离
      this.scrollTopValue = $event.target.scrollTop
      // 计算 navBar 背景颜色(navBar 背景透明度) 
      let opacity = this.scrollTopValue / this.ANCHOR_SCROLL_TOP
      // 指定 navBar 插槽样式
      if (opacity >= 1) {
        this.navBarCurrentSlotStyle = this.naBarSlotStyle.highlight
      } else {
        this.navBarCurrentSlotStyle = this.naBarSlotStyle.normal
      }
	  // 根据透明比例来设置 navBar 的背景颜色
      this.navBarStyle.backgroundColor = "rgba(255, 255, 255, " + opacity + ")"
    }
  },

中间的插槽可以放置其他的组件,样式就要进行相对应的修改
(px2rem() 这个是我做移动端适配的函数,使用者将 px2rem() 里面的数值取出来加上 px 就可以正常使用,只不过没有适配)

实现效果

在这里插入图片描述

关注公众号:大明贵妇,无套路获取前端学习资料,期待各位客官来临
在这里插入图片描述

Logo

前往低代码交流专区

更多推荐