指望的使用方式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 addEventListener
和removeEventListener
作一些註冊工做了.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
CodePen Demothis