type
This commit is contained in:
parent
2b2a74147c
commit
2ac58a3d14
1 changed files with 104 additions and 0 deletions
104
type.html
Normal file
104
type.html
Normal file
|
|
@ -0,0 +1,104 @@
|
||||||
|
<!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>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue