cheungseol / cheungseol.github.io

2 stars 0 forks source link

相交检测 #26

Open cheungseol opened 3 years ago

cheungseol commented 3 years ago

常见的需要相交检测的场景:

过去常用的方法

判断元素是否在浏览器视窗内时,常见的方法使用:

// Old-school scroll event listening (avoid)
window.addEventListener('scroll', () => checkForVisibility)

window.addEventListener('resize', () => checkForVisibility)

function checkForVisibility() { 
animatedElements.map(element => {    
    const distTop = element.getBoundingClientRect().top    
    const distBottom = element.getBoundingClientRect().bottom    
    const distPercentTop = Math.round((distTop / window.innerHeight) * 100)    
    const distPercentBottom = Math.round((distBottom / window.innerHeight) * 100)
   // Based on this position, animate element accordingly  
}}

getBoundingClientRect() 方法会触发浏览器 trigger reflows,可能会造次性能问题。这种方式实现的相交检测通常要用到事件监听,并且需要频繁调用Element.getBoundingClientRect() 方法以获取相关元素的边界信息。事件监听和调用 Element.getBoundingClientRect()都是 在主线程上运行,因此频繁触发、调用可能会造成性能问题。这种检测方法极其怪异且不优雅。

(假如有一个无限滚动的网页,开发者使用了一个第三方库来管理整个页面的广告,又用了另外一个库来实现消息盒子和点赞,并且页面有很多动画(译注:动画往往意味着较高的性能消耗)。两个库都有自己的相交检测程序,都运行在主线程里,而网站的开发者对这些库的内部实现知之甚少,所以并未意识到有什么问题。但当用户滚动页面时,这些相交检测程序就会在页面滚动回调函数里不停触发调用,造成性能问题,体验效果让人失望。)

推荐的方法

Intersection Observer API 允许配置一个回调函数,当以下情况发生时会被调用

使用 IntersectionObservers 不会有性能问题:提供了一种 异步 检测目标元素与祖先元素或 viewport 相交情况变化的方法。

// Create an intersection observer with default options, that 
// triggers a class on/off depending on an element’s visibility
// in the viewport

const animationObserver = new IntersectionObserver((entries, observer) => {  
    for (const entry of entries) {    
           entry.target.classList.toggle('build-in-animate', entry.isIntersecting)  
    }
});

// Use that IntersectionObserver to observe the visibility
// of some elements

for (const element of querySelectorAll('.js-build-in')) { 
   animationObserver.observe(element);
}

Intersection Observer API 会注册一个回调函数,每当被监视的元素进入或者退出另外一个元素时(或者 viewport ),或者两个元素的相交部分大小发生变化时,该回调方法会被触发执行。这样,我们网站的 主线程不需要再为了监听元素相交而辛苦劳作,浏览器会自行优化元素相交管理

注册的 回调函数将会在主线程中被执行。所以该函数执行速度要尽可能的快。如果有一些耗时的操作需要执行,建议使用 Window.requestIdleCallback() 方法

Intersection Observer API 无法提供重叠的像素个数或者具体哪个像素重叠,他的更常见的使用方式是——当两个元素相交比例在 N% 左右时,触发回调,以执行某些逻辑

IntersectionObserver API 并不会每次在元素的交集发生变化的时候都会执行回调。相反它使用了thresholds参数。当你创建一个observer的时候,你可以提供一个或者多个number类型的数值用来表示target元素在root元素的可见程序的百分比 API的回调函数只会在元素达到thresholds规定的阈值时才会执行

例如,当想要 target 在 root 元素中的可见性每超过25%或者减少25%的时候都通知一次。你可以在创建observer的时候指定thresholds属性值为[0, 0.25, 0.5, 0.75, 1],你可以通过检测在每次交集发生变化的时候的都会传递回调函数的参数"IntersectionObserverEntry.isIntersecting"的属性值来判断target元素在root元素中的可见性是否发生变化。如果isIntersecting 是 true,target元素的至少已经达到thresholds属性值当中规定的其中一个阈值,如果是false,target元素不在给定的阈值范围内可见。