169 lines
4.5 KiB
JavaScript
169 lines
4.5 KiB
JavaScript
window.requestAnimFrame = (function(){
|
|
return window.requestAnimationFrame ||
|
|
window.webkitRequestAnimationFrame ||
|
|
window.mozRequestAnimationFrame ||
|
|
window.oRequestAnimationFrame ||
|
|
window.msRequestAnimationFrame ||
|
|
function(callback){
|
|
window.setTimeout(callback, 1000 / 8);
|
|
};
|
|
})();
|
|
|
|
(function() {
|
|
'use strict';
|
|
|
|
// Initial setup
|
|
function $(x) {return document.getElementById(x);}
|
|
|
|
let $container = $("banner-textarea");
|
|
let $message = $("banner-msg");
|
|
let $animate = $("banner-msg-animate");
|
|
let $paragraph = null;
|
|
|
|
const asciiEscMap = {
|
|
'&': '&',
|
|
'<': '<',
|
|
'>': '>',
|
|
'"': '"',
|
|
"'": ''',
|
|
'`': '`'
|
|
};
|
|
|
|
function needsAsciiEsc(str) {
|
|
for (let i of str) {
|
|
if (asciiEscMap[i]) { return true; }
|
|
}
|
|
return false;
|
|
}
|
|
function isAsciiString(str) {
|
|
for (let i of str) {
|
|
if (i.codePointAt(0) > 127) { return false; }
|
|
}
|
|
return true;
|
|
}
|
|
function maybeEscpDec(ch) {
|
|
const cp = ch.codePointAt(0);
|
|
return cp > 127 ? '&#' + cp + ';' : ch;
|
|
}
|
|
function escpAscii(str) {
|
|
return [...str].map(x => asciiEscMap[x] || x).join("");
|
|
}
|
|
|
|
function escp(text) {
|
|
if (needsAsciiEsc(text)) {
|
|
text = escpAscii(text);
|
|
}
|
|
return isAsciiString(text) ? text : [...text].map(maybeEscpDec).join("");
|
|
}
|
|
|
|
// Messages setup
|
|
|
|
/* MESSAGES be like:
|
|
* [ {"delay": 0, "text": "message 1"},
|
|
* {"delay": 1200, "text": "message 2"},
|
|
* {"delay": 2200, "text": "message 4"},
|
|
* {"delay": 3600, "text": "message 5"},
|
|
* {"delay": 5200, "text": "message 6"} ]
|
|
*/
|
|
const MESSAGES = JSON.parse($animate.getAttribute('data-msg'));
|
|
|
|
function scramble($element, text, options) {
|
|
const defaults = {
|
|
probability: 0.25,
|
|
glitches: "-%¥¶!\"❏_△§*¢ ¿",
|
|
blank: "",
|
|
duration: text.length * 80,
|
|
delay: 0.0
|
|
};
|
|
const settings = {...defaults, ...options};
|
|
|
|
function shuffle() {return (Math.random() < 0.5) ? 1 : -1;}
|
|
function wrap(text, classes) {return "<span class=\"" + classes + "\">" + text + "</span>";}
|
|
|
|
const glitchCharacters = [...settings.glitches].map(escp);
|
|
const glitchLength = glitchCharacters.length;
|
|
const glitchProbability = settings.probability;
|
|
const glitches = glitchCharacters.map(x => wrap(x, "pop"));
|
|
|
|
const ghostCharacters = [...$element.innerText].map(escp);
|
|
const ghostLength = ghostCharacters.length;
|
|
const ghosts = ghostCharacters.map(x => wrap(x, "ghost"));
|
|
const textCharacters = [...text].map(escp);
|
|
const textLength = textCharacters.length;
|
|
const order = Array.from(Array(textLength).keys()).sort(shuffle);
|
|
|
|
let output = [];
|
|
for (let i = 0; i < textLength; ++i) {
|
|
const index = Math.floor(Math.random() * (glitchLength - 1));
|
|
const glitchCharacter = glitches[index];
|
|
const ghostCharacter = ghosts[i] || settings.blank;
|
|
const character = Math.random() < glitchProbability ? glitchCharacter : ghostCharacter;
|
|
output.push(character);
|
|
}
|
|
|
|
// Animation
|
|
const duration = settings.duration;
|
|
const ease = settings.ease;
|
|
|
|
let start = null;
|
|
const TRESH = 1000 / 8; // 8FPS, more than enough
|
|
|
|
function easeInOutQuad(t) {
|
|
if (t < 0.5)
|
|
return 2 * t * t;
|
|
|
|
return Math.min((4 - 2 * t) * t - 1, 1);
|
|
}
|
|
function refresh() {
|
|
const now = Date.now();
|
|
if (!start) { start = now; }
|
|
|
|
const elapsed = now - start;
|
|
if (elapsed < TRESH) {
|
|
window.requestAnimFrame(refresh);
|
|
return;
|
|
}
|
|
if (elapsed > duration) {
|
|
$element.innerHTML = text;
|
|
return;
|
|
}
|
|
|
|
const interp = easeInOutQuad(elapsed / duration);
|
|
const progress = Math.floor(interp * (textLength - 1));
|
|
for (let i = 0; i < progress; ++i) {
|
|
const index = order[i];
|
|
output[index] = textCharacters[index];
|
|
}
|
|
|
|
$element.innerHTML = output.join('');
|
|
window.requestAnimFrame(refresh);
|
|
}
|
|
|
|
setTimeout(refresh, settings.delay);
|
|
}
|
|
|
|
function animate() {
|
|
for (let i = 0; i < MESSAGES.length; ++i) {
|
|
let data = MESSAGES[i];
|
|
let element = $paragraph.item(i);
|
|
|
|
element.innerText = '';
|
|
let options = { delay: data.delay };
|
|
|
|
scramble(element, data.text, options);
|
|
}
|
|
}
|
|
|
|
function initialize() {
|
|
for (let _ in MESSAGES) {
|
|
let elem = document.createElement("p");
|
|
elem.className = "banner-msg-line";
|
|
$message.appendChild(elem);
|
|
}
|
|
$paragraph = $container.getElementsByTagName("p");
|
|
animate();
|
|
}
|
|
|
|
initialize();
|
|
}).call(this);
|