记一次无缝监听元素不可见时吸顶

简介: 记一次无缝监听元素不可见时吸顶

以代码为主,原理大家都懂,只是要做到平滑切换有些问题

需求:

tab栏要在它被滚动到顶部时吸顶。

问题:

会抖动,不流畅

解决过程

分析了一下,实际上当元素吸顶后原来区域的元素占据的位置是空了的,那么也就会有一个下面的元素向上占位的过程,解决方法是填充该区域

代码

// 获取当前滚动条高度
export const getScrollTop = function (params) {
  let scrollTop = 0;
  if (document.documentElement && document.documentElement.scrollTop) {
    scrollTop = document.documentElement.scrollTop;
  } else if (document.body) {
    scrollTop = document.body.scrollTop;
  }
  return scrollTop;
}

这是一个导出的方法,需要后面引入

tab页面代码:

tab的html

<template>
  <div class="match-tab" id="match-tab" :class="{'fixed': tabFixed}" :style="{'top': page.appNavigationHeight + 'px'}">
    <!-- <div class="item" @click="sendChildData(0)">
      <span :class="selectIndex == 0 ? 'active' : ''">实况</span>
    </div> -->
    <div class="item" @click="sendChildData(2)">
      <span :class="selectIndex == 2 ? 'active' : ''">图片墙</span>
    </div>
    <div class="item" @click="sendChildData(1)" >
      <span :class="selectIndex == 1 ? 'active' : ''">时间轴</span>
    </div>
    <div class="item" @click="sendChildData(3)">
      <span :class="selectIndex == 3 ? 'active' : ''">热门</span>
    </div>
  </div>
</template>

style

<style lang="less" scoped>
  .match-tab{
    margin-top: 5px;
    display: flex;
    justify-content: space-between;
    width: 100%;
    background: rgba(255,255,255,.3);
    &.fixed {
      position: fixed;
      z-index: 1;
      top: 0px;
      left: 0px;
      margin-top: 0;
      // padding-bottom: 10px;
      background: rgba(255,255,255,.9);
    }
    .item{
      width: 50%;
      display: flex;
      height: 45px;
      align-items: center;
      justify-content: center;
      color: #333;
      font-size: 15px;
      span{
        display: inline-block;
        height: 100%;
        line-height: 45px;
        box-sizing: border-box;
        &.active{
          border-bottom: 3px solid #49a4f3;
          color: #49a4f3;
        }
      }
    }
  }
</style>

script引入方法

import { getScrollTop } from "@/libs/utils";

tab的几个需要的data

 props:['selectIndex'],
  data(){
    return{
      // selectIndex:this.$parent.tabSelect
      page: this.$parent.page,
      tabFixed: false,
    }
  },

props里的和page无需理会,这是切换tab用的

methods

onScroll(options) {
      const el = document.querySelector(options.el);
      if (!el) return;
      let isLower = false;
      let diffValue = 0;
      let elTop = 0;
      let elHeight = options.isFixed === true ? el.offsetHeight : 0;
      if (typeof options.diffValue === "function") {
        diffValue = options.diffValue(el) || 0;
      }
      // 开始监听滚动条
      window.addEventListener("scroll", () => {
        let scrollTop = getScrollTop();
        if (typeof options.onScroll === "function") {
          options.onScroll(el, scrollTop);
        }
        if (isLower === false) {
          // 元素顶端到可见区域(网页)顶端的距离
          let eleClientTop = el.getBoundingClientRect().top;
          // 网页指定元素触碰顶部时
          if (eleClientTop <= diffValue) {
            elTop = el.offsetTop + elHeight;
            isLower = true;
            if (typeof options.onLower === "function") {
              options.onLower(el);
            }
          }
        } else if (isLower === true) {
          if (scrollTop + elHeight < elTop) {
            elTop = 0;
            isLower = false;
            if (typeof options.onHigher === "function") {
              options.onHigher(el);
            }
          }
        }
      });
    },
    // 监听元素位置
    tabsListener() {
      let versionCode = this.page.versionCode;
      let isInApp = this.page.isInApp;
      if (isInApp && versionCode < 66) {
        return;
      }
      let tabsEl = document.querySelector("#match-tab");
      if (!tabsEl) {
        return;
      }
      //添加占位元素
      function addPlaceholderEl(el) {
        let height = el.offsetHeight;
        console.log(height)
        let placeholderEl = window.document.createElement("div");
        placeholderEl.style.height = height + "px";
        placeholderEl.style.width = "100%";
        el.parentNode.insertBefore(placeholderEl, el.nextSibling);
        return placeholderEl;
      }
      let tabsHeight = tabsEl.offsetHeight + this.page.appNavigationHeight;
      //监听tab栏
      this.onScroll({
        el: "#match-tab",
        isFixed: true,
        diffValue: el => {
          return this.page.appNavigationHeight; //this.page.isInMiniprogram ? 0 : el.offsetHeight;
        },
        onLower: el => {
          this.placeholderEl = addPlaceholderEl(el);
          this.tabFixed = true;
        },
        onHigher: el => {
          this.placeholderEl.parentNode.removeChild(this.placeholderEl);
          this.tabFixed = false;
        }
      });
    }

mounted

setTimeout(this.tabsListener);

至此,已经可以平滑过渡了,需要看效果的可以微信打开http://m.huaxiyou.cc/app/cloudalbum/1000/detail/58查看

相关文章
|
3天前
|
小程序
在uniapp中监听globalData中的值变化
在uniapp中监听globalData中的值变化
143 0
|
3天前
|
JavaScript
【sgDrag】自定义组件:基于Vue开发支持批量声明拖拽元素、被碰撞元素,拖拽全过程监听元素碰撞检测并返回拖拽原始元素、克隆元素及其getBoundingClientRect对象和碰撞接触元素数组。
【sgDrag】自定义组件:基于Vue开发支持批量声明拖拽元素、被碰撞元素,拖拽全过程监听元素碰撞检测并返回拖拽原始元素、克隆元素及其getBoundingClientRect对象和碰撞接触元素数组。
|
3天前
|
小程序
Uniapp 解决组件在官方文档不支持的事件上,接收小程序原生组件事件
Uniapp 解决组件在官方文档不支持的事件上,接收小程序原生组件事件
63 0
|
10月前
|
存储 Android开发 容器
QMUIContinuousNestedScrollLayout——连接滚动容器,专为文章详情页而生
QMUI 在 v1.3.2 提供了一个全新的组件:QMUIContinuousNestedLayout。点击这里可查看使用文档。本文就来聊一聊它的使用场景、设计以及实现。
95 0
activiti 全局流程监听ActivitiEventListener,实现监听不同类型事件,不需要在acitivit中配置任务监听,非常方便
activiti 全局流程监听ActivitiEventListener,实现监听不同类型事件,不需要在acitivit中配置任务监听,非常方便
873 0
activiti 全局流程监听ActivitiEventListener,实现监听不同类型事件,不需要在acitivit中配置任务监听,非常方便
华为快应用-监听属性值变化
华为快应用-监听属性值变化
91 0
|
C++
C/C++ 应用层下遍历驱动列表
实现在应用层下遍历输出驱动文件路径列表信息。
267 0
C/C++ 应用层下遍历驱动列表
|
数据可视化 图形学 流计算
Unity 基础 之 代码动态监听UI交互组件汇总
通过介绍组件面板和代码示例,演示代码监听UI交互组件。
223 0
Unity 基础 之 代码动态监听UI交互组件汇总
|
JavaScript 前端开发 容器