You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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 = {
'&': '&',
'<': '&lt;',
'>': '&gt;',
'"': '&quot;',
"'": '&#x27;',
'`': '&#x60;'
};
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);