new file: js/art.js new file: js/hdr.js new file: js/hearts.js new file: js/helpers.js new file: js/particles.js new file: js/qlpycon.js new file: js/qlpyconbanner.js new file: js/snek.js new file: js/sparks.js new file: js/stars.js modified: slamp.html modified: style.css
191 lines
5.8 KiB
JavaScript
191 lines
5.8 KiB
JavaScript
(function () {
|
|
var c = document.getElementById('hdr');
|
|
var ctx = c.getContext('2d');
|
|
var W, H, mouse = { x: -999, y: -999 };
|
|
var glitchX = 0, glitchY = 0;
|
|
var glitchXTarget = 0, glitchYTarget = 0;
|
|
var COLS = ['#00ffff', '#ff00ff', '#ffffff'];
|
|
/*const ARTS = [
|
|
{
|
|
lines: [
|
|
''
|
|
]
|
|
},
|
|
]*/
|
|
var validARTS = ARTS.filter(function(a) { return a.lines.length > 0; });
|
|
var ART = validARTS[Math.floor(Math.random() * validARTS.length)].lines;
|
|
|
|
var fontSize, LINE_H, ART_TOP, PAD_BOTTOM;
|
|
|
|
function resize() {
|
|
W = c.width = c.offsetWidth;
|
|
|
|
var maxChars = ART.reduce(function(max, l) { return Math.max(max, l.length); }, 0);
|
|
fontSize = Math.min(16, (W * 0.95) / maxChars * 1.6);
|
|
LINE_H = fontSize * 1;
|
|
ART_TOP = LINE_H * 2;
|
|
PAD_BOTTOM = LINE_H * 2;
|
|
|
|
H = c.height = Math.ceil(ART_TOP + ART.length * LINE_H + PAD_BOTTOM);
|
|
|
|
buildScanlines();
|
|
}
|
|
|
|
var scanlines = document.createElement('canvas');
|
|
function buildScanlines() {
|
|
var headerH = ART_TOP + ART.length * LINE_H + PAD_BOTTOM;
|
|
scanlines.width = 1;
|
|
scanlines.height = headerH;
|
|
var sctx = scanlines.getContext('2d');
|
|
sctx.clearRect(0, 0, 1, headerH);
|
|
for (var sy = 0; sy < headerH; sy += 3) {
|
|
sctx.fillStyle = '#101010';
|
|
sctx.fillRect(0, sy, 1, 1);
|
|
}
|
|
}
|
|
|
|
window.addEventListener('resize', function () { resize(); });
|
|
|
|
window.addEventListener('DOMContentLoaded', function() {
|
|
resize();
|
|
requestAnimationFrame(loop);
|
|
});
|
|
|
|
var moveTimer;
|
|
var glitchTarget = 0;
|
|
|
|
var lastX = 0, lastY = 0;
|
|
document.addEventListener('mousemove', function (e) {
|
|
var nx = e.clientX;
|
|
var ny = e.clientY;
|
|
var dx = nx - lastX;
|
|
var dy = ny - lastY;
|
|
mouse.x = nx;
|
|
mouse.y = ny;
|
|
lastX = nx;
|
|
lastY = ny;
|
|
glitchXTarget = Math.min(1, Math.abs(dx) / 16);
|
|
glitchYTarget = Math.min(1, Math.abs(dy) / 16);
|
|
clearTimeout(moveTimer);
|
|
moveTimer = setTimeout(function () { glitchXTarget = 0; glitchYTarget = 0; }, 100);
|
|
});
|
|
|
|
document.addEventListener('mouseleave', function () {
|
|
mouse.x = -999;
|
|
glitchXTarget = 0;
|
|
glitchYTarget = 0;
|
|
});
|
|
|
|
document.addEventListener('touchmove', function (e) {
|
|
var touch = e.touches[0];
|
|
var nx = touch.clientX;
|
|
var ny = touch.clientY;
|
|
var dx = nx - lastX;
|
|
var dy = ny - lastY;
|
|
mouse.x = nx;
|
|
mouse.y = ny;
|
|
lastX = nx;
|
|
lastY = ny;
|
|
glitchXTarget = Math.min(1, Math.abs(dx) / 16);
|
|
glitchYTarget = Math.min(1, Math.abs(dy) / 16);
|
|
clearTimeout(moveTimer);
|
|
moveTimer = setTimeout(function () { glitchXTarget = 0; glitchYTarget = 0; }, 100);
|
|
}, { passive: true });
|
|
|
|
document.addEventListener('touchend', function () {
|
|
glitchXTarget = 0;
|
|
glitchYTarget = 0;
|
|
});
|
|
|
|
/* ── text ── */
|
|
function drawText(ox, oy, col, alpha) {
|
|
ctx.save();
|
|
ctx.globalAlpha = alpha;
|
|
ctx.fillStyle = col;
|
|
ctx.font = fontSize + 'px deltarune';
|
|
ctx.textAlign = 'center';
|
|
ctx.textBaseline = 'top';
|
|
for (var i = 0; i < ART.length; i++) {
|
|
ctx.fillText(ART[i], W / 2 + ox, ART_TOP + i * LINE_H + oy, W * 0.95);
|
|
}
|
|
ctx.restore();
|
|
}
|
|
|
|
/* ── proximity glow on letters ── */
|
|
function drawGlow() {
|
|
var cx = W / 2;
|
|
var cy = ART_TOP + (ART.length * LINE_H) / 2;
|
|
var dx = mouse.x - cx;
|
|
var dy = mouse.y - cy;
|
|
var dist = Math.sqrt(dx * dx + dy * dy);
|
|
var r = Math.max(0, 1 - dist / 220);
|
|
var idle = (1 - r) * (Math.sin(t * 0.008) * 0.5 + 0.5) * 0.25;
|
|
|
|
if (idle > 0) {
|
|
ctx.save();
|
|
ctx.globalAlpha = idle * 0.15;
|
|
ctx.fillStyle = '#00ffff';
|
|
ctx.font = fontSize + 'px deltarune';
|
|
ctx.textAlign = 'center';
|
|
ctx.textBaseline = 'top';
|
|
for (var d = 3; d <= 9; d += 3) {
|
|
for (var i = 0; i < ART.length; i++) {
|
|
ctx.fillText(ART[i], W / 2 + d, ART_TOP + i * LINE_H, W * 0.95);
|
|
ctx.fillText(ART[i], W / 2 - d, ART_TOP + i * LINE_H, W * 0.95);
|
|
ctx.fillText(ART[i], W / 2, ART_TOP + i * LINE_H + d, W * 0.95);
|
|
ctx.fillText(ART[i], W / 2, ART_TOP + i * LINE_H - d, W * 0.95);
|
|
}
|
|
}
|
|
ctx.restore();
|
|
}
|
|
|
|
if (r <= 0) return;
|
|
ctx.save();
|
|
ctx.globalAlpha = r * 0.3;
|
|
ctx.fillStyle = '#ff00ff';
|
|
ctx.font = fontSize + 'px deltarune';
|
|
ctx.textAlign = 'center';
|
|
ctx.textBaseline = 'top';
|
|
for (var d = 3; d <= 9; d += 3) {
|
|
for (var i = 0; i < ART.length; i++) {
|
|
ctx.fillText(ART[i], W / 2 + d, ART_TOP + i * LINE_H, W * 0.95);
|
|
ctx.fillText(ART[i], W / 2 - d, ART_TOP + i * LINE_H, W * 0.95);
|
|
}
|
|
}
|
|
ctx.restore();
|
|
}
|
|
|
|
/* ── main loop ── */
|
|
var t = 0, lastFrame = 0;
|
|
function loop(ts) {
|
|
requestAnimationFrame(loop);
|
|
if (ts - lastFrame < 42) return;
|
|
lastFrame = ts;
|
|
t++;
|
|
|
|
ctx.clearRect(0, 0, W, H);
|
|
var headerH = ART_TOP + ART.length * LINE_H + PAD_BOTTOM;
|
|
|
|
var splitX = glitchX * 16;
|
|
var splitY = glitchY * 16;
|
|
|
|
drawText(-splitX, -splitY, '#00ffff', 0.7);
|
|
drawText( splitX, splitY, '#ff00ff', 0.7);
|
|
drawText(0, 0, '#ffffff', 0.9);
|
|
|
|
drawGlow();
|
|
|
|
/* scanline overlay */
|
|
var headerH = ART_TOP + ART.length * LINE_H + PAD_BOTTOM;
|
|
ctx.drawImage(scanlines, 0, 0, W, headerH);
|
|
|
|
glitchX += (glitchXTarget - glitchX) * 0.04;
|
|
glitchY += (glitchYTarget - glitchY) * 0.04;
|
|
}
|
|
|
|
window.rerollArt = function() {
|
|
ART = validARTS[Math.floor(Math.random() * validARTS.length)].lines;
|
|
resize();
|
|
};
|
|
})();
|