(21)详情页开发——③ Header 组件(实现 Header 渐隐渐现) | Vue.js 项目实战: 移动端“旅游网站”开发

207 阅读3分钟
转载请注明出处,未经同意,不可修改文章内容。

🔥🔥🔥“前端一万小时”两大明星专栏——“从零基础到轻松就业”、“前端面试刷题”,已于本月大改版,合二为一,干货满满,欢迎点击公众号菜单栏各模块了解。

1 需求

详情页的头部组件,有两个部分组成:

  • 当进入详情页时,左上角有一个“返回”按钮;
  • 当向下滚动查看内容时,逐渐出现一个顶部的导航栏,导航栏左边也有一个“返回”按钮。

当点击任意“返回”按钮后,都能回到首页:

01.gif

2 Header 布局

🔗前置知识:

《详情页开发——① 动态路由和 Banner 组件布局》

需求分析:整个 Header 由两个部分组成,一个圆形返回按钮和一个导航栏。其中圆形返回按钮,我们可以采用绝对定位;导航栏则需要在页面滚动时,依然在窗口顶部保持不动,所以采用固定定位。

1️⃣在 detail 下的 components 中新建一个 Header.vue

<template>
  <div> <!-- 1️⃣-②:最外层 div 中有两个 div,一个类名为 header-abs,一个为 header-fixed; -->

    <div class="header-abs"> <!-- 1️⃣-③:.header-abs 为绝对定位的返回按钮,里边有一个返回
														 图标,类名为 header-abs-back; -->
      <span class="iconfont header-abs-back">&#xe658;</span>
    </div>
    <div class="header-fixed"></div>
  </div>
</template>

<script>
export default {
  name: 'DetailHeader' // 1️⃣-①:组件命名为 DetailHeader;
}
</script>

<style lang="stylus" scoped>
.header-abs /*
  					1️⃣-④:.header-abs 绝对定位,上、左距离为 0.2rem,宽、高为 0.8rem,
  					border-radius 为 0.4rem(做一个圆形),line-height 为 0.8rem,内容居中,
  					背景色为 0.8 透明度的黑色; */
  position: absolute
  top: .2rem
  left: .2rem
  width: .8rem
  height: .8rem
  line-height: .8rem
  text-align: center
  border-radius: .4rem
  background: rgba(0, 0, 0, .8)

  .header-abs-back /* 1️⃣-⑤:返回图标颜色为白色,大小为 0.56rem; */
    color: #fff
    font-size: .56rem
</style>

1️⃣-⑥:打开 detail 下的 Detail.vue 使用 Header 组件;

<template>
  <div>
    <detail-banner></detail-banner>

    <detail-header></detail-header> <!--1️⃣-⑨:使用 Header 组件;-->

    <div class="content"></div> <!-- ❗️在详情页中添加一个 div,类名为 content,设置它的高
																为 20rem 使详情页能够滚动(后边我们再添加别的内容撑开); -->
  </div>
</template>

<script>
import DetailBanner from './components/Banner'

import DetailHeader from './components/Header' // 1️⃣-⑦:引入 Header.vue;
  
export default {
  name: 'Detail',
  components: {
    DetailBanner,

    DetailHeader // 1️⃣-⑧:注册 Header 组件;
  }
}
</script>

<style lang="stylus" scoped>
.content /* ❗️设置 .content 高为 20rem 撑开内容。 */
  height: 20rem
</style>

保存后,返回页面查看,Header 组件正确显示,第一部分的“返回”按钮,布局完成:

1️⃣-⑩:返回 Header.vue,给返回按钮添加 router-link;

<template>
  <div>
    <!-- 1️⃣-⑪:使用 router-link 标签替换 .header-abs的 div 标签,tag 设为 div,
		添加 to 属性跳转至首页 /(❗️这种 router-link 的用法,在详情页开发的“动态路由”已使用过)。 -->
    <router-link tag="div" to="/" class="header-abs">
      <span class="iconfont header-abs-back">&#xe658;</span>
    </router-link>
    <div class="header-fixed"></div>
  </div>
</template>

<script>
export default {
  name: 'DetailHeader'
}
</script>

<style lang="stylus" scoped>
.header-abs
  position: absolute
  top: .2rem
  left: .2rem
  width: .8rem
  height: .8rem
  line-height: .8rem
  text-align: center
  border-radius: .4rem
  background: rgba(0, 0, 0, .8)
  .header-abs-back
    color: #fff
    font-size: .56rem
</style>

保存后,返回页面查看。页面显示正常,点击“返回”按钮可跳转至首页,详情页可滚动:

02.gif

2️⃣详情页导航栏的布局与城市选择页导航栏相近,我们可以复制这部分的代码粘贴过来进行调整:

<template>
  <div>
    <router-link tag="div" to="/" class="header-abs">
      <span class="iconfont header-abs-back">&#xe658;</span>
    </router-link>

    <div class="header-fixed">  <!-- 2️⃣-①:.heider 改为 .header-fixed; -->
      <router-link to="/">
        
        <!-- 2️⃣-②:图标类名改为 header-fixed-back; -->
        <span class="iconfont header-fixed-back">&#xe658;</span>
      </router-link>
      
      景点详情 <!-- 2️⃣-③:文字内容改为景点详情; -->
    </div>
  </div>
</template>

<script>
export default {
  name: 'DetailHeader'
}
</script>

<style lang="stylus" scoped>
  
/* 2️⃣-④:引入 varibles.styl; */
@import '~styles/varibles.styl'

.header-abs
  position: absolute
  top: .2rem
  left: .2rem
  width: .8rem
  height: .8rem
  line-height: .8rem
  text-align: center
  border-radius: .4rem
  background: rgba(0, 0, 0, .8)
  .header-abs-back
    color: #fff
    font-size: .56rem

.header-fixed /* 2️⃣-⑤:将 .header 改为 .header-fixed,position 改为固定定位 fixed,
  						top、left、right 为 0,添加 z-index 值为 2; */
  position: fixed
  top: 0
  left: 0
  right: 0
  height: $headerHeight
  z-index: 2
  line-height: $headerHeight
  color: #fff
  text-align: center
  font-size: .32rem
  background: $bgColor
  .header-fixed-back
    position: absolute
    top: 0
    left: 0
    width: .64rem
    text-align: center
    font-size: .56rem
    color: #fff
</style>

保存后,返回页面查看。点击导航栏的“返回”图标可回到首页;在详情页向下滚动时,导航栏固定显示在页面顶部:

03.gif

3 Header 逻辑

🔗前置知识:
《JavaScript 基础——浏览器提供的对象:② DOM》
《JavaScript 基础——JS 事件:① 事件流和 DOM2 事件处理程序》
《JavaScript 基础——JS 事件:③ 常见事件使用》
《Vue 入门——⑥ Class 与 Style 绑定》

需求分析:一般情况下,显示单独的“返回”按钮,导航栏在隐藏状态;当滚动到一定距离时,导航栏才出现,“返回”按钮隐藏。所以,可以通过改变一个变量来控制它们状态切换。而导航栏的渐隐渐现效果,可以使用动态绑定样式来实现,在滚动时去动态改变它的透明度 opacity。

04.gif

3️⃣打开 detail 下 components 中的 Header.vue

<template>
  <div>
    <router-link
      tag="div"
      to="/"
      class="header-abs"
      v-show="showAbs"
    > <!-- 3️⃣-②:.header-abs 上添加 v-show 指令,值为 showAbs 变量
      (即,默认显示返回图标); -->

      <span class="iconfont header-abs-back">&#xe658;</span>
    </router-link>

    <div
      class="header-fixed"
      v-show="!showAbs"
      :style="opacityStyle"
    > <!-- 3️⃣-③:.header-fixed 添加 v-show,值为 !showAbs(即,与 showAbs 值相反时显示
      导航栏);
      3️⃣-⑨:动态绑定样式为 opacityStyle; -->

      <router-link to="/">
        <span class="iconfont header-fixed-back">&#xe658;</span>
      </router-link>
      景点详情
    </div>
  </div>
</template>

<script>
export default {
  name: 'DetailHeader',
  data () {
    return {
      showAbs: true, // 3️⃣-①:在 data 中定义一个变量 showAbs,默认为 true;

      opacityStyle: { // 3️⃣-⑧:定义一个样式变量 opacityStyle,默认 opacity 值为 0;
        opacity: 0
      }
    }
  },
  methods: {
    handleScroll () { // 3️⃣-⑤:在 methods 中定义 handleScroll 方法;

      const top = document.documentElement.scrollTop /*
                                                     3️⃣-⑥:定义一个变量 top,用来存储
                                                     滚动条滚动的位置;
                                                      */

      if (top > 50) { /*
                      3️⃣-⑦:如果 top 大于 50 时,showAbs 为 false(即,显示导航栏),
                      否则 showAbs 为 true;
                       */

        let opacity = top / 150 /*
                                3️⃣-⑩:定义一个变量 opacity,它的值为 top 除以 150
                                (即,我们让导航栏透明度在滚动距离 50~150 时进行变化);
                                 */

        opacity = opacity > 1 ? 1 : opacity /*
                                            3️⃣-⑪:opacity 的值是否大于 1,如果是,就让它
                                            等于 1,如果不是,则等于它本身;
                                             */

        this.opacityStyle = {opacity} // 3️⃣-⑫:把 opacity 赋值给 opacityStyle;

        this.showAbs = false
      } else {
        this.showAbs = true
      }
    }
  },
  activated () { /*
                 3️⃣-④:在 activated 生命周期函数中,全局绑定 scroll 事件,事件触发后
                 执行 handleScroll 方法;
                  */
    window.addEventListener('scroll', this.handleScroll)
  },
  deactivated () { /*
                   3️⃣-⑬:最后,不能忘记对全局事件解绑。我们在 deactivated 生命周期函数中解绑
                   scroll 事件
                   (❗️使用 keep-alive 时与 activated 对应的生命周期函数,它在我们从当前组件
                   切换到另一个组件时被调用)。
                    */
    window.removeEventListener('scroll', this.handleScroll)
  }
}
</script>

<style lang="stylus" scoped>
@import '~styles/varibles.styl'
.header-abs
  position: absolute
  top: .2rem
  left: .2rem
  width: .8rem
  height: .8rem
  line-height: .8rem
  text-align: center
  border-radius: .4rem
  background: rgba(0, 0, 0, .8)
  .header-abs-back
    color: #fff
    font-size: .56rem
.header-fixed
  position: fixed
  top: 0
  left: 0
  right: 0
  z-index: 2
  height: $headerHeight
  line-height: $headerHeight
  color: #fff
  text-align: center
  font-size: .32rem
  background: $bgColor
  .header-fixed-back
    position: absolute
    top: 0
    left: 0
    width: .64rem
    text-align: center
    font-size: .56rem
    color: #fff
</style>

保存后,返回页面查看。页面显示正常,控制台无报错。当在详情页上下滚动时,导航栏也有了渐隐渐现的效果:

05.gif

以上,我们完成了详情页的 Header 组件。

祝好,qdywxs ♥ you!