You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			167 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			TypeScript
		
	
			
		
		
	
	
			167 lines
		
	
	
		
			3.9 KiB
		
	
	
	
		
			TypeScript
		
	
| // Modified from https://github.com/redlanta/react-jazzicon
 | |
| 
 | |
| 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 =>
 | |
|       Color(hex)
 | |
|         .rotate(amount)
 | |
|         .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;
 | |
|   }
 | |
| }
 |