Material-ui: [Avatar] Random color from string

Created on 29 Aug 2018  路  12Comments  路  Source: mui-org/material-ui

I want to provide a random color to an Avatar component

  • [ x ] This is a v1.x issue.
  • [ x ] I have searched the issues of this repository and believe that this is not a duplicate.

I would like to be able to pass a string to the avatar component and receive a color from my pallete that is random

Here is a similar implementation
https://github.com/Sitebase/react-avatar/blob/master/src/utils.js#L74
Ideally in material-ui the component will be a function so that it can be called from else where if the need arises

Currently material ui has no such functionality

Im trying to have random but unique colors for my users one letter avatars.

Avatar enhancement good to take

Most helpful comment

@japrogramer It's a bit outside of our scope but I get the use case. I had it twice. I have used the following function:

function stringToColor(string) {
  let hash = 0;
  let i;

  /* eslint-disable no-bitwise */
  for (i = 0; i < string.length; i += 1) {
    hash = string.charCodeAt(i) + ((hash << 5) - hash);
  }

  let color = '#';

  for (i = 0; i < 3; i += 1) {
    const value = (hash >> (i * 8)) & 0xff;
    color += `00${value.toString(16)}`.substr(-2);
  }
  /* eslint-enable no-bitwise */

  return color;
}

stringToColor('Material-UI') // "#da90b2"
stringToColor('React') // "#5fe9b2"

https://github.com/oliviertassinari/SplitMe/blob/c25fe62187a856386fd0c43c51859d6f973d651e/src/main/member/Avatar.js#L8-L26.

Then, you can use this helper theme.palette.getContrastText(MY_COLOR) to get a good contrast.

All 12 comments

@japrogramer It's a bit outside of our scope but I get the use case. I had it twice. I have used the following function:

function stringToColor(string) {
  let hash = 0;
  let i;

  /* eslint-disable no-bitwise */
  for (i = 0; i < string.length; i += 1) {
    hash = string.charCodeAt(i) + ((hash << 5) - hash);
  }

  let color = '#';

  for (i = 0; i < 3; i += 1) {
    const value = (hash >> (i * 8)) & 0xff;
    color += `00${value.toString(16)}`.substr(-2);
  }
  /* eslint-enable no-bitwise */

  return color;
}

stringToColor('Material-UI') // "#da90b2"
stringToColor('React') // "#5fe9b2"

https://github.com/oliviertassinari/SplitMe/blob/c25fe62187a856386fd0c43c51859d6f973d651e/src/main/member/Avatar.js#L8-L26.

Then, you can use this helper theme.palette.getContrastText(MY_COLOR) to get a good contrast.

I'm not against having a module like this in the codebase, but it might be out of scope. I'm unsure. @mbrookes What do you think?

I have added the waiting for users upvotes tag. I'm closing the issue as we are not sure people are looking for such abstraction. So please upvote this issue if you are. We will prioritize our effort based on the number of upvotes.

Sorry, I missed the original request for comment. I agree that this is well out of scope. Allowing user to _provide_ arbitrary colors sure, generating them, not so much.

Sure, the color should be easier to pass right now I'm doing style={{ ... }}

right now I'm doing style={{ ... }}

It's perfect :+1:, you could be using CSS variables too or another dynamic option we support.

If you don't want the background of the avatar to be too light for white text, you can add a check to compare the color returned to a light grey, and if it's too light, return the grey.

if (parseInt(colour, 16) > 15658734) return '#eeeeee''

Full function:

function stringToColor(string) {
  let hash = 0;
  let i;

  /* eslint-disable no-bitwise */
  for (i = 0; i < string.length; i += 1) {
    hash = string.charCodeAt(i) + ((hash << 5) - hash);
  }

  let colour = '';

  for (i = 0; i < 3; i += 1) {
    const value = (hash >> (i * 8)) & 0xff;
    colour += `00${value.toString(16)}`.substr(-2);
  }
  /* eslint-enable no-bitwise */

  if (parseInt(colour, 16) > 15658734) return '#eeeeee''
  return `#${colour}`;
}

@peterzernia Consider setting the text color with theme.palette.getContrastText().

I would propose the introduction of a demo with this logic.

I have an alternate implementation:

import seedrandom from 'seedrandom'

const ctx = document.createElement('canvas').getContext('2d')

const colorToHex = color => {
    ctx.fillStyle = color
    const hexColor = ctx.fillStyle as string

    return hexColor
}

const PHI = 1.618033988749895

// https://martin.ankerl.com/2009/12/09/how-to-create-random-colors-programmatically/
const generateColorFromString = str =>
    colorToHex(
        `hsl(${Math.floor(((seedrandom(str)() + 1 / PHI) % 1) * 360)}, 50%, 50%)`,
    )

This generates colors deterministically from a string.

You can set the brightness and saturation of all generated colors (no longer a need to worry about the legibility of the any text).

It also limits the possible colors to a subset, so that you don't end up with 2 colors that are not different enough to be distinctive.

Read the post in my comment for more information.

@zeorin Thanks for sharing! Note that for the contrast issue, here is an helper: theme.palette.getContrastText() :).

Maybe we should bring an helper in the library itself. What would be the next step?

It could use hslToRgb() from colorManipulator rather than using canvas, which would also mean that it would work for SSR.

Was this page helpful?
0 / 5 - 0 ratings