Permalink
Browse files

First version of avatar

  • Loading branch information...
1 parent 0529d21 commit a55ac84ff1d60ddb43cd6f7376aab2a538ddf872 @tobiaslins committed Feb 10, 2017
Showing with 2,160 additions and 1 deletion.
  1. +3 โˆ’0 .gitignore
  2. +3 โˆ’1 README.md
  3. +37 โˆ’0 helper.js
  4. +43 โˆ’0 index.js
  5. +29 โˆ’0 package.json
  6. +15 โˆ’0 svg.js
  7. +2,030 โˆ’0 yarn.lock
View
@@ -0,0 +1,3 @@
+node_modules
+*.log
+test.html
View
@@ -1 +1,3 @@
-# avatar
+# avatar
+
+Avatar as a microservice generates beautiful generated avatars for your app or homepage.
View
@@ -0,0 +1,37 @@
+function djb2(str) {
+ let hash = 5381
+ for (let i = 0; i < str.length; i++) {
+ hash = ((hash << 5) + hash) + str.charCodeAt(i)
+ }
+ return hash
+}
+
+function shouldChangeColor(color) {
+ const rgb = color.rgb().array()
+ const val = 765 - (rgb[0] + rgb[1] + rgb[2])
+ if (val < 250) {
+ return true
+ }
+ return false
+}
+
+exports.hashStringToColor = function (str) {
+ const hash = djb2(str)
+ const r = (hash & 0xFF0000) >> 16
+ const g = (hash & 0x00FF00) >> 8
+ const b = hash & 0x0000FF
+ return '#' + ('0' + r.toString(16)).substr(-2) + ('0' + g.toString(16)).substr(-2) + ('0' + b.toString(16)).substr(-2)
+}
+
+exports.getMatchingColor = function (firstColor) {
+ let color = firstColor
+ if (color.dark()) {
+ color = color.saturate(0.3).rotate(90)
+ } else {
+ color = color.desaturate(0.3).rotate(90)
+ }
+ if (shouldChangeColor(color)) {
+ color = color.rotate(-200).saturate(0.5)
+ }
+ return color
+}
View
@@ -0,0 +1,43 @@
+const crypto = require('crypto')
+const url = require('url')
+
+const sharp = require('sharp')
+const Color = require('color')
+
+const svg = require('./svg')
+const helper = require('./helper')
+
+module.exports = (req, res) => {
+ const {pathname, query} = url.parse(req.url, true)
+ let imageSize = 150
+ const maxSize = 1000
+
+ if (pathname === '/favicon.ico') {
+ return ''
+ }
+ res.setHeader('Cache-Control', 'max-age=2592000, public')
+ if (query.type === 'svg') {
+ res.setHeader('Content-Type', 'svg+xml')
+ } else {
+ res.setHeader('Content-Type', 'image/png')
+ }
+
+ const hash = crypto.createHash('md5').update(pathname).digest('hex')
+
+ let firstColor = helper.hashStringToColor(hash)
+ firstColor = new Color(firstColor).saturate(0.5)
+
+ let avatar = svg.replace('$FIRST', firstColor.hex())
+ avatar = avatar.replace('$SECOND', helper.getMatchingColor(firstColor).hex())
+
+ if (query.type === 'svg') {
+ return avatar
+ }
+
+ if (query.size && query.size.match(/^-?\d+$/) && query.size <= maxSize) {
+ imageSize = parseInt(query.size, 10)
+ }
+
+ const png = sharp(new Buffer(avatar)).resize(imageSize, imageSize).png()
+ return png
+}
View
@@ -0,0 +1,29 @@
+{
+ "name": "avatar",
+ "version": "1.0.0",
+ "main": "index.js",
+ "repository": "tobiaslins/avatar",
+ "scripts": {
+ "start": "micro"
+ },
+ "author": "Tobias Lins <tobias@lins.in>",
+ "license": "MIT",
+ "dependencies": {
+ "color": "^1.0.3",
+ "micro": "^7.0.3",
+ "sharp": "^0.17.1",
+ "url": "^0.11.0"
+ },
+ "devDependencies": {
+ "eslint-config-xo-react": "^0.10.0",
+ "xo": "^0.17.1"
+ },
+ "xo": {
+ "esnext": true,
+ "semicolon": false,
+ "space": true,
+ "env": [
+ "node"
+ ]
+ }
+}
View
15 svg.js
@@ -0,0 +1,15 @@
+module.exports = `<?xml version="1.0" standalone="no"?>
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg width="120" height="120" viewBox="120 120 120 120" version="1.1" xmlns="http://www.w3.org/2000/svg">
+ <g>
+ <defs>
+ <linearGradient id="avatar" x1="0" y1="0" x2="1" y2="1">
+ <stop offset="0%" stop-color="$FIRST"/>
+ <stop offset="100%" stop-color="$SECOND"/>
+ </linearGradient>
+ </defs>
+
+ <rect fill="url(#avatar)" x="120" y="120" width="120" height="120"/>
+ </g>
+</svg>
+`
Oops, something went wrong.

0 comments on commit a55ac84

Please sign in to comment.