Last active
August 20, 2018 17:56
-
-
Save marekhrabe/5189295 to your computer and use it in GitHub Desktop.
Typing Enigma from http://getenigma64.com/
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<div id="enigma"></div> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Makes Enigma awesome. | |
* | |
* @class Enigma | |
* @param {Element} element Enigma main element | |
* @constructor | |
*/ | |
var Enigma = function (element) { | |
// basic environment | |
this.element = element; | |
this.playing = false; | |
this.writing = true; | |
this.time = +new Date(); | |
this.letters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split(''); | |
// wait mode | |
this.wait = false; | |
// button properties | |
this.buttonPressLength = 300; | |
// creates canvases and buttons | |
this.prepareElements(); | |
// attaches event listeners | |
this.prepareEventListeners(); | |
// activate awesome mode | |
this.element.className = 'awesome'; | |
// text content | |
this.lines = ['DATA:IMAGE/PNG;BASE64,']; | |
this.queue = []; | |
this.nextLetterAppend = 3000; | |
// blinking text cursor | |
this.cursorVisible = true; | |
this.nextCursorChange = 200; | |
this.cursorInterval = 300; | |
// base rendering settings and font properties | |
this.ctx.font = 'bold 11px sans-serif'; | |
this.ctx.fillStyle = '#b37b4e'; | |
this.baseY = 33; | |
this.baseX = 25; | |
this.scrollY = 0; | |
this.lineHeight = 14; | |
this.scrollSpeed = 30; | |
this.maxWidth = 170; | |
this.maxLines = 2; | |
// words | |
this.nextWord = 15000; | |
this.wordInterval = 15000; | |
this.words = ['piffle', 'css/hat', 'enigma', 'by/marek', 'this/is/not/random', 'lol', 'omg', 'base64', ' lol /lag ', 'wtf', 'dafuq?', 'jan/palounek', 'jan/palounek', 'jan/palounek', 'jan/palounek', 'petr/brzek', 'si/velky/panko']; | |
this.wordSeparator = '/'; | |
}; | |
/** | |
* Creates elements for Enigma. Canvases for display and reflection and buttons. | |
* | |
* @method prepareElements | |
*/ | |
Enigma.prototype.prepareElements = function () { | |
// buttons | |
this.buttonsContainer = document.createElement('div'); | |
this.buttons = {}; | |
for (var i = 0, ii = this.letters.length; i < ii; ++i) { | |
this.buttons[this.letters[i]] = document.createElement('span'); | |
this.buttons[this.letters[i]].className = this.letters[i].toLowerCase(); | |
this.buttons[this.letters[i]].innerHTML = '<b></b>'; | |
this.buttonsContainer.appendChild(this.buttons[this.letters[i]]); | |
} | |
this.element.appendChild(this.buttonsContainer); | |
// main display canvas | |
this.canvas = document.createElement('canvas'); | |
this.canvas.width = 218; | |
this.canvas.height = 58; | |
this.canvas.className = 'display'; | |
this.ctx = this.canvas.getContext('2d'); | |
this.element.appendChild(this.canvas); | |
// reflection canvas | |
this.reflection = document.createElement('canvas'); | |
this.reflection.width = 218; | |
this.reflection.height = 58; | |
this.reflection.className = 'reflection'; | |
this.reflectionCtx = this.reflection.getContext('2d'); | |
this.element.appendChild(this.reflection); | |
// blinking LED | |
var led = document.createElement('span'); | |
led.className = 'led'; | |
this.element.appendChild(led); | |
}; | |
/** | |
* Attaches event listeners. | |
* | |
* @method prepareEventListeners | |
*/ | |
Enigma.prototype.prepareEventListeners = function () { | |
var that = this; | |
window.addEventListener('scroll', function () { | |
if (window.scrollY > 250 && that.playing) { | |
that.stop(); | |
} | |
if (window.scrollY < 250 && !that.playing) { | |
that.start(); | |
} | |
}, false); | |
window.addEventListener('resize', function () { | |
if (window.innerWidth < 800 && that.playing) { | |
that.stop(); | |
} | |
if (window.innerWidth > 799 && !that.playing) { | |
that.start(); | |
} | |
}, false); | |
document.addEventListener('keydown', function (e) { | |
var letter = String.fromCharCode(e.which); | |
if (that.buttons[letter]) { | |
that.buttonDown(letter); | |
that.appendLetter(letter); | |
} else if (e.which === 32) { | |
that.appendLetter('/'); | |
} else if (e.which === 13) { | |
if (that.lines[that.lines.length - 1].replace('_', '') !== '') { | |
that.lines.push(''); | |
} | |
} | |
this.wait = 5000; | |
}, false); | |
document.addEventListener('keyup', function (e) { | |
var letter = String.fromCharCode(e.which); | |
if (that.buttons[letter]) { | |
that.buttonUp(letter); | |
} | |
}, false); | |
this.element.addEventListener('mouseover', function () { | |
that.writing = false; | |
}, false); | |
this.element.addEventListener('mouseout', function () { | |
that.writing = true; | |
}, false); | |
this.element.addEventListener('mousedown', function (e) { | |
var btn = e.target; | |
if (btn.tagName === 'B') { | |
btn = btn.parentElement; | |
} | |
if (btn.tagName === 'SPAN') { | |
that.appendLetter(btn.className.substr(0, 1).toUpperCase()); | |
} | |
}, false); | |
window.addEventListener('blur', function () { | |
that.stop(); | |
}, false); | |
window.addEventListener('focus', function () { | |
that.start(); | |
}, false); | |
}; | |
/** | |
* Starts function and rendering loop. | |
* | |
* @method start | |
*/ | |
Enigma.prototype.start = function () { | |
this.playing = true; | |
this.loop(); | |
}; | |
/** | |
* Stops function and rendering loop. | |
* | |
* @method stop | |
*/ | |
Enigma.prototype.stop = function () { | |
this.playing = false; | |
}; | |
/** | |
* Function and rendering loop. | |
* | |
* @method loop | |
* @param {Integer} time Current time | |
*/ | |
Enigma.prototype.loop = function (time) { | |
if (this.playing) { | |
var that = this; | |
requestAnimationFrame(function (time) { | |
that.loop(time); | |
}); | |
var delta = time ? time - this.time : 0; | |
this.time = time; | |
this.update(delta); | |
this.render(); | |
} | |
}; | |
/** | |
* Computes if given line will fit on our display line. | |
* | |
* @method willFit | |
* @param {String} line Line of text | |
* @return {Bool} Returns true when line fits | |
*/ | |
Enigma.prototype.willFit = function (line) { | |
var dimensions = this.ctx.measureText(line); | |
return dimensions.width < this.maxWidth; | |
}; | |
/** | |
* Appends a letter on display. | |
* | |
* @method appendLetter | |
* @param {String} letter A letter | |
*/ | |
Enigma.prototype.appendLetter = function (letter) { | |
var lastLine = this.lines[this.lines.length - 1]; | |
if (this.willFit(lastLine + letter)) { | |
this.lines[this.lines.length - 1] += letter; | |
} else { | |
this.lines.push(letter); | |
} | |
}; | |
/** | |
* Pushes a letter and automaticaly lifts it up | |
* | |
* @method pushButton | |
* @param {String} letter A letter of button | |
*/ | |
Enigma.prototype.pushButton = function (letter) { | |
this.buttonDown(letter); | |
var that = this; | |
setTimeout(function () { | |
that.buttonUp(letter); | |
}, this.buttonPressLength); | |
}; | |
/** | |
* Pushes the button down | |
* | |
* @method buttonDown | |
* @param {String} letter A letter of button | |
*/ | |
Enigma.prototype.buttonDown = function (letter) { | |
var btn = this.buttons[letter]; | |
if (btn) { | |
btn.className += ' down'; | |
if (!this.writing) { | |
this.element.className += ' pressed'; | |
} | |
} | |
}; | |
/** | |
* Lifts the button up | |
* | |
* @method buttonDown | |
* @param {String} letter A letter of button | |
*/ | |
Enigma.prototype.buttonUp = function (letter) { | |
var btn = this.buttons[letter]; | |
if (btn) { | |
btn.className = btn.className.replace(/ down/g, ''); | |
this.element.className = this.element.className.replace(/ pressed/g, ''); | |
} | |
}; | |
/** | |
* Logical part of animation loop. Computes lines of text, may append letter. | |
* | |
* @method update | |
* @param {Integer} delta Delta between this time and last time loop was called in miliseconds | |
*/ | |
Enigma.prototype.update = function (delta) { | |
// first run | |
delta = delta || 0; | |
// text cursor cleanup | |
for (var i = 0, ii = this.lines.length; i < ii; ++i) { | |
this.lines[i] = this.lines[i].replace('_', ''); | |
} | |
// letter appending | |
if (this.writing && !this.wait) { | |
this.nextLetterAppend -= delta; | |
if (this.nextLetterAppend < 0) { | |
this.nextLetterAppend = 100 + Math.random() * 300; | |
var nextLetter = this.queue.length ? this.queue.shift() : this.letters[Math.floor(this.letters.length * Math.random())]; | |
this.appendLetter(nextLetter); | |
this.pushButton(nextLetter); | |
} | |
} | |
// waiting | |
if (this.wait) { | |
this.wait -= delta; | |
if (this.wait <= 0) { | |
this.wait = false; | |
} | |
} | |
// blinking text cursor | |
this.nextCursorChange -= delta; | |
if (this.nextCursorChange < 0) { | |
this.nextCursorChange = this.cursorInterval; | |
this.cursorVisible = !this.cursorVisible; | |
} | |
if (this.cursorVisible) { | |
this.appendLetter('_'); | |
} | |
// scrolling | |
if (this.lines.length > this.maxLines) { | |
this.scrollY += Math.ceil((delta / 1000) * this.scrollSpeed); | |
if (this.scrollY >= this.lineHeight) { | |
this.lines.shift(); | |
this.scrollY = 0; | |
} | |
} | |
// random words | |
if (this.writing) { | |
this.nextWord -= delta; | |
} | |
if (this.nextWord < 0 && this.writing) { | |
this.nextWord = this.wordInterval + 5 * Math.random(); | |
if (!this.queue.length) { | |
this.queue = this.words[Math.floor(this.words.length * Math.random())].toUpperCase().split(''); | |
this.queue.unshift(this.wordSeparator); | |
this.queue.push(this.wordSeparator); | |
} | |
} | |
// reflection | |
this.reflectionCtx.putImageData(this.ctx.getImageData(0, 0, this.canvas.width, this.canvas.height), 0, 0); | |
}; | |
/** | |
* Rendering part of animation loop. | |
* | |
* @method render | |
*/ | |
Enigma.prototype.render = function () { | |
var c = this.ctx; | |
c.clearRect(0, 0, this.canvas.width, this.canvas.height); | |
for (var i = 0, ii = this.lines.length; i < ii; ++i) { | |
// animate first line opacity when scrolling | |
if (i === 0 && this.scrollY !== 0) { | |
c.globalAlpha = 1 - this.scrollY / this.lineHeight; | |
} else { | |
c.globalAlpha = 1; | |
} | |
// render line | |
c.fillText(this.lines[i], this.baseX, this.baseY - this.scrollY + i * this.lineHeight); | |
} | |
}; |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// code uses LESSHat mixin library | |
// http://lesshat.com/ | |
#enigma { | |
display: block; | |
.size (467px, 401px); | |
background-repeat: no-repeat; | |
background-image: url(http://getenigma64.com/static/img/enigma.png); | |
position: absolute; | |
top: 30px; | |
left: -100px; | |
.user-select (none); | |
canvas { | |
display: none; | |
} | |
&.awesome { | |
background-image: url(http://getenigma64.com/static/img/enigma-blank.png); | |
.perspective (1000px); | |
canvas { | |
display: block; | |
position: absolute; | |
} | |
.display { | |
left: 100px; | |
top: 48px; | |
.transform (~'rotateX(45deg) rotateZ(-16deg)'); | |
} | |
.reflection { | |
left: 92px; | |
top: 15px; | |
opacity: .8; | |
.transform (~'rotateX(290deg) rotateY(189deg) rotateZ(181deg)'); | |
-webkit-filter: blur(1.66px); | |
-moz-filter: blur(1.66px); | |
-ms-filter: blur(1.66px); | |
-o-filter: blur(1.66px); | |
filter: blur(1.66px); | |
} | |
div { | |
display: block; | |
position: relative; | |
z-index: 10; | |
span { | |
display: block; | |
position: absolute; | |
background-image: url(http://getenigma64.com/static/img/buttons.png); | |
background-repeat: no-repeat; | |
.transition (~'.1s ease'); | |
.transform (~'translate3d(0, 0, 0)'); | |
b { | |
content: ''; | |
display: block; | |
width: 28px; | |
height: 28px; | |
position: absolute; | |
top: -3px; | |
left: -3px; | |
cursor: pointer; | |
} | |
&:hover { | |
.transform (~'translate3d(0, 2px, 0)'); | |
} | |
&.down, &:active { | |
.transform (~'translate3d(0, 6px, 0)'); | |
} | |
} | |
.letter (@x, @y, @w, @h) { | |
.size (@w, @h); | |
top: @y; | |
left: @x; | |
background-position: -@x -@y; | |
} | |
.a { | |
.letter (144px, 148px, 22px, 15px); | |
} | |
.b { | |
.letter (289px, 148px, 23px, 14px); | |
} | |
.c { | |
.letter (234px, 160px, 22px, 15px); | |
} | |
.d { | |
.letter (202px, 136px, 22px, 14px); | |
} | |
.e { | |
.letter (175px, 113px, 21px, 14px); | |
} | |
.f { | |
.letter (230px, 131px, 22px, 14px); | |
} | |
.g { | |
.letter (257px, 125px, 21px, 14px); | |
} | |
.h { | |
.letter (283px, 120px, 21px, 13px); | |
} | |
.i { | |
.letter (304px, 90px, 20px, 11px); | |
} | |
.j { | |
.letter (309px, 115px, 21px, 13px); | |
} | |
.k { | |
.letter (334px, 110px, 20px, 13px); | |
} | |
.l { | |
.letter (367px, 132px, 22px, 13px); | |
} | |
.m { | |
.letter (342px, 137px, 22px, 14px); | |
} | |
.n { | |
.letter (316px, 142px, 22px, 14px); | |
} | |
.o { | |
.letter (328px, 85px, 20px, 12px); | |
} | |
.p { | |
.letter (143px, 179px, 24px, 17px); | |
} | |
.q { | |
.letter (118px, 124px, 22px, 13px); | |
} | |
.r { | |
.letter (203px, 108px, 20px, 13px); | |
} | |
.s { | |
.letter (173px, 142px, 23px, 15px); | |
} | |
.t { | |
.letter (229px, 104px, 21px, 12px); | |
} | |
.u { | |
.letter (280px, 94px, 20px, 12px); | |
} | |
.v { | |
.letter (262px, 154px, 22px, 15px); | |
} | |
.w { | |
.letter (147px, 119px, 22px, 13px); | |
} | |
.x { | |
.letter (205px, 166px, 22px, 16px); | |
} | |
.y { | |
.letter (175px, 172px, 23px, 17px); | |
} | |
.z { | |
.letter (255px, 99px, 20px, 12px); | |
} | |
} | |
.led { | |
display: block; | |
.size (125px, 140px); | |
background-image: url(http://getenigma64.com/static/img/led.png); | |
position: absolute; | |
top: 111px; | |
left: 63px; | |
.animation (~'led 2s ease infinite'); | |
.transition (~'.3s ease'); | |
.transform (~'translate3d(0, 0, 0)'); | |
} | |
&:hover { | |
.led { | |
.opacity (1); | |
.animation (~'blink 3s ease infinite'); | |
} | |
} | |
&.pressed { | |
.led { | |
.opacity (0); | |
.animation (none); | |
.transition (~'.1s ease'); | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment