传统的实现方法是: 监听到scroll事件后,调用目标元素(绿色方块)的getBoundingClientRect()方法,得到它对应于视口左上角的坐标,再判断是否在视口之内。这种方法的缺点是,由于scroll事件密集发生,计算量很大,容易造成性能问题。 交叉观察器
// 开始观察
io.observe(document.getElementById('example'));
// 停止观察
io.unobserve(element);
// 关闭观察器
io.disconnect();
callback一般会触发两次。一次是目标元素刚刚进入视口(开始可见),另一次是完全离开视口(开始不可见)
IntersectionObserverEntry 对象
{
time: 3893.92,
rootBounds: ClientRect {
bottom: 920,
height: 1024,
left: 0,
right: 1024,
top: 0,
width: 920
},
boundingClientRect: ClientRect {
// ...
},
intersectionRect: ClientRect {
// ...
},
intersectionRatio: 0.54,
target: element
}
new IntersectionObserver(
entries => {/* ... */},
{
threshold: [0, 0.25, 0.5, 0.75, 1]
}
);
this.autoTrackExpose = function () {
if (!_this.getEnableExpose()) {
util.log('未开启元素曝光');
}
common.bindEvent(window, 'scroll,click,resize', common.throttling(handleExposeSignal, EXPOSE_THROTTLE_TIME));
handleExposeSignal();
function handleExposeSignal () {
var exposeNode = document.querySelectorAll('['+ EXPOSE_ATTR_FLAG +']');
var viewportRect = common.getViewport();
console.log(11);
exposeNode.forEach(function (node) {
var preStatus = node.isExposed;
// 更新 isExposed
node.isExposed = common.intersectRect(node.getBoundingClientRect(), viewportRect);
node.nodeInfo = common.getTargetInfo(node);
// 节点 destroy时需要 取消曝光心跳
preStatus ? node.isExposed ? null : exitExposeStatus(node) : node.isExposed ? inExposeStatus(node) : null;
});
function updateNodeHbTime (node) {
node.mHbTime = new Date().getTime();
}
function inExposeStatus (node) {
// 发送曝光事件
_this.trackExpose(node.nodeInfo);
updateNodeHbTime(node);
// 曝光心跳
var exposeHbObj = util.merge(node.nodeInfo);
exposeHbObj.expTime = EXPOSE_HB_TIME;
node.mTimeId = setInterval(function () {
_this.trackExposeHb(exposeHbObj);
// 更新 节点曝光时间
updateNodeHbTime(node);
}, EXPOSE_HB_TIME * 1000);
}
function exitExposeStatus (node) {
var exposeHbObj = util.merge(node.nodeInfo);
exposeHbObj.expTime = Math.ceil((new Date().getTime() - node.mHbTime) / 1000);
clearInterval(node.mTimeId);
_this.trackExposeHb(exposeHbObj);
}
}
};