UNPKG

@cfx-kit/wallet-avatar

Version:

| Statements | Branches | Functions | Lines | | --------------------------- | ----------------------- | ------------------------- | ----------------- | | ![Statements](https://img.shields.io/badg

280 lines (272 loc) 10.7 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('mersenne-twister')) : typeof define === 'function' && define.amd ? define(['exports', 'mersenne-twister'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global["wallet-avatar"] = {}, global.MersenneTwister)); })(this, (function (exports, MersenneTwister) { 'use strict'; function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; } var MersenneTwister__default = /*#__PURE__*/_interopDefaultLegacy(MersenneTwister); function escapeHTML(html) { const map = { '&': '&amp;', '<': '&lt;', '>': '&gt;', '"': '&quot;', "'": '&#039;', }; return `${html}`.replace(/[&<>"']/g, (i) => { return map[i] || i; }); } function valueToHTML(v) { if (v instanceof HTMLTemplate) { return v.toString(); } if (Array.isArray(v)) { return v.map((i) => valueToHTML(i)).join(''); } if ((typeof v === 'boolean' && v === false) || v === undefined || v === null) { return ''; } return escapeHTML(v); } class HTMLTemplate { constructor(strings, values) { this.strings = strings; this.values = values; } getHTML() { const { strings, values } = this; let str = strings[0]; for (let i = 1; i < strings.length; i++) { const v = values[i - 1]; str += `${valueToHTML(v)}${strings[i]}`; } return str; } toString() { return this.getHTML(); } } function html(strings, ...values) { return new HTMLTemplate(strings, values); } const colors = [ '#01888C', '#FC7500', '#034F5D', '#F73F01', '#FC1960', '#C7144C', '#F3C100', '#1598F2', '#2465E1', '#F19E02', // gold ]; const shapeCount = 4; const svgNamespaceURI = 'http://www.w3.org/2000/svg'; function HSLToHex(hsl) { let { h, s, l } = hsl; s /= 100; l /= 100; let c = (1 - Math.abs(2 * l - 1)) * s, x = c * (1 - Math.abs(((h / 60) % 2) - 1)), m = l - c / 2, _r = 0, _g = 0, _b = 0; if (0 <= h && h < 60) { _r = c; _g = x; _b = 0; } else if (60 <= h && h < 120) { _r = x; _g = c; _b = 0; } else if (120 <= h && h < 180) { _r = 0; _g = c; _b = x; } else if (180 <= h && h < 240) { _r = 0; _g = x; _b = c; } else if (240 <= h && h < 300) { _r = x; _g = 0; _b = c; } else if (300 <= h && h < 360) { _r = c; _g = 0; _b = x; } // Having obtained RGB, convert channels to hex let r = Math.round((_r + m) * 255).toString(16), g = Math.round((_g + m) * 255).toString(16), b = Math.round((_b + m) * 255).toString(16); // Prepend 0s, if necessary if (r.length == 1) r = '0' + r; if (g.length == 1) g = '0' + g; if (b.length == 1) b = '0' + b; return '#' + r + g + b; } function addressToNumber(address) { const addr = address.slice(2, 10); const seed = parseInt(addr, 16); return seed; } class WalletAvatarGenerate { constructor(mt) { this.mt = mt; } generateAvatarSvg(address) { const html = this.generateAvatarHTML(address); const temp = document.createElement('template'); temp.innerHTML = html; return temp.content.firstChild; } generateAvatarURL(address) { const html = this.generateAvatarHTML(address); return `data:image/svg+xml;base64,${btoa(html)}`; } canvasRender(address, size) { const diameter = size; const canvas = document.createElement('canvas'); const ctx = canvas.getContext('2d'); canvas.height = canvas.width = size; this.mt.init_seed(addressToNumber(address)); const remainingColors = this.hueShift(colors.slice()); const bgColor = this.genColor(remainingColors); ctx.fillStyle = bgColor; ctx.fillRect(0, 0, diameter, diameter); for (let i = 0; i < shapeCount - 1; i++) { const total = shapeCount - 1; const firstRot = this.mt.random(); const angle = Math.PI * 2 * firstRot; const velocity = (diameter * this.mt.random()) / total + (diameter * i) / total; const tx = Math.cos(angle) * velocity; const ty = Math.sin(angle) * velocity; const secondRot = this.mt.random(); const rot = firstRot * 360 + secondRot * 180; const fill = this.genColor(remainingColors); const p = new Path2D(); p.rect(0, 0, diameter, diameter); ctx.fillStyle = fill; const { x, y } = new DOMMatrix() .translate(0, 0) .rotate(rot) .transformPoint({ x: -diameter / 2, y: -diameter / 2 }); ctx.translate(x + diameter / 2 + tx, y + diameter / 2 + ty); ctx.rotate((rot * Math.PI) / 180); ctx.fill(p); ctx.resetTransform(); } return canvas; } generateAvatarPNG(address, quality = 2) { const size = (quality > 1 ? Math.min(10, quality) : 1) * 100; return this.canvasRender(address, size).toDataURL('image/png', 1); } generateAvatarHTML(address) { this.mt.init_seed(addressToNumber(address)); const remainingColors = this.hueShift(colors.slice()); const bgColor = this.genColor(remainingColors); const bgRectHTML = html `<rect x="0" y="0" width="100" height="100" fill="${bgColor}"></rect>`; const items = Array(shapeCount - 1) .fill('$') .map((_, i) => { return this.genShape(remainingColors, i, shapeCount - 1); }); const svgHTML = html `<svg width="100%" height="100%" viewBox="0 0 100 100" xmlns="${svgNamespaceURI}">${bgRectHTML}${items}</svg>`; return svgHTML.getHTML(); } genShape(remainingColors, i, total) { const diameter = 100; const center = diameter / 2; const firstRot = this.mt.random(); const angle = Math.PI * 2 * firstRot; const velocity = (diameter / total) * this.mt.random() + (i * diameter) / total; const tx = Math.cos(angle) * velocity; const ty = Math.sin(angle) * velocity; const translate = 'translate(' + tx + ' ' + ty + ')'; const secondRot = this.mt.random(); const rot = firstRot * 360 + secondRot * 180; const rotate = 'rotate(' + rot.toFixed(1) + ' ' + center + ' ' + center + ')'; const transform = translate + ' ' + rotate; const fill = this.genColor(remainingColors); const rectHTML = html `<rect x="0" y="0" width="${diameter}" height="${diameter}" transform="${transform}" fill="${fill}"></rect>`; return rectHTML; } genColor(colors) { // must call once this.mt.random(); const idx = Math.floor(colors.length * this.mt.random()); const color = colors.splice(idx, 1)[0]; return color; } hueShift(colors) { const wobble = 30; const amount = this.mt.random() * 30 - wobble / 2; const rotate = (hex) => this.colorRotate(hex, amount); return colors.map(rotate); } colorRotate(hex, degrees) { const hsl = this.hexToHSL(hex); let hue = hsl.h; hue = (hue + degrees) % 360; hue = hue < 0 ? 360 + hue : hue; hsl.h = hue; return HSLToHex(hsl); } hexToHSL(hex) { // Convert hex to RGB first let r = parseInt('0x' + hex[1] + hex[2]); let g = parseInt('0x' + hex[3] + hex[4]); let b = parseInt('0x' + hex[5] + hex[6]); // Then to HSL r /= 255; g /= 255; b /= 255; let cmin = Math.min(r, g, b), cmax = Math.max(r, g, b), delta = cmax - cmin, h = 0, s = 0, l = 0; if (delta == 0) h = 0; else if (cmax == r) h = ((g - b) / delta) % 6; else if (cmax == g) h = (b - r) / delta + 2; else h = (r - g) / delta + 4; h = Math.round(h * 60); if (h < 0) h += 360; l = (cmax + cmin) / 2; s = delta == 0 ? 0 : delta / (1 - Math.abs(2 * l - 1)); s = +(s * 100).toFixed(1); l = +(l * 100).toFixed(1); return { h, s, l }; } } function generateAvatarSVG(address) { const wag = new WalletAvatarGenerate(new MersenneTwister__default["default"]()); return wag.generateAvatarSvg(address); } function generateAvatarURL(address) { const wag = new WalletAvatarGenerate(new MersenneTwister__default["default"]()); return wag.generateAvatarURL(address); } function generateAvatarHTML(address) { const wag = new WalletAvatarGenerate(new MersenneTwister__default["default"]()); return wag.generateAvatarHTML(address); } function generateAvatarPNG(address, quality) { const wag = new WalletAvatarGenerate(new MersenneTwister__default["default"]()); return wag.generateAvatarPNG(address, quality); } exports.WalletAvatarGenerate = WalletAvatarGenerate; exports["default"] = generateAvatarSVG; exports.generateAvatarHTML = generateAvatarHTML; exports.generateAvatarPNG = generateAvatarPNG; exports.generateAvatarSVG = generateAvatarSVG; exports.generateAvatarURL = generateAvatarURL; Object.defineProperty(exports, '__esModule', { value: true }); }));