看别人怎么做的? medium 如何实现渐进式图片加载!

197 阅读2分钟

这段代码实现了一个图片渐进式加载的效果,先加载小图然后再加载大图,提升用户体验。

演示代码codepen

  1. 基本结构:
<!-- 假设的HTML结构 -->
<div class="placeholder" data-large="large-image.jpg">
    <img class="img-small" src="small-image.jpg">
</div>
  1. 代码逻辑解析:
window.onload = function() {
    // 获取DOM元素
    var placeholder = document.querySelector('.placeholder'),  // 容器元素
        small = placeholder.querySelector('.img-small')        // 小图元素

    // 步骤1:加载小图
    var img = new Image();  // 创建新的Image对象
    img.src = small.src;    // 设置图片源
    img.onload = function () {
        small.classList.add('loaded');  // 小图加载完成后添加loaded类
    };

    // 步骤2:加载大图
    var imgLarge = new Image();
    imgLarge.src = placeholder.dataset.large;  // 从data-large属性获取大图URL
    imgLarge.onload = function () {
        imgLarge.classList.add('loaded');      // 大图加载完成后添加loaded类
    };
    placeholder.appendChild(imgLarge);         // 将大图添加到容器中
}
  1. 工作原理:
  • 首先加载体积小的缩略图(模糊或低质量)
  • 同时开始加载高质量大图
  • 小图加载完成后立即显示,给用户快速反馈
  • 大图加载完成后平滑替换小图
  1. 相关CSS:
.placeholder {
    position: relative;
    overflow: hidden;
}

.img-small {
    /* 初始状态可能是模糊的 */
    filter: blur(50px);
    transition: opacity .1s ease-in-out;
}

.img-small.loaded {
    opacity: 1;
}

.placeholder img:last-child {
    position: absolute;
    top: 0;
    left: 0;
    opacity: 0;
    transition: opacity .3s ease-in-out;
}

.placeholder img:last-child.loaded {
    opacity: 1;
}
  1. 优点:
  • 提升用户体验,不会出现空白等待
  • 渐进式加载,先显示模糊预览
  • 网络慢时效果更明显
  • 减少用户等待焦虑
  1. 使用场景:
  • 大图片展示
  • 图片列表
  • 产品详情页
  • 背景图片加载

这种技术被称为"渐进式图片加载"或"模糊加载",类似于Medium网站的图片加载效果。Facebook、Instagram等也使用类似技术来优化图片加载体验。

如何结合React进行简单实现?

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

const ProgressiveImage = ({ smallSrc, largeSrc, alt, className }) => {
  const [smallImageLoaded, setSmallImageLoaded] = useState(false);
  const [largeImageLoaded, setLargeImageLoaded] = useState(false);

  useEffect(() => {
    // Load small image
    const smallImage = new Image();
    smallImage.src = smallSrc;
    smallImage.onload = () => {
      setSmallImageLoaded(true);
    };

    // Load large image
    const largeImage = new Image();
    largeImage.src = largeSrc;
    largeImage.onload = () => {
      setLargeImageLoaded(true);
    };
  }, [smallSrc, largeSrc]);

  return (
    <div className="relative overflow-hidden">
      <img
        src={smallSrc}
        alt={alt}
        className={`
          w-full h-full
          ${smallImageLoaded ? 'opacity-100' : 'opacity-0'}
          ${largeImageLoaded ? 'scale-110 blur-2xl' : 'scale-100 blur-none'}
          transition-all duration-200
          ${className || ''}
        `}
      />
      {largeImageLoaded && (
        <img
          src={largeSrc}
          alt={alt}
          className={`
            absolute top-0 left-0
            w-full h-full
            ${largeImageLoaded ? 'opacity-100' : 'opacity-0'}
            transition-opacity duration-300
            ${className || ''}
          `}
        />
      )}
    </div>
  );
};

export default ProgressiveImage;