Custom Code Experiment •
Electric Hot
Source Code View HTML / CSS / JavaScript
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<title>Electric Hot</title>
<style>
@import url('https://fonts.googleapis.com/css2?family=Raleway:wght@900&display=swap');
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
background: #000;
overflow: hidden;
width: 100vw;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
#stage {
position: relative;
width: 100vw;
height: 100px;
filter: url(#mt) blur(0.4px);
}
.mt {
position: absolute;
width: 100%;
text-align: center;
font-family: 'Raleway', sans-serif;
font-size: 80pt;
font-weight: 900;
color: #fff;
will-change: opacity, filter;
transform: translateZ(0);
letter-spacing: -0.02em;
text-rendering: geometricPrecision;
}
@media (max-width: 720px) {
.mt { font-size: 13vw; }
#stage { height: 18vw; }
}
svg {
position: absolute;
width: 0;
height: 0;
visibility: hidden;
pointer-events: none;
}
</style>
</head>
<body>
<div id="stage">
<span id="ma" class="mt"></span>
<span id="mb" class="mt"></span>
</div>
<svg aria-hidden="true">
<defs>
<filter id="mt" x="-20%" y="-40%" width="140%" height="180%">
<feColorMatrix type="matrix"
values="1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 255 -128"/>
</filter>
</defs>
</svg>
<script>
(function () {
const elA = document.getElementById('ma');
const elB = document.getElementById('mb');
const words = [
"It",
"is",
"the",
"body",
"of",
"nothing",
"and",
"electric",
"hot."
];
const HOLD = 1.1;
const TRANSITION = 1.3;
let idx = 0;
let t = 0;
let last = null;
function smoothstep(t) {
t = Math.max(0, Math.min(1, t));
return t * t * (3 - 2 * t);
}
function applyProgress(el, progress, entering) {
const p = entering ? progress : 1 - progress;
const s = smoothstep(p);
const blurVal = 12 * (1 - s);
el.style.opacity = s;
el.style.filter = blurVal < 0.05 ? 'none' : `blur(${blurVal.toFixed(2)}px)`;
}
function show(el, word) {
el.textContent = word;
el.style.opacity = 1;
el.style.filter = 'none';
}
function hide(el) {
el.style.opacity = 0;
el.style.filter = 'blur(12px)';
}
let phase = 'hold';
let current = elA;
let incoming = elB;
show(current, words[0]);
hide(incoming);
incoming.textContent = words[1];
function tick(ts) {
requestAnimationFrame(tick);
if (!last) { last = ts; return; }
const dt = Math.min((ts - last) / 1000, 0.064);
last = ts;
t += dt;
if (phase === 'hold') {
if (t >= HOLD) {
t -= HOLD;
phase = 'transition';
idx = (idx + 1) % words.length;
incoming.textContent = words[idx];
hide(incoming);
}
} else {
const progress = Math.min(t / TRANSITION, 1);
applyProgress(current, progress, false);
applyProgress(incoming, progress, true);
if (progress >= 1) {
const tmp = current;
current = incoming;
incoming = tmp;
show(current, words[idx]);
hide(incoming);
t -= TRANSITION;
phase = 'hold';
}
}
}
requestAnimationFrame(tick);
})();
</script>
</body>
</html>