りゅうじの学習blog

学習したことをアウトプットしていきます。

7/20 ユーザのアイコンの色をランダムに割り当てる

要件

  • アイコンの文字は名前の1文字目を表示する
  • 色は5色からランダムで設定する
  • 色はユーザーに固有のもので、再読み込みなどで変更されない。

やり方

  • 登録日時を文字コードにし、ランダムのhsl( 色相(Hue)、彩度(Saturation)、輝度(Lightness))で色を割り当てる、濃淡は調整可能なのでデザイナーと擦り合わせる(今回は hを登録日時の文字コード sが30 lが60)

参考

Compute an arbitrary color for user avatar starting from their username (with Javascript).

// Import
import React, { VFC, useMemo } from 'react';
import styled from 'styled-components';
import { managementColors } from '../../../../themes/colors';

// Types
type ContainerProps = { item: MemberListItem };
type Props = { item: MemberListItem; icon: string; iconColor: string };
export interface MemberListItem {
  id: string;
  icon?: string;
  name: string;
  email: string;
  createdAt: string;
}
interface IconProps {
  color: string;
}

// Style

// 省略

const Icon = styled('div')<IconProps>`
  width: 32px;
  height: 32px;
  border-radius: 50%;
  margin-right: 12px;
  background-color: ${(props) => props.color};
  color: white;
  display: flex;
  justify-content: center;
  align-items: center;
  font-size: 14px;
  font-weight: 700;
`;

// DOM
const Component: VFC<Props> = ({ item, icon, iconColor }) => {
  return (
       <>
        <Icon color={iconColor}>{icon}</Icon>
        <div>
          {item.name}
          <br />
          <Email>{item.email}</Email>
        </div>
       </>
  );
};

// Container
const Container: VFC<ContainerProps> = ({ item }) => {
  const dateToHslColor = (dateStr: string, s: number, l: number): string => {
    let hash = 0;
    Array.from(dateStr).forEach((char) => {
      // eslint-disable-next-line no-bitwise
      hash = char.charCodeAt(0) + ((hash << 5) - hash);
    });

    const h = hash % 360;
    return `hsl(${h}, ${s}%, ${l}%)`;
  };
  const getInitial = (name: string) => {
    return name[0];
  };
  const getRandomColor = useMemo(() => {
    return dateToHslColor(item.createdAt, 30, 60);
  }, [item.createdAt]);
  const icon = getInitial(item.name);
  const iconColor = getRandomColor;

  return <Component {...{ item, icon, iconColor }} />;
};

export { Container as MemberListCell };
  1. dateToHslColor(dateStr: string, s: number, l: number): string

    この関数は、渡された日付文字列をベースに一意の色を生成します。その色はHSL(Hue, Saturation, Lightness)形式で表されます。

    具体的には、日付文字列からハッシュ値を計算し、そのハッシュ値を360で割った余りを色相(Hue)として使用しています。なぜ360で割るのかというと、色相は通常0から360の範囲で表されるからです。関数の2つ目と3つ目の引数は彩度(Saturation)と輝度(Lightness)を指定するために使われます。

  2. getInitial(name: string)

    この関数は、名前の最初の文字(イニシャル)を返します。ここでは、アイコン上に表示する文字として使用されます。

  3. getRandomColor

    この関数は、useMemoフックを使って生成された色の値をメモ化(キャッシュ)します。ここでのキーはitem.createdAtで、この値が変わると新しい色が生成されます。

これらの関数を組み合わせることで、各ユーザーが作成された日時に基づいて異なる色のアイコンを持つようになります。この方法はユーザーが視覚的に区別しやすくなり、特定のユーザーを一目で認識できるようになります。

コメント

元々、私が考えた実装では、ユーザ名を文字コードにして合計値を指定された色の数で割り、その余りから色を選択するロジックにしていました。

ですが、実装後に、これでは同姓同名では一緒の色になってしまうのと、当初の色は5色だったんですが、5色では被る人が多く出てきてしまうので、登録日時にし、色も5色ではなく、hslのslのみを固定して、hをcreateAtの文字コードによって変化するようにすることで、対応しました。