react-图片节点缓存策略(避免图片频繁切换出现的问题)

770 阅读2分钟

开发过程中,什么样的功能都可能碰到,例如:我这里有几十张不同亮度的路灯图片,调节亮度时通过更换图片来实现不同亮度的切换效果(ps:本人还没实力使用 react 绘制这么好的光效😂)

demo案例(案例换成其他网络图片效果会更明显)

首页放置一个Slider用于切换,底部div给一个id,方便查询动态插入节点

然后我们先默认加载一张背景图片和其他所有还没使用的图片

function App() {
  useEffect(() => {
    //默认加载全部
    // HomeBkgImageManager.shared.loadImages()
    //显示图片
    HomeBkgImageManager.shared.loadImageWithoutIndex(0)
    //加载剩余图片,避免重复加载,可以改进
    HomeBkgImageManager.shared.showImageWithIndex(0)
  }, [])

  return (
    <div
      id="home_bkg"
      style={{ width: '100%', height: '100%' }}>
      
      <Slider 
        style={{
          position: 'absolute',
          left: 0,
          top: 0,
          backgroundColor: 'white',
          width: 100,
          zIndex: 1000
        }}
        onChange={e => {
          HomeBkgImageManager.shared.showImageWithIndex(e)
        }}
        min={0}
        max={10}
        />
    </div>
  );
}

缓存策略,如要缓存在内存中,如下所示,并且演示了如何动态创建节点(非声明式的创建节点),并加入到了对应节点中

大致逻辑:通过提前创建图片 img 节点,下载成功后(直接加入集合倒是也可以,下载失败做一下处理),将节点加入到集合中,方便我们下次直接获取,获取时,先从缓存中获取节点,如果没获取到在下载

当然逻辑比较简单,可以自行完善的更好更通用,感觉使用场景没那么多,就做个简单案例即可

let instance: HomeBkgImageManager
export default class HomeBkgImageManager  {
    images: any[] = []
    currentIndex: number = -1

    //创建一个单例方便调用
    static get shared() {
        if (!instance) {
            instance = new HomeBkgImageManager()
        }
        return instance
    }

    //展示指定位置图片
    showImageWithIndex(index: number) {
        //获取我们要插入图片的参考位置节点
        let element = document.getElementById('home_bkg')
        if (!element) return
        //同一个节点忽略
        if (index === this.currentIndex) return
        //移除老的节点
        let lastImage = this.images[this.currentIndex]
        if (lastImage && element.contains(lastImage)) {
            element.removeChild(lastImage)
        }
        //更新新的节点
        let image = this.images[index]
        if (image) {
            //插入到当前节点里面,且排到最前面,before系列的是相对参考节点,并列前后的
            element.insertAdjacentElement('afterbegin', image)
        }else {
            //动态创建图片节点,当然其他也可以
            const img = document.createElement('img');
            img.style.width = '100%'
            img.style.height = '100%'
            img.style.position = 'absolute'
            img.style.zIndex = '0px'
            img.src = `${process.env.PUBLIC_URL}/light_${index}.jpg`;
            img.onload = () => {
                this.images[index] = img
                //插入到当前节点里面,且排到最前面,before系列的是相对参考节点,并列前后的
                element?.insertAdjacentElement('afterbegin', img)
            };
        }
        this.currentIndex = index
    }

    //加载所有图片,排除默认显示的,避免重复下载
    loadImageWithoutIndex(index: number) {
        for (let i = 0; i <= 10; i++) {
            if (i === index) continue
            const img = document.createElement('img');
            img.style.width = '100%'
            img.style.height = '100%'
            img.style.position = 'absolute'
            img.style.zIndex = '0px'
            img.src = `${process.env.PUBLIC_URL}/light_${i}.jpg`;
            img.onload = () => this.images[i] = img;
        }
    }
    
    //预加载所有图片
    loadImages() {
        for (let i = 0; i <= 10; i++) {
            const img = document.createElement('img');
            img.style.width = '100%'
            img.style.height = '100%'
            img.style.position = 'absolute'
            img.style.zIndex = '0px'
            img.src = `${process.env.PUBLIC_URL}/light_${i}.jpg`;
            img.onload = () => this.images[i] = img;
        }
    }
}