JavaScript로 이미지 파일 색상을 동적으로 변경시키기

Gomao·2023년 11월 6일
1

Web programming

목록 보기
14/16

1. 자바스크립트에서의 이미지 색상 변경

자바스크립트로 .jpg와 같은 확장자로 저장된 이미지 파일을 <img> 태그를 이용해서 웹 페이지에 띄울 수 있다.

이때, 만약 특정 상태값이 변하면 이미지의 색상을 바꾸거나 하고 싶다면 우리는 보통..

색상이 변경된 새 이미지를 저장하고, 이미지 링크를 바꾸는 방식으로 처리한다.

아래는 내가 개인적으로 작업하는 Repository에서 발췌한 코드이다.
(태그 이름이 특이한 것은 styled-components를 사용했기 때문이다)

	{!userStat.doping.noblessBoss 
      ? (<NonSelectedImg src="./images/noblessboss_no.png" alt="노블보공" />) 
      : (<SelectedImg src="./images/noblessboss.png" alt="노블보공" />)}

아무튼 2가지 이미지를 각각 저장해두고, 상태에 따라 다른 이미지를 렌더링하도록 처리하는 것이 일반적이다.

2. 이미지가 너무 많으면? 색상이 너무 다양하면?

그런데 만약 이미지 색상을 2개가 아니라 10개, 20개 이상 컨트롤해야 한다면?

20개의 이미지 파일을 모두 생성해서 저장하고 불러올 생각인가?

그런데 이미지 종류가 20개라면? 그러면 400개의 이미지 파일을 만들 생각인가?

말도 안된다.

=> 그럼 어떻게 하지?

우리는 이럴 때 사용할 수 있는 <svg>태그와 .svg파일을 활용할 수 있다.

3. Svg파일 설정

이미지를 svg 코드로 저장하면 다음과 같은 형식으로 저장된다.

// newImg.svg
<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<rect width="40" height="40" fill="url(#pattern0)"/>
<defs>
<pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1">
<use xlink:href="#image0_106_174" transform="scale(0.00163399)"/>
</pattern>
<image id="image0_106_174" width="612" height="612" xlink:href="data:image/png;base64,iVBORw0KGgoAAA..."/>
</defs>
</svg>

여기서 우리는 fill 옵션에 주목해야 한다.

svg파일에서 fill옵션은 해당 파츠의 색상을 의미하는데,

다음과 같이 설정해줄 수 있다.

fill="current"

자 이렇게 하면 색상을 동적으로 적용할 준비가 된 것이다.

4. Svg컴포넌트 설정

1) 파일 읽어오기

하지만 가져온 svg파일을 <img src="...">를 통해 읽게 되면 설정을 할 수 없다.

다음과 같이 컴포넌트로 파일을 읽어와야 한다.

import { ReactComponent } from "@assets/newImg.svg";

그리고 이제 컴포넌트에 parameter로 색상을 넘겨줄 수 있다.

<ReactComponent fill="#EEE" />

2) 컴포넌트가 1개가 아닌 경우

그런데 여기서 색상을 컨트롤해야 하는 여러 개의 이미지 컴포넌트가 있으면 어떻게 하지?

다음 코드는 ReactComponent 변수의 중복으로 에러가 뜰 것이다.

import { ReactComponent } from "@assets/newImg.svg";
import { ReactComponent } from "@assets/newImg2.svg";
import { ReactComponent } from "@assets/newImg3.svg";

import에 대해 조금 잘 이해하고 있다면 다음과 같이 선언할 수 있다.

import { ReactComponent as Img1 } from "@assets/newImg.svg";
import { ReactComponent as Img2 } from "@assets/newImg2.svg";
import { ReactComponent as Img3 } from "@assets/newImg3.svg";

이제 <Img1 ... /> <Img2 ... /> ... 태그를 각각 사용할 수 있다.

3) 컴포넌트가 아주 많은 경우

그런데 이번엔 이미지 컴포넌트가 40개이고, 모두 렌더링해야 하는 상황이다.
그럼 코드가 너무 더러워질 것이기 때문에, 배열의 map메소드를 사용하고 싶은 상황이다.

컴포넌트를 객체에 담을 수 있다!

const IMG_LIST = [
   {number: 1, component: <Img1 />},
   {number: 2, component: <Img2 />},
   {number: 3, component: <Img3 />},
   ...
]

Q. 그럼 이제 props를 어떻게 줄 건데?

React에는 cloleElement라는 기능이 있다. (https://react.dev/reference/react/cloneElement)

이것을 이용하여 다음과 같이 구성할 수 있다.

export default function SvgLoader({number, fill}) {
  
  // 리스트에서 해당하는 컴포넌트를 찾아온다.
  const matched = IMG_LIST.find((item) => item.number === number)?.component;
  
  // 해당하는 컴포넌트가 있으면 다음과 같은 방법으로 속성을 줄 수 있다.
  return <div>{matchedComponent && React.cloneElement(matchedComponent, { fill })}</div>;

이제 부모 컴포넌트에서 mapping function 내에 SvgLoader를 사용하여 렌더링할 수 있다.

5. 컴포넌트에서 활용

interface ImgProps{
  number: number;
  fill: string;
}

const LIST: ImgProps[] = [
  { number : 1, fill : "#FFF" },
  { number : 2, fill : "#EEE" },
  { number : 3, fill : "#DDD" }
]

function Component(){

...

return <>
  {LIST.map(item => {
  	return <div>{SvgLoader(item.number, item.fill)}</div>
  })}  
  </>
}

velog에서 직접 입력한 코드여서 오타가 있을지도..? 아마도 제대로 작동할 것임

이런 느낌으로 사용할 수 있다.

이번 포스팅은 여기까지.

profile
코딩꿈나무 고마오

0개의 댓글