uni-appH5项目实现导航区域与内容区域联动效果

一、需求描述

将导航区域与内容区域实现联动,即点击导航区域,内容区滚动到对应位置,内容区滚动过程中根据内容定位到相对应的导航栏。

效果如下:

侧边导航与内容联动效果

二、功能实现思路分析汇总:

三、具体代码

1、功能一

右侧内容区scroll-view的scroll-into-view属性绑定id,当触发左侧导航2的点击事件时设置subjectId的值,从而实现滚动到对应区域。

 <!-- 右侧内容区 -->
    <scroll-view
      :scroll-y="true"
      scroll-with-animation
      :scroll-into-view="subjectId"
      class="deep-right"
      @scroll="scrolling"
    >
      <view
        v-for="(subject, index) in subjectList"
        :id="'subject' + index"
        :key="index"
        class="subject-con"
      >
        <view
          v-for="(son, i) in subject.sonList"
          :key="i"
          class="subject-item"
          @click="goToDetail(son)"
        >
          <view>{{ son.code }}</view>
          <view>{{ son.name }}</view>
        </view>
      </view>
    </scroll-view>
  const subjectId = ref(''); //scroll-into-view 绑定值
  const navChange = (index: any) => { // 点击左侧导航栏触发
    navAvtive.value = index;
    subjectId.value = 'subject' + index;
  };

2、功能二

2.1、方案一

滚动方法中,获取各个块级元素距离视口顶部的距离Top1,获取导航区域1距离视口顶部的距离Top2,将Top1与Top2比较,Top1<Top2即代表块级元素滚动到导航1的下方了。

 const scrolling = (e: any) => {
    //subjectList 是右侧内容区的数据
    subjectList.value.forEach((item, index) => {
      //动态循环判断 Top1和Top2 的大小,将比较过程封装成方法调用
      if (isInViewport(document.getElementById(`subject${index}`))) {
        if (slideNavList.value) {
            // 因为我这里左侧导航是封装的子组件,因此调用左侧导航组件的setTabActive方法,修改导航激活tab
          slideNavList.value.setTabActive(index);
        }
      }
    });
  };

动态比较过程的方法封装

 const isInViewport = (element: any) => {
    //getBoundingClientRect() JS的方法,用于获取元素位置和尺寸
    // careerMap-nav 是顶部横向导航1块元素的Id
    const rect = element.getBoundingClientRect();
    return (
    // 起初这里用的是Math.abs(rect.top),发现上滑时左侧导航定位不准,打印发现 上滑过程中rect.top会起初是赋值然后逐渐增加
     rect.top <=
      document.getElementById('careerMap-nav')?.getBoundingClientRect()?.bottom 
        //careerMap-nav 元素下边界相对于视口顶部的距离
    );
  };

Js中的getBoundingClientRect · 前端押题宝典 · 看云getBoundingClientRect()方法相关指路:Js中的getBoundingClientRect · 前端押题宝典 · 看云

2.2方案二:

// 获取一级学科距离父元素的top值
  const subjectPosition = ref([])
  const getPositionTop = async () => {
    setTimeout(() => {
      const query = uni.createSelectorQuery().in(this);
      query
        .selectAll(".subject-con")
        .boundingClientRect((data) => {
          console.log("得到布局位置信息" + JSON.stringify(data));
          let height = 0
          data.forEach((item: any) => {
            // console.log("节点顶部离页面顶部的距离为" + item.top);
            // item.height累加
            height = height+item.height
            subjectPosition.value.push(height); //和父元素的距离
          });
          console.log("subjectPosition.value",subjectPosition.value);
        })
        .exec();
    }, 100)
}
// 找到scrollTop介于数组中哪两个元素之间
const getBounds =(number:any, sortedArray:any) =>{
  let lowerBound = -1;
  let upperBound = sortedArray.length;
  for (let i = 0; i < sortedArray.length; i++) {
    if (sortedArray[i] === number) {
      // 如果数组中存在给定的数字,则直接返回其索引
      return { lowerBound: i, upperBound: i };
    } else if (sortedArray[i] > number) {
      // 如果当前元素大于给定的数字,则找到了上界
      upperBound = i;
      break; // 因为数组是升序的,所以不需要再继续查找
    }
    // 否则,当前元素小于给定的数字,继续查找
    lowerBound = i;
  }
  // 如果循环结束仍未找到上界,upperBound保持数组长度
  return { lowerBound, upperBound: upperBound < sortedArray.length ? upperBound : sortedArray.length - 1 };
}
const slideNavList = ref();
  const scrolling = (e: any) => {
    console.log("scrollTop", e.detail.scrollTop);
    const { lowerBound, upperBound }=getBounds( e.detail.scrollTop,subjectPosition.value)
    console.log(lowerBound, upperBound);
    if (slideNavList.value) {
      slideNavList.value.setTabActive(upperBound);
    }
  }

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值