Vertical Scroll

PaulCroft

New Member
Hi All

I have created a vertical scroll and really struggling to get it to work. It is a poem that someone wrote and I am trying to get it to scroll up the screen, but I only get a few lines of the poem and then it repeats from the beginning without going through the whole text. I am also struggling to get the text size to be large enough to read AND to center and stay within the box that I have created. I have worked with the transform and the filter for the scrolling and have changed the font size. But it all seems to change things but not in the way I want it to.

I am wondering if there is a tutorial anywhere on creating a verticle scroll from beginning to end, that will explain what I am doing wrong and how to correct it. This is a project for the family of someone who recently passed away and I would really like it to be almost perfect. But OBS is giving me fits.

TIA
 

Suslik V

Active Member
You need to prepare assets before launching OBS. Because OBS is tool for real-time encoding and streaming. What you trying to do is task for the video editor program (not for OBS). In video editors it is much easier to make animations.

Simple vertically scrolling text can be made in the browser. For example, many solution exist online with names similar to "Teleprompter".
You can make from the source code your own teleprompter3_offline.html page locally and open it in the browser.
HTML:
<!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>

Edit: and sometimes plugins also work in OBS (like Move Source filter):
 
Last edited:
Top