[Vue] 如何利用intersectionOberver實現全局appear/disappear事件

指望的使用方式vue

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

<script>
export default {
    methods: {
        onAppear() { console.log('onAppear') },
        onDisappear() { console.log('onDisappear') }
    }
}
</script>
複製代碼

那麼就須要讓以下操做生效node

el.addEventListener('appear', callback)
el.addEventListener('disappear', callback)
el.removeEventListener('appear', callback)
el.removeEventListener('disappear', callback)
複製代碼

那麼問題來, intersectionOberver是做爲appear, disappear事件的觸發者, 就須要Hook addEventListenerremoveEventListener作一些註冊工做了.app

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;
  };
}
複製代碼

實際效果以下: ui

appear,disappear.gif

CodePen Demothis

相關文章
相關標籤/搜索