슬라이드 배너를 만들 때, 각 요소 썸네일 이미지의 색을 추출해 배경색을 깔아달라는 디자인의 요청이 있었다. 그래서 발견한 것이 ColorThief 라는 라이브러리..!
ColorThief 는 Javascript만 사용하여 이미지에서 색상 팔레트를 가져오는 라이브러리라고 한다.
ColorThief 를 설치 후 import 해주자
npm i --save colorthief
import ColorThief from 'colorthief';
const imageRef = useRef<HTMLImageElement>(null);
const colorThief = useRef<ColorThief | null>(null);
const [colorList, setColorList] = useState<string[]>([]);
const [error, setError] = useState<string | null>(null);
useEffect(() => {
colorThief.current = new ColorThief();
}, []);
ColorThief
인스턴스를 생성한 후 ref
에 저장해주자
imageRef
역시 img 요소를 반복문으로 저장해주고 있다.
const { slides, options, isAutoPlay = true, onClick } = props;
// slides: 슬라이드 요소(ReactNode)가 담긴 배열
<S.EmblaContainer className='embla__container'>
{slides?.map((slide, index) => (
<S.EmblaSlide
className='embla__slide'
$isOnClick={!!onClick}
key={index}
onClick={() => onClick?.(slide)}
>
<S.EmblaSlideContent>
<S.EmblaSlideImage>
<S.EmblaSlideImageBadge>
{slide.isNew && (
<CommonBadge
label='NEW'
color='primary.blue'
bgColor='secondary.skyblue'
borderNone
/>
)}
</S.EmblaSlideImageBadge>
{error && <div>오류 발생: {error}</div>}
<img
ref={imageRef}
src={slide?.attach?.realPath}
alt={slide?.title}
onLoad={handleImageLoad(index)}
crossOrigin='anonymous'
style={{ display: 'block' }}
className={`embla-slide-image-${index}`}
/>
...
img 가 로드될 때를 기점으로 이미지를 참조하여 colorThief
내장함수인 getColor()
의 인수로 넣어준다.
이때, getColor()
함수가 내부적으로 img 의 데이터를 가져오려 하면서 아래와 같은 예외 상황이 발생할 수 있다.
img
가 아직 완전히 로드되지 않았을 때이런 상황을 대비해 try/catch 문을 사용해주자.
그렇게 하면 rgb 세가지 숫자를 반환해주는데(ex. 23, 31, 54), 이때 이 세가지 값들이 들어간 dominantColor 를 colorList 에 rgb 문자 형식으로 넣어주면 된다.!
const handleImageLoad = (index: number) => (event: React.SyntheticEvent<HTMLImageElement>) => {
const img = event.currentTarget;
if (colorThief.current) {
try {
const dominantColor = colorThief.current.getColor(img);
const rgbString = `rgb(${dominantColor[0]}, ${dominantColor[1]}, ${dominantColor[2]})`;
setColorList((prev) => {
const newList = [...prev];
newList[index] = rgbString;
return newList;
});
setError(null);
} catch (err) {
setError('색상 추출 중 오류가 발생했습니다.');
console.error(err);
}
}
};
하지만 새로고침을 했을 경우 색상이 추출되지 않는 상황이 발생했다.
이는 useEffect 로 따로 설정을 해주었다.
위에서 언급했듯이, 이미지가 완전히 로드되었을 때를 기점으로 불러주는것이 바람직하기 떄문에 img.complete 등의 조건부를 넣어주자.
// 새로고침 or 슬라이드가 변경되거나 컴포넌트가 마운트될 때 색상 추출 시도
useEffect(() => {
if (slides && colorThief.current && Array.isArray(slides)) {
// 약간의 지연 후 색상 추출 시도 (이미지가 로드되었을 가능성)
const timer = setTimeout(() => {
slides.forEach((_, index) => {
const img = document.querySelector(`.embla-slide-image-${index}`) as HTMLImageElement;
if (img && img.complete && img.naturalWidth > 0) {
try {
const dominantColor = colorThief.current!.getColor(img);
const rgbString = `rgb(${dominantColor[0]}, ${dominantColor[1]}, ${dominantColor[2]})`;
setColorList((prev) => {
const newList = [...prev];
newList[index] = rgbString;
return newList;
});
} catch (err) {
console.error(`색상 추출 실패 (인덱스 ${index}):`, err);
}
}
});
}, 100);
return () => clearTimeout(timer);
}
}, [slides]);
마지막 코드를 추가해주니 새로고침을 했을 때도 정상적으로 색상이 잘 추출되는 것을 확인할 수 있었다..! ☺️