【问题标题】:Add placeholder="blur" to multiple remote images in nextjs在 nextjs 中将 placeholder="blur" 添加到多个远程图像
【发布时间】:2021-09-27 09:54:59
【问题描述】:

我有一个 nextjs 应用程序,它通过 api 显示电影及其海报,我试图在所有图像完全加载显示之前为它们添加模糊效果。我在 github 讨论中找到了这个 comment,它使用了 plaiceholder,但我找不到让它适用于 multiple 图像的方法。这是我的一些代码

// index.js

...
 return (
    <>
      <MovieList data={props.data.trending} title="Trending Right Now" />
      <MovieList data={props.data.popular} title="Popular" />
    </>
  );

export async function getStaticProps() {
  var promises = [];
  const api_key = '...'; // don't worry about api key I can make another one
  const urls = [
    `https://api.themoviedb.org/3/trending/movie/week?api_key=${api_key}`,
    `https://api.themoviedb.org/3/movie/popular?api_key=${api_key}&language=en-US&page=1`
  ];

  urls.forEach(function(url) {
    promises.push(
      axios.get(url).then(
        function(data) {
          return { success: true, data: data };
        },
        function() {
          return { success: false };
        }
      )
    );
  });
  const [trendingRes, popularRes] = await Promise.all(promises);

  const trending = trendingRes.success === false ? null : trendingRes.data.data;
  const popular = popularRes.success === false ? null : popularRes.data.data;

  return { props: { data: { trending, popular } } };
}
// MovieList.js

   {props.data
        ? props.data.results.map((movie, index) => {
            return (
              <>
                ...
                <div>
                  <Image
                    src={`https://image.tmdb.org/t/p/w500${movie.poster_path}`}
                    alt="movie poster"
                    width="260"
                    height="391"
                    placeholder="blur" // doesn't work
                  />
                </div>
              </>
            );
          })
     : 'Not found'}

你可以看到完整代码here

【问题讨论】:

标签: next.js nextjs-image


【解决方案1】:

我终于设法通过使用 plaiceholder 并在 api 调用期间将每个 api 中的图像转换为 blurhash 代码并在我的 movieList 组件中使用该代码和 react-blurhash 来工作

// index.js 

  const [trendingRes, popularRes] = await Promise.all(
    urls.map((url) =>
      axios.get(url).then(
        (data) =>
          Promise.all(
            data.data.results.map((one) => {
              return getPlaiceholder(
                `https://image.tmdb.org/t/p/w500${one.poster_path}`
              )
                .then(({ blurhash, img }) => {
                  return { img, ...one, blurhash };
                })
                .catch(() => ({ ...one, img: "error", blurhash: null }));
            })
          ).then((values) => ({ success: true, data: values })),

        () => ({ success: false })
      )
    )
  ).then((data) =>  data);
// movieList.js 

      {props.data
        ? props.data.map((movie, index) => {
            return (
              <>
                {movie.blurhash ? (
                  <div className="overflow-hidden relative block w-[260px] h-[391px]">
                    <BlurhashCanvas
                      punch={1}
                      hash={movie.blurhash.hash}
                      width={movie.blurhash.height}
                      height={movie.blurhash.width}
                      className="absolute left-0 top-0 w-full h-full inset-0"
                    />

                    <Image src={movie.img.src} width="260" height="391" />
                  </div>
    
               // show image without blur effect if there is any blurhash error 
                ) : ( 
                  <Image
                    src={`https://image.tmdb.org/t/p/w500${movie.poster_path}`}
                    width="260"
                    height="391"
                    className="aspect-w-260 aspect-h-391"
                  />
                )}
              </>
            );
          })
        : "Not found"}

【讨论】:

    【解决方案2】:

    如果您使用 CDN 提供图像,Next.js 还支持模糊动态图像,允许您提供自定义的 blurDataURL,由后端提供。

    <Image
        src={`https://image.tmdb.org/t/p/w500${movie.poster_path}`}
        alt="movie poster"
        width="260"
        height="391"
        placeholder="blur"
        blurDataURL="data:image/jpeg;base64,/9j/2wBDAAYEBQYFBAYGBQYHBwYIChAKCgkJChQODwwQFxQYGBcUFhYaHSUfGhsjHBYWICwgIyYnKSopGR8tMC0oMCUoKSj/2wBDAQcHBwoIChMKChMoGhYaKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCgoKCj/wAARCAAIAAoDASIAAhEBAxEB/8QAFQABAQAAAAAAAAAAAAAAAAAAAAb/xAAhEAACAQMDBQAAAAAAAAAAAAABAgMABAUGIWEREiMxUf/EABUBAQEAAAAAAAAAAAAAAAAAAAMF/8QAGhEAAgIDAAAAAAAAAAAAAAAAAAECEgMRkf/aAAwDAQACEQMRAD8AltJagyeH0AthI5xdrLcNM91BF5pX2HaH9bcfaSXWGaRm knyJckliyjqTzSlT54b6bk+h0R//2Q=="
    />
    

    【讨论】:

    • 是的,如果后端可以提供blurDataURL,这将是完美的,但就我而言,我正在客户端处理它
    【解决方案3】:

    在使用动态图像进行模糊处理时,您可以提供blurDataURL 属性。

    在解析从getStaticProps 中的API 接收到的数据时,使用plaiceholder 将图像转换为base64。

    // index.js - getStaticProps
    
    const [trendingRes, popularRes] = await Promise.all(
        urls.map((url) =>
            axios.get(url).then(
                (data) =>
                    Promise.all(
                        data.data.results.map((one) => {
                            return getPlaiceholder(`https://image.tmdb.org/t/p/w500${one.poster_path}`)
                                .then(({ base64, img }) => {
                                    return { ...one, img, base64 };
                                })
                                .catch(() => ({ ...one, img: null, base64: null }));
                        })
                    )
                    .then(
                        (values) => ({ success: true, data: values })),
                        () => ({ success: false })
                    )
            )
        ).then((data) =>  data);
    )
    

    然后在您的MovieList 组件中,将blurDataURLplaceholder="blur" 结合使用。

    // MovieList.js
    
    {props.data && 
        props.data.map((movie, index) => {
            return (
                <>
                    <div>
                        <Image
                            src={movie.img}
                            alt="movie poster"
                            width="260"
                            height="391"
                            placeholder="blur"
                            blurDataURL={movie.base64}
                        />
                    </div>
                </>
            );
        })
    }
    

    【讨论】:

    • 我将您的代码复制到我的文件中,但我没有看到模糊效果,而只是在我的所有图像上看到了空占位符。我已经在Image 上设置了placeholder="blur"blurDataURL。当我检查网络选项卡时,base64 图像不会首先作为占位符加载。你知道什么可能导致这种情况吗?我无法在在线 IDE 上重现错误,因为我遇到了 npm 包问题。
    • 我更新了 getStaticProps 代码以反映您在自己的答案中所做的,但使用 base64
    • 抱歉,使用更新的base64 代码,我仍然看不到图像的模糊效果。发现问题后,我会更新自己的答案。
    猜你喜欢
    • 2022-12-30
    • 1970-01-01
    • 1970-01-01
    • 2019-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多