199 lines
6.5 KiB
Vue
199 lines
6.5 KiB
Vue
<template>
|
|
<div class="identicon"
|
|
v-bind:style="{backgroundImage: 'url('+img+')', width: 8*size+'px', height: 8*size+'px'}">
|
|
</div>
|
|
</template>
|
|
|
|
<script>
|
|
export default {
|
|
name: "Identicon",
|
|
props: {
|
|
address: {
|
|
default: ""
|
|
},
|
|
size: {
|
|
type: Number,
|
|
default: 5
|
|
}
|
|
},
|
|
data () {
|
|
return {
|
|
randseed: new Array(4),
|
|
img: "",
|
|
defaultImg: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH4gkHECkpHU3uFgAAAZlJREFUWMPt2D1PwkAYB/D/AxWIbZUCAvGNwZiAcTG+JQ4sJn4AV7+UfgNXjY6ObrppXBiEGBMUgimKKHDy2jpUUHGTIhe5Z7rc8OTX9p67e0oXu3cmOA4HOA8BFEABFMBhB0q9JnBrEgIxGeqsGx7fCBwSoVExUMpUoV+VwPTG4ICRLQ2BmPJj3qU44Y/K8EdlpE8LeExUBgPU5kZhGiaYXkfmrIhytg6SgMn1cYSWVRARIps+ML3+6zfZE5Dl60gd5b/NmU0ge/6CQpJhYScMAPBFZTC9+PdF0o37GtViE6ZhXZQ8Xom/KiYCQNa4+WbwB5yOe0FkCQspxhcwtKJiYtGq7ucbhtd0dXD7YHeEV1VMbXgBAOVcDbcnT/ycJMElpYOrPNSQPNR5OupMzMS1zvZzfaDbktU2YHht7HP7OdZte2zbgHLI3Rm3aiZ/QHJ+fGjD3i7WNqDTZaUiB/F13WqHHRXbV6BHk0BOAhHA8g3+gPPbQbgUayFe7t3ztwbNVn9+8fz/nqQdif2caDsFUAAFUACHEfgOXvt3FLbL3AsAAAAASUVORK5CYII="
|
|
}
|
|
},
|
|
created() {
|
|
if(this.address && this.isAddressValid(this.address)) {
|
|
this.createIcon({
|
|
seed: this.address,
|
|
scale: this.size
|
|
})
|
|
} else {
|
|
this.img = this.defaultImg
|
|
}
|
|
},
|
|
watch: {
|
|
address: function(address) {
|
|
if(address && this.isAddressValid(address)) {
|
|
this.createIcon({
|
|
seed: address,
|
|
scale: this.size
|
|
})
|
|
} else {
|
|
this.img = this.defaultImg
|
|
}
|
|
}
|
|
},
|
|
methods: {
|
|
isAddressValid(input) {
|
|
|
|
if(!(/^[0-9A-Za-z]+$/.test(input))) return false
|
|
|
|
switch (input.substring(0,4)) {
|
|
case "Sumo":
|
|
case "RYoL":
|
|
case "Suto":
|
|
case "RYoT":
|
|
return input.length === 99
|
|
|
|
case "Subo":
|
|
case "Suso":
|
|
return input.length == 98
|
|
|
|
case "RYoS":
|
|
case "RYoU":
|
|
return input.length == 99
|
|
|
|
case "Sumi":
|
|
case "RYoN":
|
|
case "Suti":
|
|
case "RYoE":
|
|
return input.length === 110
|
|
|
|
case "RYoK":
|
|
case "RYoH":
|
|
return input.length === 55
|
|
|
|
default:
|
|
return false
|
|
}
|
|
},
|
|
|
|
seedrand(seed) {
|
|
for (var i = 0; i < this.randseed.length; i++) {
|
|
this.randseed[i] = 0;
|
|
}
|
|
for (var i = 0; i < seed.length; i++) {
|
|
this.randseed[i%4] = ((this.randseed[i%4] << 5) - this.randseed[i%4]) + seed.charCodeAt(i);
|
|
}
|
|
},
|
|
|
|
rand() {
|
|
// based on Java's String.hashCode(), expanded to 4 32bit values
|
|
var t = this.randseed[0] ^ (this.randseed[0] << 11);
|
|
|
|
this.randseed[0] = this.randseed[1];
|
|
this.randseed[1] = this.randseed[2];
|
|
this.randseed[2] = this.randseed[3];
|
|
this.randseed[3] = (this.randseed[3] ^ (this.randseed[3] >> 19) ^ t ^ (t >> 8));
|
|
|
|
return (this.randseed[3]>>>0) / ((1 << 31)>>>0);
|
|
},
|
|
|
|
createColor() {
|
|
//saturation is the whole color spectrum
|
|
var h = Math.floor(this.rand() * 360);
|
|
//saturation goes from 40 to 100, it avoids greyish colors
|
|
var s = ((this.rand() * 60) + 40) + '%';
|
|
//lightness can be anything from 0 to 100, but probabilities are a bell curve around 50%
|
|
var l = ((this.rand()+this.rand()+this.rand()+this.rand()) * 25) + '%';
|
|
|
|
var color = 'hsl(' + h + ',' + s + ',' + l + ')';
|
|
return color;
|
|
},
|
|
|
|
createImageData(size) {
|
|
var width = size; // Only support square icons for now
|
|
var height = size;
|
|
|
|
var dataWidth = Math.ceil(width / 2);
|
|
var mirrorWidth = width - dataWidth;
|
|
|
|
var data = [];
|
|
for(var y = 0; y < height; y++) {
|
|
var row = [];
|
|
for(var x = 0; x < dataWidth; x++) {
|
|
// this makes foreground and background color to have a 43% (1/2.3) probability
|
|
// spot color has 13% chance
|
|
row[x] = Math.floor(this.rand()*2.3);
|
|
}
|
|
var r = row.slice(0, mirrorWidth);
|
|
r.reverse();
|
|
row = row.concat(r);
|
|
|
|
for(var i = 0; i < row.length; i++) {
|
|
data.push(row[i]);
|
|
}
|
|
}
|
|
|
|
return data;
|
|
},
|
|
|
|
buildOpts(opts) {
|
|
var newOpts = {};
|
|
|
|
newOpts.seed = opts.seed || Math.floor((Math.random()*Math.pow(10,16))).toString(16);
|
|
|
|
this.seedrand(newOpts.seed);
|
|
|
|
newOpts.size = opts.size || 8;
|
|
newOpts.scale = opts.scale || 4;
|
|
newOpts.color = opts.color || this.createColor();
|
|
newOpts.bgcolor = opts.bgcolor || this.createColor();
|
|
newOpts.spotcolor = opts.spotcolor || this.createColor();
|
|
|
|
return newOpts;
|
|
},
|
|
|
|
renderIcon(opts, canvas) {
|
|
opts = this.buildOpts(opts || {});
|
|
var imageData = this.createImageData(opts.size);
|
|
var width = Math.sqrt(imageData.length);
|
|
|
|
canvas.width = canvas.height = opts.size * opts.scale;
|
|
|
|
var cc = canvas.getContext('2d');
|
|
cc.fillStyle = opts.bgcolor;
|
|
cc.fillRect(0, 0, canvas.width, canvas.height);
|
|
cc.fillStyle = opts.color;
|
|
|
|
for(var i = 0; i < imageData.length; i++) {
|
|
|
|
// if data is 0, leave the background
|
|
if(imageData[i]) {
|
|
var row = Math.floor(i / width);
|
|
var col = i % width;
|
|
|
|
// if data is 2, choose spot color, if 1 choose foreground
|
|
cc.fillStyle = (imageData[i] == 1) ? opts.color : opts.spotcolor;
|
|
|
|
cc.fillRect(col * opts.scale, row * opts.scale, opts.scale, opts.scale);
|
|
}
|
|
}
|
|
return canvas;
|
|
},
|
|
|
|
createIcon(opts) {
|
|
var canvas = document.createElement('canvas');
|
|
this.renderIcon(opts, canvas);
|
|
this.img = canvas.toDataURL()
|
|
}
|
|
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
</style>
|