Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

interSectionObserver实践 #34

Open
keep-run opened this issue May 18, 2021 · 0 comments
Open

interSectionObserver实践 #34

keep-run opened this issue May 18, 2021 · 0 comments

Comments

@keep-run
Copy link
Owner

简介

interSection Observer 提供了一种异步检测目标元素与指定祖先元素相交情况变化的方法;基于这个API,最常见的两个使用场景是:

  • 移动端无限滚动;
  • 图片懒加载;
    下边具体看看这两个场景的具体例子;

图片懒加载

传统的做法都是绑定scroller事件,滚动过程通过Element.getBoundingClientRect()计算是否出现在可视区,然后加载图片,然而这些都是在主线程频繁触发,会影响性能;

import React, { useState, useEffect, useRef } from 'react';
import { imgs } from './imgs.ts'  //这个文件会返回一个很长的图片列表

export default () => {

  useEffect(() => {
    const interSectionObserver = new IntersectionObserver(entries => {
      // 当目标元素与视窗(默认的祖先元素)交叉超过阈值(默认为0)时的回调,entries是所有观测的目标元素
      entries.forEach(item => {
        if (item.isIntersecting) {  // true表示超过交叉阈值,取dataset.src复制给src,实现图片加载
          const imgDom = item.target
          const imgSrc = imgDom.dataset.src
          imgDom.src = imgSrc
          interSectionObserver.unobserve(imgDom) // 图片加载完毕之后,取消观测该元素,否则来回滚动会继续触发;
        }
      })
    }, {
      rootMargin: "350px 0px"  // 这个会使得根元素的矩形大小上下各增加350px; 实现图片没有出现在屏幕可视区,但是开始加载;
    })

    Array.from(document.querySelectorAll('img')).forEach(dom => {
      interSectionObserver.observe(dom)  // 找到所有的img元素,并观测;
    })

  }, [])

  return (<div style={{ height: "600px", overflow: "scroll" }}>
    {
      imgs.map((item, index) => {
        return <div style={{ 'justifyContent': "center" }} key={item}>
          {/** 先把图片资源存储在data-src中 */}
          <img data-src={item} style={{ width: "300px", height: "165px", margin: "20px" }} />
          <span>{index}</span>
        </div>
      })
    }
  </div>)
}

无限滚动

这个场景在移动端会比较常见,滚定到底部实现自动分页,一般的做法也是绑定scoller事件,判断距离底部一定的值时发接口。问题和图片懒加载是一样的,利用IntersectionObserver就可以改善很多,在最底部放一个元素,观测该元素的交叉状况即可实现分页;

import React, { useState, useEffect, useRef } from 'react';

const bgColors = ['#4e61d4', '#ccc', '#999', 'pink', 'yellow']
const generateBlock = (counts: Number) => {
  return new Array(counts).fill(0).map(() => {
    const backgroundColor = bgColors[Math.floor(Math.random() * bgColors.length)]
    return <div style={{ height: "50px", margin: "20px 0", backgroundColor }} />
  })
}

export default function () {
  const [blocks, setBlocks] = useState(generateBlock(10));
  const footerRef = useRef('')

  useEffect(() => {
    const interSectionObserver = new IntersectionObserver((enteries) => {
      // 可见
      if (enteries[0].isIntersecting) {
        addblocks(10)  //增加10个背景色随机的块(模拟分页)
      }
    },{
      rootMargin:"50px 0px"
    });
    if (footerRef.current) {
      interSectionObserver.observe(footerRef.current)
    }
  }, [])

  const addblocks = (counts: Number) => {
    setBlocks((prevBlocks) => ([...prevBlocks, ...generateBlock(counts)]))
  }

  return (
    <>
      <div>无线向下滚动</div>
      {[...blocks]}
      <div ref={footerRef} style={{ height: "1px" }}>footer</div>
    </>
  );

}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant