This commit is contained in:
parent
59373c6380
commit
6515bda730
18 changed files with 728 additions and 39 deletions
492
frontend-vue/src/utils/identiheart.ts
Normal file
492
frontend-vue/src/utils/identiheart.ts
Normal file
|
@ -0,0 +1,492 @@
|
|||
class Crusher {
|
||||
hash(s: any): number {
|
||||
return String(s).split("").reduce(function(a, b) {
|
||||
a = ((a << 5) - a) + b.charCodeAt(0);
|
||||
return a & a
|
||||
}, 0);
|
||||
}
|
||||
isDOMElement(o:any): boolean {
|
||||
return (
|
||||
typeof HTMLElement === "object" ? o instanceof HTMLElement :
|
||||
o && typeof o === "object" && true && o.nodeType === 1 && typeof o.nodeName==="string"
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum BlockType {
|
||||
ONE = 1,
|
||||
TWO = 2,
|
||||
THREE = 3,
|
||||
}
|
||||
|
||||
class Block {
|
||||
canvas: HTMLCanvasElement;
|
||||
context: CanvasRenderingContext2D;
|
||||
type: BlockType;
|
||||
primary: string;
|
||||
accent: string;
|
||||
cellSize: number;
|
||||
margin: number;
|
||||
scale: number;
|
||||
pos: Position;
|
||||
hash: number;
|
||||
|
||||
constructor(
|
||||
c: HTMLCanvasElement,
|
||||
ctx: CanvasRenderingContext2D,
|
||||
type: BlockType,
|
||||
primary: string,
|
||||
accent: string,
|
||||
hash: number,
|
||||
cellSize: number,
|
||||
margin: number,
|
||||
scale: number,
|
||||
pos: Position,
|
||||
) {
|
||||
this.canvas = c;
|
||||
this.context = ctx;
|
||||
this.type = type;
|
||||
this.primary = primary;
|
||||
this.accent = accent;
|
||||
this.hash = hash;
|
||||
this.cellSize = cellSize;
|
||||
this.margin = margin;
|
||||
this.scale = scale;
|
||||
this.pos = pos;
|
||||
}
|
||||
|
||||
offset() {
|
||||
this.context.save();
|
||||
this.context.translate(0.6 * this.scale, -0.6 * this.scale);
|
||||
}
|
||||
|
||||
resetOffset() {
|
||||
this.context.restore();
|
||||
}
|
||||
|
||||
makePath(hash: number, offset: number) {
|
||||
const mod = Math.abs(hash + offset) % 4;
|
||||
|
||||
switch(mod) {
|
||||
case 0:
|
||||
// top
|
||||
this.context.beginPath();
|
||||
this.context.moveTo(this.pos.x, this.pos.y);
|
||||
this.context.lineTo(this.pos.x + this.cellSize, this.pos.y);
|
||||
this.context.lineTo(this.pos.x + this.cellSize, this.pos.y + this.cellSize);
|
||||
this.context.closePath();
|
||||
break;
|
||||
case 1:
|
||||
// right
|
||||
this.context.beginPath();
|
||||
this.context.moveTo(this.pos.x + this.cellSize, this.pos.y);
|
||||
this.context.lineTo(this.pos.x + this.cellSize, this.pos.y + this.cellSize);
|
||||
this.context.lineTo(this.pos.x, this.pos.y + this.cellSize);
|
||||
this.context.closePath();
|
||||
break;
|
||||
case 2:
|
||||
// bottom
|
||||
this.context.beginPath();
|
||||
this.context.moveTo(this.pos.x, this.pos.y);
|
||||
this.context.lineTo(this.pos.x, this.pos.y + this.cellSize);
|
||||
this.context.lineTo(this.pos.x + this.cellSize, this.pos.y + this.cellSize);
|
||||
this.context.closePath();
|
||||
break;
|
||||
case 3:
|
||||
// left
|
||||
this.context.beginPath();
|
||||
this.context.moveTo(this.pos.x, this.pos.y);
|
||||
this.context.lineTo(this.pos.x + this.cellSize, this.pos.y);
|
||||
this.context.lineTo(this.pos.x, this.pos.y + this.cellSize);
|
||||
this.context.closePath();
|
||||
break;
|
||||
default:
|
||||
// top
|
||||
this.context.beginPath();
|
||||
this.context.moveTo(this.pos.x, this.pos.y);
|
||||
this.context.lineTo(this.pos.x + this.cellSize, this.pos.y);
|
||||
this.context.lineTo(this.pos.x, this.pos.y + this.cellSize);
|
||||
this.context.closePath();
|
||||
}
|
||||
}
|
||||
|
||||
draw() {
|
||||
this.offset();
|
||||
|
||||
if (this.type === BlockType.ONE) {
|
||||
this.makePath(this.hash, this.hash % 3);
|
||||
this.context.fillStyle = this.primary;
|
||||
this.context.fill();
|
||||
|
||||
this.makePath(this.hash, this.hash % 5);
|
||||
this.context.fillStyle = this.accent;
|
||||
this.context.fill();
|
||||
} else if (this.type === BlockType.TWO) {
|
||||
this.makePath(this.hash, this.hash % 4);
|
||||
this.context.fillStyle = this.accent;
|
||||
this.context.fill();
|
||||
|
||||
this.makePath(this.hash, this.hash % 3);
|
||||
this.context.fillStyle = this.primary;
|
||||
this.context.fill();
|
||||
} else {
|
||||
this.makePath(this.hash, this.hash % 7);
|
||||
this.context.fillStyle = this.accent;
|
||||
this.context.fill();
|
||||
|
||||
this.makePath(this.hash, this.hash % 8);
|
||||
this.context.fillStyle = this.primary;
|
||||
this.context.fill();
|
||||
}
|
||||
|
||||
this.resetOffset();
|
||||
}
|
||||
}
|
||||
|
||||
class Position {
|
||||
x: number;
|
||||
y: number;
|
||||
constructor(x: number, y: number) {
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
}
|
||||
|
||||
class Shape {
|
||||
canvas: HTMLCanvasElement;
|
||||
context: CanvasRenderingContext2D;
|
||||
hash: number;
|
||||
primary: string;
|
||||
accent: string;
|
||||
pos: Position;
|
||||
scale: number;
|
||||
cellSize: number;
|
||||
strokeColor: string;
|
||||
|
||||
constructor(
|
||||
c: HTMLCanvasElement,
|
||||
ctx: CanvasRenderingContext2D,
|
||||
hash: number,
|
||||
primary: string,
|
||||
accent: string,
|
||||
pos: Position,
|
||||
scale: number,
|
||||
cellSize: number,
|
||||
strokeColor: string
|
||||
) {
|
||||
this.canvas = c;
|
||||
this.context = ctx;
|
||||
this.hash = hash;
|
||||
this.primary = primary;
|
||||
this.accent = accent;
|
||||
this.pos = pos;
|
||||
this.scale = scale;
|
||||
this.cellSize = cellSize;
|
||||
this.strokeColor = strokeColor;
|
||||
}
|
||||
|
||||
getColor() {
|
||||
return [this.primary, this.accent][Math.abs(this.hash % 2)];
|
||||
}
|
||||
|
||||
makePath() {
|
||||
const mod = Math.abs(this.hash + 1) % 4;
|
||||
|
||||
switch(mod) {
|
||||
case 0:
|
||||
// square
|
||||
this.context.beginPath();
|
||||
this.context.moveTo(this.pos.x, this.pos.y);
|
||||
this.context.lineTo(this.pos.x + (this.cellSize / 2), this.pos.y);
|
||||
this.context.lineTo(this.pos.x + (this.cellSize / 2), this.pos.y - (this.cellSize / 2));
|
||||
this.context.lineTo(this.pos.x, this.pos.y - (this.cellSize / 2));
|
||||
this.context.closePath();
|
||||
break;
|
||||
case 1:
|
||||
//circle
|
||||
this.context.beginPath();
|
||||
this.context.arc(
|
||||
this.pos.x + (this.cellSize / Math.PI) - 5,
|
||||
this.pos.y - (this.cellSize / Math.PI) + 5,
|
||||
this.cellSize / 3,
|
||||
0,
|
||||
Math.PI * 2,
|
||||
true
|
||||
);
|
||||
break;
|
||||
case 2:
|
||||
// triangle
|
||||
this.context.beginPath();
|
||||
this.context.moveTo(this.pos.x, this.pos.y);
|
||||
this.context.lineTo(this.pos.x + (this.cellSize * 0.65), this.pos.y);
|
||||
this.context.lineTo(this.pos.x, this.pos.y - (this.cellSize * 0.65));
|
||||
this.context.closePath();
|
||||
break;
|
||||
case 3:
|
||||
// oval
|
||||
this.context.beginPath();
|
||||
this.context.moveTo(this.pos.x - (this.cellSize * 0.2), this.pos.y + (this.cellSize * 0.2));
|
||||
this.context.quadraticCurveTo(this.pos.x + (this.cellSize * 0.4), this.pos.y, this.pos.x + (this.cellSize * 0.5), this.pos.y - (this.cellSize * 0.5));
|
||||
this.context.moveTo(this.pos.x + (this.cellSize * 0.5), this.pos.y - (this.cellSize * 0.5));
|
||||
this.context.quadraticCurveTo(this.pos.x , this.pos.y - (this.cellSize * 0.4), this.pos.x - (this.cellSize * 0.2), this.pos.y + (this.cellSize * 0.2));
|
||||
break;
|
||||
default:
|
||||
//square
|
||||
this.context.beginPath();
|
||||
this.context.moveTo(this.pos.x, this.pos.y);
|
||||
this.context.lineTo(this.pos.x + (this.cellSize / 2), this.pos.y);
|
||||
this.context.lineTo(this.pos.x + (this.cellSize / 2), this.pos.y - (this.cellSize / 2));
|
||||
this.context.lineTo(this.pos.x, this.pos.y - (this.cellSize / 2));
|
||||
this.context.closePath();
|
||||
}
|
||||
}
|
||||
|
||||
draw(hasStroke: boolean, strokeWeight: number) {
|
||||
const color = this.getColor();
|
||||
this.context.globalCompositeOperation = "source-over";
|
||||
|
||||
this.makePath();
|
||||
this.context.fillStyle = color;
|
||||
this.context.strokeStyle = this.strokeColor;
|
||||
this.context.lineWidth = this.scale * ((4/5 * strokeWeight) / this.canvas.width);
|
||||
this.context.lineJoin = "round";
|
||||
this.context.lineCap = "round";
|
||||
this.context.fill();
|
||||
|
||||
if (hasStroke) {
|
||||
this.context.stroke();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @name IdentiHeart
|
||||
* @author Schlipak
|
||||
* @copyright Apache license 2015 Guillaume de Matos
|
||||
*/
|
||||
export class Identiheart {
|
||||
canvas: HTMLCanvasElement
|
||||
context: CanvasRenderingContext2D
|
||||
margin: number
|
||||
crusher: Crusher
|
||||
palette = [
|
||||
'#F44336', '#E91E63', '#9C27B0', '#673AB7', '#3F51B5', '#2196F3',
|
||||
'#03A9F4', '#00BCD4', '#009688', '#4CAF50', '#8BC34A', '#CDDC39',
|
||||
'#FFEB3B', '#FFC107', '#FF9800', '#FF5722', '#795548', '#607D8B'
|
||||
];
|
||||
|
||||
primary: string = "#F44336";
|
||||
accent: string = "#E91E63";
|
||||
scale: number;
|
||||
cellSize: number;
|
||||
hash: number = 0;
|
||||
blocks: Block[] = [];
|
||||
shape?: Shape;
|
||||
hasStroke: boolean = true;
|
||||
strokeWeight = 500;
|
||||
strokeColor = "#000000";
|
||||
compositeOperation: GlobalCompositeOperation = 'multiply';
|
||||
|
||||
constructor(c: HTMLCanvasElement, margin?: number, scale?: number, ctx?: CanvasRenderingContext2D) {
|
||||
this.canvas = c;
|
||||
this.context = ctx ?? c.getContext("2d")!;
|
||||
this.margin = margin || 5;
|
||||
this.scale = scale || 20;
|
||||
this.crusher = new Crusher();
|
||||
this.cellSize = (this.canvas.width / 2) - (this.margin * this.scale);
|
||||
}
|
||||
|
||||
setUsername(username: string) {
|
||||
this.hash = this.crusher.hash(username);
|
||||
return this;
|
||||
}
|
||||
|
||||
setPalette(palette:string[]) {
|
||||
if (palette.length < 2) {
|
||||
throw new Error('Palette must be more than two elements long');
|
||||
}
|
||||
|
||||
this.palette = palette;
|
||||
return this;
|
||||
}
|
||||
|
||||
setHasStroke(b: boolean) {
|
||||
this.hasStroke = b;
|
||||
return this;
|
||||
}
|
||||
|
||||
setStrokeWeight(weight: number) {
|
||||
this.strokeWeight = weight;
|
||||
return this;
|
||||
}
|
||||
|
||||
setStrokeColor(color: string) {
|
||||
this.strokeColor = color;
|
||||
return this;
|
||||
}
|
||||
|
||||
setCompositeOperation(operation: GlobalCompositeOperation) {
|
||||
this.compositeOperation = operation;
|
||||
return this;
|
||||
}
|
||||
|
||||
setCanvas(canvas: HTMLCanvasElement) {
|
||||
this.canvas = canvas;
|
||||
this.context = canvas.getContext('2d')!;
|
||||
return this;
|
||||
}
|
||||
|
||||
init() {
|
||||
// Purge the block array
|
||||
this.blocks = new Array<Block>();
|
||||
// Purge the shape
|
||||
this.shape = undefined;
|
||||
|
||||
// Generate colors
|
||||
const crusher = new Crusher();
|
||||
const subHash = crusher.hash(this.hash);
|
||||
this.accent = this.palette[Math.abs(subHash % this.palette.length)];
|
||||
|
||||
// Clear the canvas
|
||||
this.context.globalCompositeOperation = "source-over";
|
||||
this.context.clearRect(0, 0, this.canvas.width, this.canvas.height);
|
||||
this.context.globalCompositeOperation = this.compositeOperation;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
draw() {
|
||||
this.init();
|
||||
|
||||
// Rotate the canvas -45deg
|
||||
this.context.save();
|
||||
this.context.translate(this.canvas.width/2, this.canvas.height/2);
|
||||
this.context.rotate(- Math.PI / 4);
|
||||
this.context.translate(-this.canvas.width/2, -this.canvas.height/2);
|
||||
|
||||
this.generateBlocks();
|
||||
this.drawBlocks();
|
||||
|
||||
if (this.hasStroke) {
|
||||
this.drawOutline();
|
||||
}
|
||||
|
||||
this.shape = new Shape(this.canvas, this.context, this.hash, this.primary, this.accent, {
|
||||
x: (this.margin * this.scale) + 1.5 * this.cellSize,
|
||||
y: (this.margin * this.scale) + 0.5 * this.cellSize
|
||||
}, this.scale, this.cellSize, this.strokeColor);
|
||||
this.shape.draw(this.hasStroke, this.strokeWeight);
|
||||
|
||||
// Restore the original matrix
|
||||
this.context.restore();
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
offset() {
|
||||
this.context.save();
|
||||
this.context.translate(0.6 * this.scale, - 0.6 * this.scale);
|
||||
}
|
||||
|
||||
resetOffset() {
|
||||
this.context.restore();
|
||||
}
|
||||
|
||||
drawOutline() {
|
||||
this.offset();
|
||||
this.context.globalCompositeOperation = "source-over";
|
||||
|
||||
// Outer lines
|
||||
this.context.beginPath();
|
||||
this.context.moveTo(this.margin * this.scale, this.margin * this.scale);
|
||||
this.context.lineTo(this.margin * this.scale, this.canvas.height - (this.margin * this.scale));
|
||||
this.context.lineTo(this.canvas.width - (this.margin * this.scale), this.canvas.height - (this.margin * this.scale));
|
||||
this.context.lineTo(this.canvas.width - (this.margin * this.scale), this.canvas.height / 2);
|
||||
this.context.lineTo(this.canvas.width / 2, this.canvas.height / 2);
|
||||
this.context.lineTo(this.canvas.width / 2, this.margin * this.scale);
|
||||
this.context.closePath();
|
||||
|
||||
this.context.strokeStyle = this.strokeColor;
|
||||
this.context.lineWidth = this.scale * (this.strokeWeight / this.canvas.width);
|
||||
this.context.lineJoin = "round";
|
||||
this.context.lineCap = "round";
|
||||
this.context.stroke();
|
||||
|
||||
// Inner lines
|
||||
this.context.beginPath();
|
||||
this.context.moveTo(this.canvas.width / 2, this.canvas.height / 2);
|
||||
this.context.lineTo(this.margin * this.scale, this.canvas.height / 2);
|
||||
this.context.moveTo(this.canvas.width / 2, this.canvas.height / 2);
|
||||
this.context.lineTo(this.canvas.width / 2, this.canvas.height - (this.margin * this.scale));
|
||||
|
||||
this.context.stroke();
|
||||
|
||||
this.resetOffset();
|
||||
this.context.globalCompositeOperation = this.compositeOperation;
|
||||
}
|
||||
|
||||
generateBlocks() {
|
||||
/*
|
||||
c: HTMLCanvasElement,
|
||||
ctx: CanvasRenderingContext2D,
|
||||
type: BlockType,
|
||||
primary: string,
|
||||
accent: string,
|
||||
hash: number,
|
||||
cellSize: number,
|
||||
margin: number,
|
||||
scale: number,
|
||||
pos: Position,
|
||||
* */
|
||||
let b1 = new Block(
|
||||
this.canvas,
|
||||
this.context,
|
||||
BlockType.ONE,
|
||||
this.primary,
|
||||
this.accent,
|
||||
this.hash,
|
||||
this.cellSize,
|
||||
this.margin,
|
||||
this.scale,
|
||||
new Position(this.margin * this.scale,this.margin * this.scale));
|
||||
this.blocks.push(b1);
|
||||
|
||||
let b2 = new Block(
|
||||
this.canvas,
|
||||
this.context,
|
||||
BlockType.TWO,
|
||||
this.primary, this.accent,
|
||||
this.hash,
|
||||
this.cellSize,
|
||||
this.margin,
|
||||
this.scale,
|
||||
new Position(this.margin * this.scale,this.canvas.height / 2)
|
||||
);
|
||||
this.blocks.push(b2);
|
||||
|
||||
let b3 = new Block(
|
||||
this.canvas,
|
||||
this.context,
|
||||
BlockType.THREE,
|
||||
this.primary,
|
||||
this.accent,
|
||||
this.hash,
|
||||
this.cellSize,
|
||||
this.margin,
|
||||
this.scale,
|
||||
new Position(this.canvas.width / 2, this.canvas.height / 2)
|
||||
);
|
||||
this.blocks.push(b3);
|
||||
}
|
||||
|
||||
drawBlocks() {
|
||||
if (this.blocks.length == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (var i = 0; i < this.blocks.length; i++) {
|
||||
this.blocks[i].draw();
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue