[Vue] 如何利用intersectionOberver实现全局appear/disappear事件

期望的使用方式

<template>
    <div @appear="onAppear" @disappear="onDisappear">box</div>
</template>

<script>
export default {
    methods: {
        onAppear() { console.log('onAppear') },
        onDisappear() { console.log('onDisappear') }
    }
}
</script>
复制代码

那么就需要让如下操作生效

el.addEventListener('appear', callback)
el.addEventListener('disappear', callback)
el.removeEventListener('appear', callback)
el.removeEventListener('disappear', callback)
复制代码

那么问题来, intersectionOberver是作为appear, disappear事件的触发者, 就需要Hook addEventListenerremoveEventListener做一些注册工作了.

extend(EventTarget.prototype, 'addEventListener', function(eventName) {
  let node = this;
  let ioContext = node.__IO__;

  if (eventName === 'appear' || eventName === 'disappear') {
    // 一个节点需要一个 io 即可
    if (node.__IO__) {
      ioContext.listenerNum++;
      return;
    }

    let io = new IntersectionObserver(entries => {
      const ioContext = node.__IO__;
      const { visible: lastVisible } = ioContext;
      const entry = entries[entries.length - 1];
      const ratio = entry.intersectionRatio;
      const visible = entry.isIntersecting && ratio >= 0;

      if (lastVisible === undefined) {
        ioContext.visible = visible;
      } else if (visible !== lastVisible) {
        ioContext.visible = visible;

        node.dispatchEvent(
          new CustomEvent(visible ? 'appear' : 'disappear', {
            bubbles: false // appear/disappear是节点相关的事件不能冒泡
          })
        );
      }
    });

    node.__IO__ = {
      instance: io,
      listenerNum: 1
    };
    io.observe(node);
  }
});

extend(EventTarget.prototype, 'removeEventListener', function(eventName) {
  let node = this;
  let ioContext = node.__IO__;

  if (eventName === 'appear' || eventName === 'disappear') {
    // 当事件为没有监听器的时候就可以把 io 注销, 释放内存
    if (--ioContext.listenerNum === 0) {
      ioContext.instance.disconnect();
      ioContext.instance = null;
      node.__IO__ = null;
    }
  }
});

function extend(obj, fnName, cb) {
  const oldFn = obj[fnName];
  obj[fnName] = function wrap() {
    let ret;
    oldFn && (ret = oldFn.apply(this, arguments));
    cb && cb.apply(this, arguments);
    return ret;
  };
}
复制代码

实际效果如下:

CodePen Demo

转载于:https://juejin.im/post/5cd10959f265da03a00fe5c6

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值