104 lines
No EOL
2.1 KiB
HTML
104 lines
No EOL
2.1 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<title>Typewriter Upward Swing</title>
|
|
<style>
|
|
body {
|
|
font-family: monospace;
|
|
padding: 40px;
|
|
font-size: 2rem;
|
|
overflow-x: hidden;
|
|
}
|
|
|
|
#typewriter {
|
|
position: relative;
|
|
display: inline-block;
|
|
white-space: pre;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
#text {
|
|
position: relative;
|
|
}
|
|
|
|
#typebar {
|
|
position: absolute;
|
|
width: 40px;
|
|
height: 800px; /* long typebar */
|
|
transform-origin: bottom center;
|
|
opacity: 0;
|
|
transition:
|
|
transform 0.12s ease-out,
|
|
opacity 0.15s ease-out;
|
|
pointer-events: none;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<div id="typewriter">
|
|
<span id="text"></span>
|
|
|
|
<svg id="typebar" viewBox="0 0 20 500">
|
|
<rect x="7" y="0" width="6" height="500" fill="#333"/>
|
|
</svg>
|
|
</div>
|
|
|
|
<script>
|
|
const fullText = "Typed text";
|
|
const textEl = document.getElementById("text");
|
|
const typebar = document.getElementById("typebar");
|
|
const container = document.getElementById("typewriter");
|
|
|
|
let i = 0;
|
|
|
|
function nextChar() {
|
|
if (i >= fullText.length) return;
|
|
|
|
|
|
|
|
// Measure character position
|
|
const range = document.createRange();
|
|
range.setStart(textEl.firstChild, i);
|
|
range.setEnd(textEl.firstChild, i + 1);
|
|
const charRect = range.getBoundingClientRect();
|
|
const containerRect = container.getBoundingClientRect();
|
|
|
|
// Random downward rest angle between 20° and 60°
|
|
const restAngle = 20 + Math.random() * 40;
|
|
|
|
// Position typebar's pivot slightly below the text
|
|
typebar.style.left = (charRect.left - containerRect.left) + "px";
|
|
typebar.style.top = (charRect.top - containerRect.top + 20) + "px";
|
|
|
|
// Set initial REST POSITION
|
|
typebar.style.transform = `rotate(${restAngle}deg)`;
|
|
|
|
// FADE IN before swing
|
|
|
|
|
|
// STRIKE
|
|
requestAnimationFrame(() => {
|
|
typebar.style.transform = `rotate(0deg)`;
|
|
typebar.style.opacity = 1;
|
|
|
|
// Add next character
|
|
textEl.textContent += fullText[i];
|
|
|
|
// Retract + FADE OUT
|
|
setTimeout(() => {
|
|
typebar.style.transform = `rotate(${restAngle}deg)`;
|
|
typebar.style.opacity = 0;
|
|
}, 120);
|
|
});
|
|
|
|
i++;
|
|
setTimeout(nextChar, 300);
|
|
}
|
|
|
|
nextChar();
|
|
</script>
|
|
|
|
</body>
|
|
</html> |