使用自定义事件Directive实现吸顶/吸低效果(vue3+ts)

使用自定义事件Directive实现吸顶/吸低效果(vue3+ts+directive+eventListener)

自定义事件部分:

import type { DirectiveBinding, Directive } from 'vue'

type Position = 'top' | 'bottom'

interface DirectiveBindingValue {
  position?: Position; // 定位方式 吸顶/吸底
  height?: number; // 自身高度(仅定位前有遮挡,且需要展示完整高度时传递)
  clientY?: number; // 偏移距离
}

// 超出视图则固定定位
const affix: Directive = {
  mounted(el, binding: DirectiveBinding) {
    // 监听的容器
    const container = document.getElementsByClassName('view-container')[0]
    if (!container) { return }
    // 传参(可不传) 例如:v-affix="{position:'top',height:36,clientY:23}"
    const bindingValue: DirectiveBindingValue = binding.value ?? {}

    const clientY = bindingValue.clientY ?? 0
    const height = bindingValue.height ?? 0
    const position = bindingValue.position ?? 'top'
    // 定位样式
    const style = `position: fixed;${position}: ${clientY}px;z-index:3`

    // 变化时的方法(默认为吸顶)
    let positionFun = () => {
      // 获取元素的位置信息
      const t = el.getBoundingClientRect()
      // 元素超出顶部时,定位
      if (t.top < clientY || t.top < (clientY + height)) {
        el.setAttribute('style', style)
      } else {
        el.setAttribute('style', '')
      }
    }

    // 固定方式为吸底时
    if (position === 'bottom') {
      positionFun = () => {
        const t = el.getBoundingClientRect()
        const windowHeight = window.innerHeight
        // 元素距离底部的距离小于指定偏移高度时,手动定位
        if (t.bottom + clientY > windowHeight || t.bottom + clientY + height > windowHeight) {
          el.setAttribute('style', style)
        } else {
          el.setAttribute('style', '')
        }
      }
    }

    // 滚动监听
    container.addEventListener('scroll', () => {
      el.setAttribute('style', '')
      positionFun()
    })
  },
  // 销毁前移除事件监听
  unmounted() {
    // 监听的容器
    const container = document.getElementsByClassName('view-container')[0]
    if (!container) { return }
    container.removeEventListener('scroll', () => { })
  }
}
export default affix 

入口文件引入:

import affix from '@/directives/affix'
import { createApp } from 'vue'

const app = createApp({})

app.directive('affix', affix)

组件中使用:

<template>
  <div class="header"></div>
  <div class="view-container">
    <!-- 有遮挡的顶部 -->
    <div v-affix="{ position: 'top', height: 50, clientY: 50 }" class="menu-bar">吸顶</div>
    <div class="content" />
    <!-- 无遮挡的底部 -->
    <div v-affix="{ position: 'bottom', clientY: 0 }" class="menu-bar">吸底</div>
  </div>
</template>

<style scoped>
.header {
  width: 100%;
  height: 58px;
  background: #fff;
}

.view-container {
  border: 1px solid;
  padding: 100px 0 100px 0;
}

.menu-bar {
  width: 100%;
  height: 50px;
  background: rgb(133, 101, 101);
  font-size: 20px;
  color: #fff;
}

.content {
  width: 100%;
  height: 2000px;
}</style>

效果(顶部初始位置,吸顶和吸底效果,低部初始位置):
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值