In this post, we are going to present the process of creating the typewriter effect.
The following codepen illustrates the final result.
Our goal
The functionality we want to achieve is a subset of another library (with more options and methods) called text-typing, that I have developed. It is in beta version yet, but available as npm package for anyone who would like to experiment with some of its capabilities.
You can find text-typing here:
kapantzak / text-typing
Simple typewriting effect
text-typing
A tool for creating typewriter effect, with a simple, promise based api.
Installation
Install npm package
npm
npm install text-typing
yarn
yarn add text-typing
And then import the package
import {textTyping} from "text-typing";
Usage
All you need to do is to initialize the tool, passing a reference to an existing DOM element, and start typing!
HTML
<h1 id="myHeading"></h1>
JS
(async () => {
const txt = textTyping(document.getElementById("myHeading"));
await txt.typeText("Hello");
})();
Chaining methods
You can call multiple methods on the same instance, either by using await
(inside an async
function), or by using then
after a method call
await
(async () => {
const txt = textTyping(elem);
await txt.typeText("Hello");
await txt.backspace(2);
})();
then
(() => {
const txt = textTyping(elem);
txt.typeText("Hello").then(resp => {
resp.backspace(2);
});
})();
Options
speed
The typing speed that is going to be used by called methods, if no specific speed is provided to the specific method.
…
For this post, we are going to develop a function that exposes a small api (one method 😋) which then, we can call in order to apply the typewriter effect. This function will take two parameters:
- The text to be typed
- An array of of two numbers that is going to be used as the speed range of the typing process
The call is going to be like that:
const textHolder = document.getElementById("myTextHolder");
const speedRange = [100, 600];
const txt = textTyping(textHolder, speedRange);
txt.typeText("Hello there!");
The markup
For start, we need a HTML element to use it as our text holder. Lets use a <div>
element with id myTextHolder
, nested in another div element that is going to be used as the wrapper element.
Then, we apply some css to verticaly allign our text (not necessary).
The JS part
We can now begin writing our js functions, starting with the main function that we are goint to call in order to apply the typewriter effect.
We declare the textTyping
function that takes two arguments
-
elem
: the HTML element to hold the text -
speedRange
: the speed range that is going to be used
Blinking cursor
We are developing a typewriting effect, so, we need to display a blinking cursor. For that, we create a <span>
element and we apply some css animations in order to achieve the blinking effect.
We create the cursor as soon as we call the textTyping
function:
and we apply the respective css:
API
Now we are going to expose our simple api, which consists of one method, the typeText
method! To achieve that, we return an object that has a property named typeText
and a value of an anonymous function that takes one argument, named text
.
In addition, we have added another <span>
element (section
) that serves the role of an inner text holder, in order to separate the cursor from the text.
Typing process
Now we have to implement the typing process. Lets try by spliting the text and getting an array of letters. We can iterate this array and insert each letter inside the section
element one by one.
Furthermore, we need to inject a timeout before each letter injection, based on the speedRange parameter provided to textTyping
function.
Lets declare a function that takes the speedRange
array and returns a random number inside the two numbers contained in the array:
Here is our first (not successful) attempt
What happened? Why are letters mixed up?
The problem is here
The for
loop instantly iterates the letters array and the setTimeout
callbacks start to execute at a random time from the loop execution end.
Each setTimeout
does not wait for the previous timeout callback to be called, as javascript has a non-blocking runtime. On the contrary, each setTimeout
, instantly pushes a callback to the message queue with a random timeout generated by getSpeed
function.
For more details about asynchronous execution, you can checkout this article about the event loop.
Async generators
In order to solve our problem, we need to find a way to, correctly iterate over a sequence of asynchronous tasks.
Async generators come to our resque!
We are not going to get into more detail about async generators or async iterators. For now, we only need to know that async generators provide us with the ability to generate sequences of asynchronous data, that can be iterated and procude the desired outcome.
Now lets apply them to our function.
First, we need to declare a function that returns a Promise which gets resolved after a certain amount of time, returning the appropriate letter for us.
We are going to call this function inside our async generator as shown in the following gist:
Note the function*
statement, this is the way we declare generators. And because we want an async generator, we prepend the async
keyword (in fact async
is not a keyword itself, instead, the combination async function
is).
All we have to do now is to iterate over the sequence that our async generator produces. We can do that with the use of the for await ... of
statement like this:
We now have the desired result, as shown in the codepen at the begining of the post.
Thanks for reading 🤗 so far! I hope it was interesting enough! Please leave your comments for any feedback or questions!
Resources
- https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for-await...of
- https://javascript.info/async-iterators-generators
- https://jakearchibald.com/2017/async-iterators-and-generators/
Top comments (0)