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.
session-desktop/ts/components/JazzIcon/JazzIcon.tsx

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;
}
}