浮动吸顶、吸底vue指令

使用方式:
<footer v-fixed="{
      fixedPosition: 'bottom', // bottom浮动吸底、top浮动吸顶
      immediate: true, // 初始化页面是否启用浮动
      fixedStyle: { // 浮动后节点样式
        'border-radius': '5px 5px 0 0',
        'background-color': 'rgba(90, 90, 90, 0.4)'
      }
    }">
   <el-button @click="back">返回</el-button>
   <el-button type="primary">确定</el-button>
</footer>
源码:
/**
 *
 * description: dom吸顶、吸底
 *
 */

// 获取上个存在滚动条的节点
function getScrollDom(el) {
  let dom = el
  while (!(dom.scrollHeight > dom.clientHeight) && dom) {
    dom = dom.parentElement
  }
  return dom
}

// 监听节点是否完全处于视口内
class intersectionObserver {
  immediate = false
  intersectionObserver = null
  scrollDom = null
  fixedDom = null
  constructor(scrollDom, fixedDom, { immediate }, revert, fixed) {
    this.scrollDom = scrollDom;
    this.fixedDom = fixedDom;
    this.immediate = immediate;
    this.intersectionObserver = new IntersectionObserver((entries) => {
      if (entries[0].isIntersecting) {
        if (this.immediate) revert()
        this.immediate = true
        return
      };
      if (this.immediate) {
        fixed()
      }
    }, {
      root: document,
      rootMargin: '0px',
      threshold: [0, 1] // 交叉阈值,这里设置为0和1,表示从完全不交叉到完全交叉的整个过程都会触发回调  
    });
  }
  observe() {
    this.intersectionObserver.observe(this.fixedDom)
  }
  disconnect() {
    this.intersectionObserver.disconnect(this.fixedDom)
  }
}
export default {
  inserted: (el, binding, vnode) => {
    /**
     * description: 指令参数说明
     * @param fixedPosition bottom下浮动、top上浮动
     * @param fixedStyle 浮动后附加样式
     * @param immediate 是否页面加载就启用浮动,默认false
     * @return {}
     *
     */
    const { fixedPosition, fixedStyle, immediate } = { fixedPosition: 'bottom', immediate: false, ...binding.value }
    const scrollDom = getScrollDom(el)
    const markNode = document.createElement('div')
    markNode.style.setProperty('transform', `translateY(${el.offsetHeight / 4}px)`)
    el.insertAdjacentElement('beforebegin', markNode)
    // 记录节点原有样式
    const oldStyle = [
      'transition',
      'zIndex',
      'position',
      'margin',
      'width',
      'left',
      'right',
      [fixedPosition],
      ...Object.keys(fixedStyle)
    ].reduce((old, val) => ({
      ...old,
      [val]: el.style.getPropertyPriority(val)
    }), {})
    const domObserver = new intersectionObserver(scrollDom, markNode, { immediate }, function () {
      // 样式还原
      Object.entries(oldStyle).forEach(([k, v]) => {
        el.style.setProperty(k, v)
      })
      el.scrollIntoView({ behavior: 'instant', block: 'end' })
    }, function () {
      const { left, right } = el.getBoundingClientRect()
      el.style.setProperty('transition', 'all 300ms ease-out')
      el.style.setProperty('z-index', 999)
      el.style.setProperty('position', 'fixed')
      el.style.setProperty(fixedPosition, '0px')
      el.style.setProperty('left', left + 'px')
      el.style.setProperty('right', (scrollDom.offsetWidth - right) + 'px')
      el.style.setProperty('margin', '0px')
      el.style.setProperty('width', 'auto')
      // 添加浮动后样式
      Object.entries(fixedStyle).forEach(([k, v]) => {
        el.style.setProperty(k, v)
      })
    })
    domObserver.observe()
  }
}
备注:主要使用技术是IntersectionObserver监听节点是否处于视口,css position:fixed 进行吸底、吸顶浮动。代码有升级空间,可改为不使用fixed浮动,节点固定位置改为可滚动窗口的顶部或底部;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值