【Vue项目复习笔记】标题和内容的联动效果

这个功能就是点击了商品就跳到商品对应的位置,点击了参数就跳到参数对应的位置等。以及上下内容的联动效果。

点击标题,滚动到对应的主题

第一步是要监听DetailNavBar的点击

  methods:{
    titleClick(index){
      this.currentIndex=index;
      this.$emit('titleClick',index)
      },
    }

将这个点击事件发出去,在Detail中

 <detail-nav-bar class="detail-nav" @titleClick="titleClick"></detail-nav-bar>

在methods中

  titleClick(index){
      this.$refs.scroll.scroll.scrollTo(0, y, 200);
    },

主要是这里的y值的获取;
先在data()里增加一个变量

  themeTopYs:[],

这个变量里面有4个值,分别对应小标题的滚动位置。那滚动到多少呢?
就需要获取对应的offsetTop,那怎么动态的获取对应的offsetTop呢?
我们先看mouted()里面是否可以拿到值

  mounted() {
    this.themeTopYs.push(0);
    this.themeTopYs.push(参数的OffsetTop);
    this.themeTopYs.push(评论的OffsetTop);
    this.themeTopYs.push(推荐的OffsetTop);
  },

那么参数还有其他的offsetTop怎么获取呢?
要添加对应的ref

<detail-param-info  ref="param" :param-info="paramInfo" ></detail-param-info>
    <detail-comment-info ref="comment" :comment-info="commentInfo" ></detail-comment-info>
    <goods-list  ref="recommend"  :goods="recommends"></goods-list>

分别为其添加对应的ref,上面的代码就变成

mounted() {
    this.themeTopYs.push(0);
    this.themeTopYs.push(this.$refs.param.$el.OffsetTop);
    this.themeTopYs.push(this.$refs.comment.$el.OffsetTop);
    this.themeTopYs.push(this.$refs.recommend.$el.OffsetTop);
  },

我们打印输出一下会发现:
在这里插入图片描述
拿到的东西有3个都是undefined,意味着上面的el里面没有东西,因为我们之前在DetailParamInfo还有其他两个组将上面都做了一个判断

 <div class="param-info" v-if="Object.keys(paramInfo).length !== 0">

只有我们paramInfo里面有值才会渲染页面,意味着它还没有请求数据,不能保证在mouted里面数据就能请求下来,这个不一定。因为在created里面才开始请求,在mouted里面数据不一定到。$el去拿组件的根组件,我们又做了一个判断,没有数据之前,根组件也不会渲染。
我们将其放到created()的getDetail函数里

this.themeTopYs=[];
      this.themeTopYs.push(0);
      this.themeTopYs.push(this.$refs.param.$el.OffsetTop);
      this.themeTopYs.push(this.$refs.comment.$el.OffsetTop);
      this.themeTopYs.push(this.$refs.recommend.$el.OffsetTop);

我们会发现其打印输出仍然没有值,因为要等我们把数据赋值过去以后,稍微等一会儿,等它把页面渲染完以后,才能保证它有值。那该怎么做呢?
有一个叫做this.$nextTick(),它就可以等到页面渲染完以后,回调一次后面的箭头函数。就可以保证他们都有值。

   this.$nextTick(()=>{
        this.themeTopYs=[];
        this.themeTopYs.push(0);
        this.themeTopYs.push(this.$refs.param.$el.OffsetTop);
        this.themeTopYs.push(this.$refs.comment.$el.OffsetTop);
        this.themeTopYs.push(this.$refs.recommend.$el.OffsetTop);
      })
    }),

打印输出
在这里插入图片描述
原因如下:

    // 第一次获取,值不对
    // 原因是:this.$refs.param.$el压根没有渲染
    this.themeTopYs=[];
    this.themeTopYs.push(0);
    this.themeTopYs.push(this.$refs.param.$el.OffsetTop);
    this.themeTopYs.push(this.$refs.comment.$el.OffsetTop);
    this.themeTopYs.push(this.$refs.recommend.$el.OffsetTop);
    console.log(this.themeTopYs);

   this.$nextTick(()=>{
        //第二次获取:值不对
        //值不对的原因是图片没有计算在内
        //根据最新的数据,对应的DOM是已经被渲染出来了
        //但是图片依然没有加载完(目前获取到的offsetTop不包含其中的图片)
        //offsetTop值不对的时候,都是因为图片的问题
        this.themeTopYs=[];

        this.themeTopYs.push(0);
        this.themeTopYs.push(this.$refs.param.$el.OffsetTop);
        this.themeTopYs.push(this.$refs.comment.$el.OffsetTop);
        this.themeTopYs.push(this.$refs.recommend.$el.OffsetTop);
      })

这两次获取均没有值,我们可以用如下办法:
详页的图片加载完以后可以在methods中的imageLoad()方法里面做一个回调,然后我们在回调里面每次都来获取一次

imageLoad(){
      this.$refs.scroll.scroll.refresh()
    
      this.themeTopYs=[];
      this.themeTopYs.push(0);
      this.themeTopYs.push(this.$refs.param.$el.OffsetTop);
      this.themeTopYs.push(this.$refs.comment.$el.OffsetTop);
      this.themeTopYs.push(this.$refs.recommend.$el.OffsetTop);
    },

这个时候,这里面的值肯定会调用很频繁,但是结果一定是对的

对于此时调用特别频繁我们可以用防抖函数
先在data()里面增加一个变量:

getThemeTopY:null,

在created()里面

   //4.给getThemeTopY赋值
        this.getThemeTopY = debounce(() => {
          this.themeTopYs = [];
          this.themeTopYs.push(0);
          this.themeTopYs.push(this.$refs.param.$el.offsetTop);
          this.themeTopYs.push(this.$refs.comment.$el.offsetTop);
          this.themeTopYs.push(this.$refs.recommend.$el.offsetTop);
          // console.log(this.themeTopYs);
        }, 100);

最后在methods中的imageLoad()里

  imageLoad(){
      this.$refs.scroll.scroll.refresh()
      this.getThemeTopY()
    },

最终结果是:
请添加图片描述
总结:
点击标题,滚动到对应的主题

●在detail中监听标题的点击,获取index
●滚动到对应的主题:

  • 获取所有主题的offsetTop
  • 问题:在哪里才能获取到正确的offsetTop
    • 1.created肯定不行,压根不能获取元素
    • 2.mounted也不行, 数据还没有获取到
    • 3.获取到数据的回调中也不行, DOM还没有渲染完
    • 4.$nextTick也不行,因为图片的高度没有被计算在内
    • 5.在图片加载完成后,获取的高度才是正确

滚动内容显示对应的标题

获取滚动位置,这就需要监听滚动。也就是监听scroll事件,它又发出过一个事件

  // 2.监听滚动的位置
    this.scroll.on('scroll', (position) => {
      // console.log(position);
      this.$emit('scroll', position)
    })

所以我们要做的就是在Detail终接收这个事件

<scroll class="content" ref="scroll" @scroll="contentScroll">

在methods中

   contentScroll(position){
      console.log(position);
        }

此时控制台并没有打印内容,原因是在我们的scroll里面,默认情况下我们的0,0的时候就默认不派发这个事件

 probeType: {
      type: Number,
      default: 0
    },

为了能让它派发事件,我们需要传入一个probe-type的值

 <scroll class="content" ref="scroll" :probe-type="3" @scroll="contentScroll" >

此时
请添加图片描述
我们主要是想要获取y值

positoinY和主题中值进行对比 比如说我们的四个参数的值分别为[0, 7938, 9120, 9452 ]
positoinY在0和7938之间,index=0
positoinY在7938和9120 之间,index = 1
positoinY 在9120和9452之间,index = 2
positoinY 超过9120 值,index = 3

我们先在data()里新增一个变量:

  currentIndex:0,

然后在methods中

   contentScroll(position){
      // console.log(position);
      // 1、获取y值
      const positionY=-position.y
      //2、positionY和主题中值进行对比
      //  [0, 7938, 9120, 9452 ]
      //positoinY在0和7938之间,index=0
       // positoinY在7938和9120 之间,index = 1
       // positoinY 在9120和9452之间,index = 2
      // positoinY 超过9120 值,index = 3

      let length=this.themeTopYs.length
      for(let i=0;i<length-1;i++){
        // if(this.currentIndex!==i && ((i<length-1 && positionY >= this.themeTopYs[i] &&
        //   positionY< this.themeTopYs[i+1]) || (i===length-1 && positionY >= this.themeTopYs[i]))){
        if(this.currentIndex!==i && (positionY>=this.themeTopYs[i] && positionY<=this.themeTopYs[i+1])){
          this.currentIndex=i;
          this.$refs.nav.currentIndex=this.currentIndex
        }
      }

其中要给我们的头部导航栏一个nav,让其对应的currentIndex一致。
我们的判断条件其中的条件一:

条件一:this.currentIndex! == i 目的是防止赋值的过程过于频繁
条件二: ((i<length-1 && positionY >= this.themeTopYs[i] &&
positionY< this.themeTopYs[i+1]) || (i== =length-1 && positionY >= this.themeTopYs[i])))
条件1:(i<length-1 && positionY >= this.themeTopYs[i] &&
positionY< this.themeTopYs[i+1])
条件2: ( i = = = length-1 && positionY >= this.themeTopYs[i])
其中条件1是用来判断区间:在0和某个数字之间((i ===length-1 )
条件2是用来判断大于等于: i = = = length-1

我们下面来做一个优化(hack做法):
我们最后在加上一个非常大的值,让其简化我们的判断条件
在created里面push五个值,其中一个就是最大值

this.themeTopYs.push(Number.MAX_VALUE);

然后我们的判断条件就可以简化的写成

positionY>=this.themeTopYs[i] && positionY<=this.themeTopYs[i+1]

考虑到越界的问题,我们将循环里面的length-1。
多占了一些空间,但是提高了性能。

最终的结果:
请添加图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

纵有千堆雪与长街

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值