宽高动态时如何使用react-blurhash生成图片占位符?
解决react-blurhash在百分比宽高容器下的占位符问题
问题场景
使用react-blurhash组件实现图片占位符时,组件要求width和height必须传入整数,但图片容器采用百分比布局(依赖父元素尺寸),无法确定具体整数值,传入字符串"100%"不生效。
原示例代码:
<div className='sm:w-[50%] w-full flex items-center justify-center'> <div className="relative w-full h-full"> {isLoading && <Blurhash hash="L5IW7%8x3@1G2,Dz.{{}BN0g}u1I" width={"100%"} height={"100%"} resolutionX={32} resolutionY={32} punch={1} className='absolute top-0 left-0 w-full h-full transition-opacity duration-500 rounded-lg' />} <img src={Rica} alt="Rica" className='rounded-lg lg:h-[500px] relative z-10 transition-opacity duration-500' onLoad={() => { setIsLoading(false) }} /> </div> </div>
可行解决方案
方案1:动态获取父容器实际尺寸
通过useRef获取父容器DOM节点,结合useEffect监听容器尺寸变化,实时计算并传入整数宽高:
import { useRef, useState, useEffect } from 'react'; import Blurhash from 'react-blurhash'; // ... const parentRef = useRef(null); const [placeholderSize, setPlaceholderSize] = useState({ width: 300, height: 200 }); // 初始默认值 useEffect(() => { const updateSize = () => { if (parentRef.current) { const { width, height } = parentRef.current.getBoundingClientRect(); setPlaceholderSize({ width: Math.floor(width), height: Math.floor(height) }); } }; // 初始化执行一次 updateSize(); // 监听窗口 resize 事件 window.addEventListener('resize', updateSize); return () => window.removeEventListener('resize', updateSize); }, []); // ... <div className='sm:w-[50%] w-full flex items-center justify-center'> <div ref={parentRef} className="relative w-full h-full"> {isLoading && <Blurhash hash="L5IW7%8x3@1G2,Dz.{{}BN0g}u1I" width={placeholderSize.width} height={placeholderSize.height} resolutionX={32} resolutionY={32} punch={1} className='absolute top-0 left-0 w-full h-full transition-opacity duration-500 rounded-lg' />} <img src={Rica} alt="Rica" className='rounded-lg lg:h-[500px] relative z-10 transition-opacity duration-500' onLoad={() => { setIsLoading(false) }} /> </div> </div>
方案2:固定基础尺寸+CSS缩放
给Blurhash传入一个足够大的固定整数尺寸(比如和图片最大可能尺寸一致),再通过CSS让组件占满父容器。由于blurhash生成的是矢量风格的占位图,缩放不会失真:
<div className='sm:w-[50%] w-full flex items-center justify-center'> <div className="relative w-full h-full"> {isLoading && <Blurhash hash="L5IW7%8x3@1G2,Dz.{{}BN0g}u1I" width={1200} // 设为容器最大可能宽度的整数 height={800} // 对应高度整数 resolutionX={32} resolutionY={32} punch={1} className='absolute top-0 left-0 w-full h-full transition-opacity duration-500 rounded-lg' />} <img src={Rica} alt="Rica" className='rounded-lg lg:h-[500px] relative z-10 transition-opacity duration-500' onLoad={() => { setIsLoading(false) }} /> </div> </div>
方案3:使用Canvas手动渲染(进阶)
如果上述方案不满足需求,可以直接使用blurhash核心库手动渲染到Canvas,通过CSS控制Canvas尺寸:
import { decode } from 'blurhash'; import { useRef, useEffect } from 'react'; // ... const canvasRef = useRef(null); useEffect(() => { if (isLoading && canvasRef.current) { const canvas = canvasRef.current; const ctx = canvas.getContext('2d'); const pixels = decode("L5IW7%8x3@1G2,Dz.{{}BN0g}u1I", 32, 32); const imageData = new ImageData(pixels, 32, 32); ctx.putImageData(imageData, 0, 0); } }, [isLoading]); // ... <div className='sm:w-[50%] w-full flex items-center justify-center'> <div className="relative w-full h-full"> {isLoading && <canvas ref={canvasRef} className='absolute top-0 left-0 w-full h-full transition-opacity duration-500 rounded-lg' />} <img src={Rica} alt="Rica" className='rounded-lg lg:h-[500px] relative z-10 transition-opacity duration-500' onLoad={() => { setIsLoading(false) }} /> </div> </div>
内容的提问来源于stack exchange,提问作者Vedant Shah
相关产品推荐
相关产品推荐

