Skip to content

Commit

Permalink
Adding docs for demo purposes
Browse files Browse the repository at this point in the history
  • Loading branch information
vicalejuri committed Feb 20, 2017
1 parent 9856f76 commit 7db56ca
Show file tree
Hide file tree
Showing 2 changed files with 309 additions and 0 deletions.
194 changes: 194 additions & 0 deletions docs/cat-generator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,194 @@
// returns a function that calls `fn` when args are != from the previous call
// Otherwise (same args), do nothing.
// A special case of memoize.
function cacheFunctionFor( fn ) {
let old_args = []
return (...args) => {
if(args != old_args){
old_args = args
fn(...args)
}
else return;
}
}

let $ = function(selector){
if(typeof selector == 'string'){ return document.querySelector(selector) }
if(typeof selector == 'object' && selector.nodeType == 1){ return selector }
throw TypeError("$() received an invalid input (" + selector + "). Must be a string or DOMNode! ")
}

// from pairsArray [[name,x], ...] to object {name: x, ...}
let fromPairs = (pairsArr) => {
let o = {}
pairsArr.map( ([name,img]) => { o[name] = img})
return o;
}

/*
* Dynamic catties avatar using ES6 and canvas.
* Author: barrabinfc
*
* Usage:
*
* <div class="avatar" data-seed="myusername">
* <img alt="myusername" class="small">
* </div>
* <div class="avatar" data-seed="otherusername">
* <img alt="otherusername" class="big">
* </div>
* <script>
* var cat = new Meowatar({selector: '.avatar'})
* </script>
*
*
*/
class Meowatar {
constructor(opts){
this.options = Object.assign( {selector: '.avatar',
img_tag: 'img',
assets_path: '/avatars/',
seed: 0,
w: 256,
h: 256,
parts: {
'body': {url_prefix: 'body', qtd: 15},
'fur': {url_prefix: 'fur', qtd: 10},
'eyes': {url_prefix: 'eyes', qtd: 15},
'mouth': {url_prefix: 'mouth', qtd: 10 },
'acessorie': {url_prefix: 'accessorie', qtd: 20},
//'zz': {url_prefix: 'zz', qtd: 2}
}} , opts )
this.seed = this.options.seed;
this.addClassOnceFor = cacheFunctionFor( this.addClass )
this.addCanvas()
this.render()
}

// get the image URL for body `part` `i`
cattyURL(part, i){
return this.options.assets_path + (this.options.parts[part].url_prefix + '_' +
((i % this.options.parts[part].qtd )+1) + '.png')
}


// set seed from a Number or a String.
seedStart( startSeed ){
let parseStr = ( seed ) => { return seed.split('').reduce( (a,b) => {return a + b.charCodeAt()}, 0) }
let error = () => { throw TypeError("meowatar - Seed must be a number or string") }

this._seed = ((typeof startSeed == 'number')
? startSeed
: (typeof startSeed == 'string')
? parseStr(startSeed)
: error() )
}

/* pseudo-random with seed reproducibility
* Repeats every 233280 iteractions
* https://en.wikipedia.org/wiki/Linear_congruential_generator
*/
seededRandom(max, min) {
max = max || 1000;
min = min || 0;

this._seed = (this._seed * 9301 + 49297) % 233280;
var rnd = this._seed / 233280;

return Math.floor( min + rnd * (max - min));
}

addCanvas() {
this.containers = document.querySelectorAll(this.options.selector)
this.canvas_el = document.createElement('canvas')
this.canvas_el.width = this.options.w; this.canvas_el.height = this.options.h;
}
getDataURL(){ return this.canvas_el.toDataURL(); }



// load a single image Element
loadImage(name, img_uri){
return new Promise( (resolve, reject) => {
var newImg = new Image()
newImg.src = img_uri
newImg.onload = () => { resolve(newImg) }
newImg.onerror = () => { reject(newImg) }
return newImg
})
}

/*
* Load a group of images: [['name',imgURL], ...] , returns a promise
*/
preloadImages( imagesURL ) {
return Promise.all( imagesURL.map( ([name,url]) => {
return this.loadImage(name,url).then( (img) => {
return [name, img]
})
}))
}

getCtx(){
if(!this.canvas_el)
this.addCanvas()
return this.canvas_el.getContext('2d')
}

drawImg( img ){
this._ctx.drawImage(img, 0,0, this.options.w, this.options.h )
}

addClass( el, klass ) {
$(el).className += (' ' + klass)
}

// show avatar on the <img> tag of parent Element
showImage( container_el , dataURL ) {
let img = $(container_el).querySelector( this.options.img_tag )
img.src = dataURL
}

/*
* Render every selector matched
*/
render(){
this.containers.forEach( (el) => {
let seed = el.getAttribute('data-seed') || this.seed
window.requestAnimationFrame( this.draw.bind( this, seed , el) )
})
}

/* Create a new avatar from the seed.
The same seed will yield always the same avatar
Renders to EL
*/
draw( startSeed=Math.floor( Math.random()*1000) , el=this.containers[0]) {
this.seedStart(startSeed)
this._ctx = this.getCtx()

let _imagesURL = [ ['body', this.cattyURL('body',this.seededRandom())],
['fur', this.cattyURL('fur', this.seededRandom())],
['eyes', this.cattyURL('eyes',this.seededRandom())],
['mouth', this.cattyURL('mouth',this.seededRandom())],
['acessorie', this.cattyURL('acessorie',this.seededRandom())],
]

// load all images
this.preloadImages( _imagesURL).then( (imgs) => {
let _images = fromPairs(imgs)

// now draw it on <el>
this._ctx.clearRect(0,0, this.options.w, this.options.h)
this.drawImg( _images.body )
this.drawImg( _images.fur )
this.drawImg( _images.eyes )
this.drawImg( _images.mouth )
this.drawImg( _images.acessorie )

// and show as a <img> tag
this.showImage( el , this.getDataURL() )
this.addClassOnceFor( el, 'loaded')
})
}
}
115 changes: 115 additions & 0 deletions docs/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Cat avatar generator</title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta http-equiv="Content-Language" content="en-ca" />
<link rel="Shortcut Icon" href="favicon.png" type="image/x-icon" />

<style type="text/css" media="screen">
body { margin: 2em; padding: 0; background: #000; color: #666; font-size: .8rem; text-align: center; }
#wrapper { text-align: left; background: #fff; max-width: 300px; padding: 1rem; margin: 0 auto; border: 1em #333 solid; text-align: center; }
h1 { font-variant: small-caps; color: hotpink; font-size: 1.8rem; font-family: Ubuntu, Arial, sans; font-weight: bold; margin: 0 0 0.3rem 0; }


.card {
display: flex;
align-items: baseline;
}
.small { 50%; overflow: hidden; width: 2.5em; height: 2.5em;}


.avatar { display: block;
transition: transform 0.15s ease-in-out;
transform: scale(0.8);
}
.avatar.inline { display: inline-block; }
.avatar.loaded { transform: scale(1.0); }

#main-avatar > img {
z-index: 10;
}
#main-avatar.loaded.shadow:after {
display: block;
content: ' ';
z-index: 5;
width: 50%;
height: inherit;
margin: 0px auto;
border-radius: 50%;
box-shadow: 0 0 30px 5px rgba(0, 0, 0, 0.58);
transform: translate(0,-5px);
}

a { color: #00aad4;}
a:hover { color: #222;}
.smallbutton { font-size: 1rem; margin-bottom: 0.8rem; }

input[type=text]{width:90%}
.bigbutton { font-weight: bold; font-size: 1.3rem; margin-bottom: 1.3rem; }

@media (max-width: 500px) {
body { margin: 0; padding: 0; background: #FFF; font-size: 1rem; }
#wrapper { text-align: left; background: #fff; max-width: 100%; padding: 0; margin: 0 auto; border: none; text-align: center; }
h1 { font-size: 1.6rem; margin: 0.2rem 0 0.2rem 0; }
}
</style>
</head>
<body>


<div id="wrapper">

<h1>Cat avatar generator</h1>

<div class="avatar shadow" id="main-avatar">
<img alt="Avatar profile">
</div>

<form onSubmit="return onSubmit()">
<br/>
<br/>
Your name?*<br/>
<input class="smallbutton" type="text" name="seed" id="name" value="linux" autofocus />
<input class="bigbutton" type="submit" value="Generate new cat" />
</form>

<br/>
<em>(* empty=random)</em><br/><br/>
Artworks: CC-By David Revoy
<div class="avatar inline shadow" data-seed="David Revoy">
<img alt="Avatar2" class="small">
</div>
<a href="http://www.peppercarrot.com" title="my webcomic">[site]</a>
<br/>
Code: CC-By Andreas Gohr
<a href="https://www.splitbrain.org/blog/2007-01/20_monsterid_as_gravatar_fallback" title="original author">[site]</a>
<div class="avatar inline shadow" data-seed="Andreas Gohr">
<img alt="Avatar Image" class="small">
</div>
Javascript version by barrabinfc
<a href="github.com/barrabinfc" title="javascript author">[site]</a>
<div class="avatar inline shadow" data-seed="barrabinfc">
<img alt="Avatar Image" class="small">
</div>

<br/>
<a href="2016_cat-generator.zip" title="original author">[Zip sources and artwork]</a>

</div>
<script src="/cat-generator.js"></script>
<script>
let options = {seed: 'linux', selector: '.avatar'};
var cat = new Meowatar(options);

function onSubmit( ev ){

let username = document.querySelector('input[name="seed"]').value
cat.draw( username || undefined , document.querySelector('#main-avatar') )

return false;
}
</script>

</body>
</html>

0 comments on commit 7db56ca

Please sign in to comment.