交叉口观察员API案例研究



朋友们,美好的一天!

总览


Intersection Observer API(IOA)允许应用程序异步观察元素(目标)与其父元素(根)或视口(视口)的交集。换句话说,每次目标元素与根或视口相交时,此API都会提供对特定函数的调用。

使用示例:

  • 延迟或延迟加载图像
  • 无休止的页面滚动
  • 接收有关广告可见度的信息,以计算印象费用
  • 在用户的视野中启动过程或动画


要开始使用IOA,您需要使用构造函数来创建带有两个参数的观察者对象-回调函数和设置:

// 
let options = {
    root: document.querySelector('.scroll-list'),
    rootMargin: '5px',
    threshold: 0.5
}

//   
let callback = function(entries, observer){
    ...
}

// 
let observer = new IntersectionObserver(callback, options)

设定:

  • root-充当目标视口的元素(目标元素的祖先,视口为null)
  • rootMargin-根周围的边距(CSS中的边距,默认情况下所有边距均为0)
  • 阈值-一个数字或数字数组,指示目标和根的交点的可接受百分比

接下来,创建目标元素,观察者将对其进行观察:

let target = document.querySelector('.list-item')
observer.observe(target)

回调调用返回一个对象,该对象包含目标元素发生的更改的记录:

let callback = (entries, observer) => {
    entries.forEach(entry => {
        // entry () - 
        //   entry.boundingClientRect
        //   entry.intersectionRatio
        //   entry.intersectionRect
        //   entry.isIntersecting
        //   entry.rootBounds
        //   entry.target
        //   entry.time
    })
}

该网络充满了有关理论的信息,但是有关使用IOA的实践的材料很多。我决定填补这一空白。

例子


延迟(延迟)图像加载


任务:在用户滚动页面时上传(显示)图像。

编码:

//    
window.onload = () => {
    //  
    const options = {
        //    -  
        root: null,
        //  
        rootMargin: '0px',
        //   -  
        threshold: 0.5
    }

    //  
    const observer = new IntersectionObserver((entries, observer) => {
        //   - 
        entries.forEach(entry => {
            //    
            if (entry.isIntersecting) {
                const lazyImg = entry.target
                //     -   
                console.log(lazyImg)
                //   
                lazyImg.style.background = 'deepskyblue'
                //  
                observer.unobserve(lazyImg)
            }
        })
    }, options)

    //       img  
    const arr = document.querySelectorAll('img')
    arr.forEach(i => {
        observer.observe(i)
    })
}

结果:


视口外的容器背景为白色。



当您跨过查看区域一半时,背景变为天蓝色。

Codepen

→  Github

图像替换


任务:当用户滚动页面时,将占位符图像更改为原始图像。

编码:

window.onload = () => {
    const observer = new IntersectionObserver((entries, observer) => {
        entries.forEach(entry => {
            if (entry.isIntersecting) {
                console.log(entry)
                //        "data-src"
                entry.target.src = entry.target.dataset.src
                observer.unobserve(entry.target)
            }
        })
    }, { threshold: 0.5 })

    document.querySelectorAll('img').forEach(img => observer.observe(img))
}

结果:


第一张图像已上传,因为它在查看区域中。第二个是占位符。



当您进一步滚动时,占位符将替换为原始图像。

Codepen

→  Github

更改容器背景


任务:当用户在那里滚动页面并返回时,更改容器的背景。

编码:

window.addEventListener('load', event => {
    let box = document.querySelector('div')
    // ratio -   
    let prevRatio = 0.0

    let observer = new IntersectionObserver((entries, observer) => {
        entries.forEach(entry => {
            let curRatio = entry.intersectionRatio
            
            //      -  -
            //           (  )
           //      
            curRatio > prevRatio ? entry.target.style.background = `rgba(40,40,190,${curRatio})` : entry.target.style.background = `rgba(190,40,40,${curRatio})`

            prevRatio = curRatio
        })
    }, {
        threshold: buildThresholdList()
    })

    observer.observe(box)
    
    //    
    //      20 ,   
    function buildThresholdList() {
        let thresholds = []
        let steps = 20

        for (let i = 1.0; i <= steps; i++) {
            let ratio = i / steps
            thresholds.push(ratio)
        }
        return thresholds
    }
})

结果:


容器背景从



浅蓝色...



变为蓝色... 变为浅红色。

Codepen

→  Github

处理视频


任务:暂停正在运行的视频,然后根据落入查看区域的视频重新开始播放。

编码:

window.onload = () => {
    let video = document.querySelector('video')

    let observer = new IntersectionObserver(() => {
        //   
        if (!video.paused) {
            //  
            video.pause()
        //      (   > 0)
        } else if(video.currentTime != 0) {
            //  
            video.play()
        }
    }, { threshold: 0.4 })

    observer.observe(video)
}

结果:



视频在观看区域中时,将播放。



一旦视频超出观看区域40%以上,其播放就会暂停。如果您查看的观看区域>视频的40%,将继续播放。

Codepen

→  Github

页面浏览进度


任务:在用户滚动页面时显示查看页面的进度。

编码:

//          
let p = document.querySelector('p')
// n -   
let n = 0

let observer = new IntersectionObserver((entries, observer) => {
    entries.forEach(entry => {
        if(entry.isIntersecting){
            // observer   div
            //       
            //     
            p.textContent = `${n++} div viewed`
            observer.unobserve(entry.target)
        }
    })
}, {threshold: 0.9})

document.querySelectorAll('div').forEach(div => observer.observe(div))

结果:该



页面刚刚加载,因此我们尚未查看任何容器。



到达页面末尾时,该段落显示有关查看4格的信息。

Codepen

→  Github

无限滚动


任务:实现无尽的清单。

编码:

let ul = document.querySelector('ul')
let n = 1

//    
function createLi(){
    li = document.createElement('li')
    li.innerHTML = `${++n} item`
    ul.append(li)
}

//  ,        
//     
//         li
//       () 
let observer = new IntersectionObserver((entries, observer) => {
    entries.forEach(entry => {
        if (entry.isIntersecting) {
            createLi()
        }
        observer.unobserve(entry.target)
        observer.observe(document.querySelector('li:last-child'))
    })
}, {
    threshold: 1
})

observer.observe(document.querySelector('li'))

结果:



我们有12个列表项。最后一项在视口之外。



当您尝试到达最后一个元素时,将创建一个对用户隐藏的新(最后)元素。等等到无穷大。

Codepen

→  Github

调整父母大小时调整孩子大小


任务:建立一个元素的大小对另一个元素的依赖性。

编码:

//      -   
//      
let info = document.querySelector('.info')
let parent = document.querySelector('.parent')
let child = document.querySelector('.child')
//     50px  
child.style.width = parent.offsetWidth - 50 + 'px'
//     
info.textContent = `child width: ${child.offsetWidth}px`

let options = {
    //      
    root: parent,
    threshold: 1
}

let observer = new IntersectionObserver((entries, observer) => {
    entries.forEach(entry => {
        //         50px
        if ((entry.target.parentElement.offsetWidth - entry.target.offsetWidth) < 50) {
            //     50px
            entry.target.style.width = entry.target.offsetWidth - 50 + 'px'
        }
    })
}, options)

observer.observe(child)

//  ,  ,       IOA
//       resize
window.addEventListener('resize', () => {
    info.textContent = `child width: ${child.offsetWidth}px`
    if ((parent.offsetWidth - child.offsetWidth) > 51) {
        child.style.width = child.offsetWidth + 50 + 'px'
    }
})

结果:



初始状态。



当减小父元素的宽度时,子元素的宽度减小。同时,它们之间的距离几乎总是等于50px(“几乎”是由于采用了反向机制)。

Codepen

→  Github

处理动画


任务:在可见时为对象设置动画。

编码:

//        
//     
//  - 
let observer = new IntersectionObserver((entries, observer) => {
    entries.forEach(entry => {
        entry.isIntersecting ? entry.target.classList.replace('to-left', 'to-right') : entry.target.classList.replace('to-right', 'to-left')
    })
}, {
    threshold: .5
})

observer.observe(document.querySelector('img'))

结果:



我们看到了Bart头部的一部分。巴特按下到查看区域的左侧。



如果超过50%的Bart落入观看区域,他将移至中间。当超过50%的Bart离开查看区域时,它将返回其初始位置。

Codepen

→  Github

谢谢您的关注。

All Articles