Value에 따라 Gradient Color 만들기

KeyEun Lee·2023년 12월 8일
0

20231208 TIL

요구사항

  • Rank 값의 크기에 따라, 요구된 Color Hex 값의 Range '#FF5A00'(Min) ~ '#FFD9C6'(Max)에서 Color 값을 자동으로 생성하라

Solution

1) Rank 값의 Min, Max 구하기

let numericRanksPower = sortedData
    .map(item => item['COLUMN_NAME'])
    .filter(rank => !isNaN(rank) && rank !== null && rank !== '');

const maxRankPower = Math.max(...numericRanksPower);
const minRankPower = Math.min(...numericRanksPower);
  • Math.maxMath.min은 입력된 숫자 중 가장 큰 숫자와 작은 숫자를 반환한다. 포인트는 만약 인수 중 하나라도 숫자로 변환하지 못한다면 NaN로 반환한다는 것!
  • 따라서, .filter(rank => !isNaN(rank) && rank !== null && rank !== '') 요 코드로 숫자로 변환하지 못할 case를 방지한다.

2) 대장 함수: rank에 따른 color code 반환

function getRankColorPower(rank) {
    if (isNaN(rank) || rank === null || rank === '') {
        return 'transparent'; // Or any default color for non-numeric values
    }
    // Interpolate color based on rank
    const proportion = (rank - minRankPower) / (maxRankPower - minRankPower);
    return interpolateColor('#FF5A00', '#FFD9C6', proportion);
}
  • 가장 대장함수는 어떤 방식이어야할까? rank를 인수로 던져주면, 그거에 맞는 color code를 반환하는 방식이어야겠지.
    - 단, rank가 NaN이거나 null일 때는 transparent 처리가 필요해.
  • 우선은 그 color code를 반환할 함수를 interpolateColor라고 해보자.
    - 시작값(Hex Code), 끝값(Hex code), 비율을 인수로 받는다.

3) 아기 함수: Color을 interpolate하기 위해서는 Hex가 아니라 RGB로 접근해야해

  • Hex 상황에서는 Color을 주어진 범위 내에서 interpolation 할 수 없다. 즉, RGB 형태로 변환한 후, R값, G값, B값을 각각 interpolate 해주어야한다!
  • 참고) Hexadecimal Color (16진법 색 형식 #RRGGBB)

A. function hexToRgb(hex)

function hexToRgb(hex) {
    let result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex);
    return result ? {
        r: parseInt(result[1], 16),
        g: parseInt(result[2], 16),
        b: parseInt(result[3], 16)
    } : null;
}
  • 정규표현식/^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i:

    • /^#?: 문자열의 시작 부분에 선택적 #이 있는지 확인함. 뒤에 오는 ? 은 #이 선택 사항임을 의미.
    • ([a-f\d]{2})([a-f\d]{2})([a-f\d]{2}): 숫자(\d) 또는 a부터 f까지의 문자가 정확히 2개씩 3번 나오는지 캡쳐하는 부분.
    • $: 문자열의 종료.
    • /i: 이 플래그를 사용하면 정규식이 대소문자를 구분하지 않으므로 문자 a-f가 대문자 또는 소문자가 될 수 있음.
  • exec(): 정규식 실행! 문자열이 패턴과 일치하면 코드는 일치하는 전체 문자열과 일치하는 그룹(빨강, 녹색, 파랑 구성 요소)이 포함된 배열을 반환. 문자열이 일치하지 않으면 코드는 null을 반환.

  • 예시

let result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec('#FF5A00');
console.log(result)

  • parseInt(result[i], 16): 16진수를 10진수로 반환. 각 구성 요소(r, g, b)는 색상의 빨강, 녹색, 파랑의 강도를 나타내며 0에서 255 사이의 값을 가짐.

B. function interpolate(start, end, proportion)

  • hexToRGB로 각각 r,g,b값을 뽑아냈으니, 이제 각각 r,g,b 범위에서 값을 반환할 수 있도록 'interpolate'하는 함수가 필요하다.
function interpolate(start, end, proportion) {
  return Math.round(start + (end - start) * proportion);
}
  • 예시와 함께 생각해보자.
    - start = 0, end = 10, proportion = 0.25라고 가정해 보자.
    • 이 함수는 다음과 같이 작동한다:
      • 범위 계산: 10 - 0 = 10.
        • 비율 적용: 10 * 0.25 = 2.5.
        • 처음부터 오프셋: 0 + 2.5 = 2.5.
        • 결과 반올림: Math.round(2.5) = 3.
        • 따라서 interpolate(0, 10, 0.25)는 3을 반환!

C. function interpolateColor(color1, color2, proportion)

  • 이제 준비는 끝! 앞선 함수를 조합해서 interpolateColor을 진행하자.
  • 인수로 받는 시작 Hex, 끝 Hex, proportion에 따라, rgb 값을 뱉어주는 것이 목표.
	function interpolateColor(color1, color2, proportion) {
      let color1RGB = hexToRgb(color1);
      let color2RGB = hexToRgb(color2);

      let resultRGB = {
        r: interpolate(color1RGB.r, color2RGB.r, proportion),
        g: interpolate(color1RGB.g, color2RGB.g, proportion),
        b: interpolate(color1RGB.b, color2RGB.b, proportion),
      };

      return `rgb(${resultRGB.r}, ${resultRGB.g}, ${resultRGB.b})`;
	}

Takeaway

  • Color interpolate 할 때는 Hex가 아니라 RGB로 생각하기
  • 일단, 최종값을 내려주는 대장함수부터 생각해보고, 이 대장함수가 작동하기 위한 아기함수들이 뭐가 필요할지 step by step으로 고민해보자.
profile
느리지만 꾸준히 공부합니다 https://keyeun.com/

0개의 댓글