Added JazzIcon
							parent
							
								
									500a88dbab
								
							
						
					
					
						commit
						a9189979e1
					
				@ -0,0 +1,165 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import Color from 'color';
 | 
			
		||||
import { Paper } from './Paper';
 | 
			
		||||
import { RNG } from './RNG';
 | 
			
		||||
 | 
			
		||||
const defaultColors = [
 | 
			
		||||
  '#01888c', // teal
 | 
			
		||||
  '#fc7500', // bright orange
 | 
			
		||||
  '#034f5d', // dark teal
 | 
			
		||||
  '#E784BA', // light pink
 | 
			
		||||
  '#81C8B6', // bright green
 | 
			
		||||
  '#c7144c', // raspberry
 | 
			
		||||
  '#f3c100', // goldenrod
 | 
			
		||||
  '#1598f2', // lightning blue
 | 
			
		||||
  '#2465e1', // sail blue
 | 
			
		||||
  '#f19e02', // gold
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const isColor = (str: string) => /(^#[0-9A-F]{6}$)|(^#[0-9A-F]{3}$)/i.test(str);
 | 
			
		||||
const isColors = (arr: Array<string>) => {
 | 
			
		||||
  if (!Array.isArray(arr)) {
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (arr.every(value => typeof value === 'string' && isColor(value))) {
 | 
			
		||||
    return true;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return false;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
interface Props {
 | 
			
		||||
  diameter: number;
 | 
			
		||||
  seed: number;
 | 
			
		||||
  paperStyles?: Object;
 | 
			
		||||
  svgStyles?: Object;
 | 
			
		||||
  shapeCount?: number;
 | 
			
		||||
  wobble?: number;
 | 
			
		||||
  colors?: Array<string>;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// tslint:disable-next-line no-http-string
 | 
			
		||||
const svgns = 'http://www.w3.org/2000/svg';
 | 
			
		||||
const shapeCount = 4;
 | 
			
		||||
const wobble = 30;
 | 
			
		||||
 | 
			
		||||
export class JazzIcon extends React.PureComponent<Props> {
 | 
			
		||||
  public render() {
 | 
			
		||||
    const {
 | 
			
		||||
      colors: customColors,
 | 
			
		||||
      diameter,
 | 
			
		||||
      paperStyles,
 | 
			
		||||
      seed,
 | 
			
		||||
      svgStyles,
 | 
			
		||||
    } = this.props;
 | 
			
		||||
 | 
			
		||||
    const generator = new RNG(seed);
 | 
			
		||||
 | 
			
		||||
    const colors = customColors || defaultColors;
 | 
			
		||||
 | 
			
		||||
    const newColours = this.hueShift(
 | 
			
		||||
      this.colorsForIcon(colors).slice(),
 | 
			
		||||
      generator
 | 
			
		||||
    );
 | 
			
		||||
    const shapesArr = Array(shapeCount).fill(null);
 | 
			
		||||
    const shuffledColours = this.shuffleArray(newColours, generator);
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <Paper color={shuffledColours[0]} diameter={diameter} style={paperStyles}>
 | 
			
		||||
        <svg
 | 
			
		||||
          xmlns={svgns}
 | 
			
		||||
          x="0"
 | 
			
		||||
          y="0"
 | 
			
		||||
          height={diameter}
 | 
			
		||||
          width={diameter}
 | 
			
		||||
          style={svgStyles}
 | 
			
		||||
        >
 | 
			
		||||
          {shapesArr.map((_, i) =>
 | 
			
		||||
            this.genShape(
 | 
			
		||||
              shuffledColours[i + 1],
 | 
			
		||||
              diameter,
 | 
			
		||||
              i,
 | 
			
		||||
              shapeCount - 1,
 | 
			
		||||
              generator
 | 
			
		||||
            )
 | 
			
		||||
          )}
 | 
			
		||||
        </svg>
 | 
			
		||||
      </Paper>
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private hueShift(colors: Array<string>, generator: RNG) {
 | 
			
		||||
    const amount = generator.random() * 30 - wobble / 2;
 | 
			
		||||
 | 
			
		||||
    return colors.map(hex => {
 | 
			
		||||
      const color = Color(hex);
 | 
			
		||||
      color.rotate(amount);
 | 
			
		||||
 | 
			
		||||
      return color.hex();
 | 
			
		||||
    });
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private genShape(
 | 
			
		||||
    colour: string,
 | 
			
		||||
    diameter: number,
 | 
			
		||||
    i: number,
 | 
			
		||||
    total: number,
 | 
			
		||||
    generator: RNG
 | 
			
		||||
  ) {
 | 
			
		||||
    const center = diameter / 2;
 | 
			
		||||
    const firstRot = generator.random();
 | 
			
		||||
    const angle = Math.PI * 2 * firstRot;
 | 
			
		||||
    const velocity =
 | 
			
		||||
      diameter / total * generator.random() + i * diameter / total;
 | 
			
		||||
    const tx = Math.cos(angle) * velocity;
 | 
			
		||||
    const ty = Math.sin(angle) * velocity;
 | 
			
		||||
    const translate = `translate(${tx} ${ty})`;
 | 
			
		||||
 | 
			
		||||
    // Third random is a shape rotation on top of all of that.
 | 
			
		||||
    const secondRot = generator.random();
 | 
			
		||||
    const rot = firstRot * 360 + secondRot * 180;
 | 
			
		||||
    const rotate = `rotate(${rot.toFixed(1)} ${center} ${center})`;
 | 
			
		||||
    const transform = `${translate} ${rotate}`;
 | 
			
		||||
 | 
			
		||||
    return (
 | 
			
		||||
      <rect
 | 
			
		||||
        key={i}
 | 
			
		||||
        x="0"
 | 
			
		||||
        y="0"
 | 
			
		||||
        rx="0"
 | 
			
		||||
        ry="0"
 | 
			
		||||
        height={diameter}
 | 
			
		||||
        width={diameter}
 | 
			
		||||
        transform={transform}
 | 
			
		||||
        fill={colour}
 | 
			
		||||
      />
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private colorsForIcon(arr: Array<string>) {
 | 
			
		||||
    if (isColors(arr)) {
 | 
			
		||||
      return arr;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return defaultColors;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  private shuffleArray<T>(array: Array<T>, generator: RNG) {
 | 
			
		||||
    let currentIndex = array.length;
 | 
			
		||||
    const newArray = [...array];
 | 
			
		||||
 | 
			
		||||
    // While there remain elements to shuffle...
 | 
			
		||||
    while (currentIndex > 0) {
 | 
			
		||||
      // Pick a remaining element...
 | 
			
		||||
      const randomIndex = generator.next() % currentIndex;
 | 
			
		||||
      currentIndex -= 1;
 | 
			
		||||
      // And swap it with the current element.
 | 
			
		||||
      const temporaryValue = newArray[currentIndex];
 | 
			
		||||
      newArray[currentIndex] = newArray[randomIndex];
 | 
			
		||||
      newArray[randomIndex] = temporaryValue;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return newArray;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,25 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
 | 
			
		||||
const styles = {
 | 
			
		||||
  borderRadius: '50px',
 | 
			
		||||
  display: 'inline-block',
 | 
			
		||||
  margin: 0,
 | 
			
		||||
  overflow: 'hidden',
 | 
			
		||||
  padding: 0,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// @ts-ignore
 | 
			
		||||
export const Paper = ({ children, color, diameter, style: styleOverrides }) => (
 | 
			
		||||
  <div
 | 
			
		||||
    className="paper"
 | 
			
		||||
    style={{
 | 
			
		||||
      ...styles,
 | 
			
		||||
      backgroundColor: color,
 | 
			
		||||
      height: diameter,
 | 
			
		||||
      width: diameter,
 | 
			
		||||
      ...(styleOverrides || {}),
 | 
			
		||||
    }}
 | 
			
		||||
  >
 | 
			
		||||
    {children}
 | 
			
		||||
  </div>
 | 
			
		||||
);
 | 
			
		||||
@ -0,0 +1,21 @@
 | 
			
		||||
export class RNG {
 | 
			
		||||
  private _seed: number;
 | 
			
		||||
  constructor(seed: number) {
 | 
			
		||||
    this._seed = seed % 2147483647;
 | 
			
		||||
    if (this._seed <= 0) {
 | 
			
		||||
      this._seed += 2147483646;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public next() {
 | 
			
		||||
    return (this._seed = (this._seed * 16807) % 2147483647);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public nextFloat() {
 | 
			
		||||
    return (this.next() - 1) / 2147483646;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  public random() {
 | 
			
		||||
    return this.nextFloat();
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
@ -0,0 +1,2 @@
 | 
			
		||||
import { JazzIcon } from './JazzIcon';
 | 
			
		||||
export { JazzIcon };
 | 
			
		||||
					Loading…
					
					
				
		Reference in New Issue