<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Teleprompter</title>
<style type="text/css">
body {
font-family: Verdana, Helvetica, Arial, sans-serif;
height: 100vh; /* 100vh = 100% of viewport height */
width: 100vw; /* 100vw = 100% of viewport width */
max-width: 95vw;
margin: auto; /* centering */
background-color: rgba(0, 0, 0, 100%); /* 0% = transparent */
}
.input_header {
position: relative;
top: 0px;
background-color: rgba(224, 224, 224, 100%);
z-index: 100; /* above */
}
@keyframes scroll {
from {
transform: translateY(100vh); /* Y = height of viewport */
}
to {
transform: translateY(-105%); /* Y = -height of content and 5% more */
}
}
@keyframes scroll_mirrored {
from {
transform: translateY(100vh) scaleX(-1); /* Y = height of viewport */
}
to {
transform: translateY(-105%) scaleX(-1); /* Y = -height of content and 5% more */
}
}
.content {
animation-name: scroll;
animation-fill-mode: forwards;
animation-timing-function: linear;
max-width: 70%; /* narrow column */
margin: auto; /* centering */
line-height: 1.5; /* line spacing */
color: rgba(224, 224, 224, 100%);
background-color: rgba(0, 0, 0, 0%); /* 0% = transparent */
overflow: hidden;
z-index: 19; /* below */
}
</style>
</head>
<body>
<div class="input_header">
<input type="file" id="input">
speed:<input type="number" id="scroll_speed" style="width: 6em;" value="60">px/s
size:<input type="number" id="font_size" style="width: 6em;" value="40">px
<input type="checkbox" id="is_html">
<label for="is_html">html markup</label>
<input type="checkbox" id="is_mirrored">
<label for="is_mirrored">mirror</label>
<hr>
</div>
<!-- Text from file -->
<div id="prompterDIV">
<p><p style="color: white;">"x" to play/pause; "r" to reset</p></p>
</div>
<script type="text/javascript">
var content_class;
const element = document.getElementById("prompterDIV");
const size = document.getElementById("font_size");
const speed = document.getElementById("scroll_speed");
const asHTML = document.getElementById("is_html");
const mirror = document.getElementById("is_mirrored");
mirror.addEventListener("change", mirror_it, false);
function mirror_it() {
if (!content_class) {
return;
}
if (mirror.checked) {
content_class.style.animationName = 'scroll_mirrored';
} else {
content_class.style.animationName = 'scroll';
}
}
size.addEventListener("change", adjustSize, false);
speed.addEventListener("change", adjustSpeed, false);
function adjustSize() {
if (!content_class) {
return;
}
content_class.style.fontSize = size.value.toString() + 'px';
}
function adjustSpeed() {
if (!content_class) {
return;
}
var dur = content_class.clientHeight;
if (speed.value > 0) {
dur = dur / speed.value;
}
content_class.style.animationDuration = dur.toString() + 's';
}
function animStart() {
content_class.style.animationPlayState = 'running';
}
function animPause() {
content_class.style.animationPlayState = 'paused';
}
// Get local file
const reader = new FileReader();
const selectedFile = document.getElementById("input");
selectedFile.addEventListener("change", handleFiles, false);
function handleFiles() {
if (selectedFile.files.length) {
// Read the file now!
reader.readAsArrayBuffer(selectedFile.files[0]);
}
}
reader.addEventListener("loadend", filesReady, false);
function filesReady() {
// Load ended - fill the content
addContent();
}
function addContent(pause) {
element.classList.add("content");
content_class = document.querySelector(".content");
if (pause) {
animPause();
}
const s = new TextDecoder().decode(reader.result);
if (asHTML.checked) {
content_class.innerHTML = s;
} else {
content_class.innerText = s;
}
adjustSize(); // Here clientHeight may change
adjustSpeed();
mirror_it();
}
function removeContent() {
animPause();
content_class.style.animationName = '';
content_class.innerText = '';
content_class.innerHTML = '';
element.classList.remove("content");
}
// Listen for checkbox event and decode buffer again
asHTML.addEventListener("change", filesReady, false);
// Listen for keypress event
document.addEventListener('keypress', controlPrompter);
// Control the prompter scrolling with "x" (play/pause) or "r" (reset)
function controlPrompter (event) {
if (event.code && event.key !== 'x' && event.key !== 'r') {
return;
} else if (event.key === 'r') {
// Reset
removeContent();
return;
}
if (content_class) {
if (content_class.style.animationPlayState === 'paused') {
if (content_class.style.animationName === '') {
addContent(); // After reset
}
animStart();
} else {
animPause();
}
}
}
</script>
</body>
</html>