DEV Community

mich0w0h
mich0w0h

Posted on

React: Blink Character's Eyes

This is a continuation of this post: Web Animation: Twitch ears twice in a row - DEV Community

What I want to achieve

  • blink the character's eyes in a web app made of React + Typescript
  • make it flexible to change the number of blink times

Blink once

Firstly, implement the simplest blink motion, blinking once.

Write a state boolean valuable which manages the blinking.

const [blinking, setBlinking] = useState(false);
Enter fullscreen mode Exit fullscreen mode

And the eyes' elements are here;

<div className="character-eyes">
<img
    src={blinking ? "/src/assets/character-eye-blink.png" : "/src/assets/character-eye-default.png"}
    alt="Character Left eye"
    className={blinking ? "character-eye-blink" : "character-eye-default"} 
/>
<img
    src={blinking ? "/src/assets/character-eye-blink.png" : "/src/assets/character-eye-default.png"}
    alt="Character Right eye"
    className={blinking ? "character-eye-blink" : "character-eye-default"} 
/>
</div>
Enter fullscreen mode Exit fullscreen mode

Now just toggling the blinking value allows the character's eyes to blink.

const delay = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));

const blinkEyes = async (): Promise<void> => {
    const duration: number = 130;

    setBlinking(true);
    await delay(duration);
    setBlinking(false);
}

Enter fullscreen mode Exit fullscreen mode

Blink several times

It's easy to increase the number of blink times

const blinkEyes = async (blinkCount: number): Promise<void> => {
    const duration: number = 130;
    for (let i = 0; i < blinkCount; i++) {
        setBlinking(true);
        await delay(duration);
        setBlinking(false);
        await delay(duration);
    }
}
Enter fullscreen mode Exit fullscreen mode

And by the blinkEyes() function, I can make more complex blinking motions like "blink → delay → blink two times → delay → blink".

const interval = 1500;
await blinkEyes(1); // One blink
await delay(interval); // Delay after one blink
await blinkEyes(2); // Two blinks
await delay(interval); // Delay after two blinks
await blinkEyes(1); // One final blink
Enter fullscreen mode Exit fullscreen mode

Blink several times endlessly

At this point, create an async function that endlessly triggers the series of blinks at certain intervals.

Additionally, declare the timeoutId to use the clearance of the timeout process.

let timeoutId: number; // for clearTimeout
const blinkWithTimer = async (timer: number): Promise<void> => {
    const interval = 1500;
    await blinkEyes(1); // One blink
    await delay(interval); // Delay after one blink
    await blinkEyes(2); // Two blinks
    await delay(interval); // Delay after two blinks
    await blinkEyes(1); // One final blink
    timeoutId = setTimeout(() => {
        blinkWithTimer(timer);
    }, timer);
}
Enter fullscreen mode Exit fullscreen mode

Then write it in the useEffect to call when the component is mounted. And call clearTimeout() with the argument timeoutId declared above to clear the timeout process when the component is unmounted.

useEffect(() => {
    blinkWithTimer(3000);

    return () => {
        clearTimeout(timeoutId); 
    };
}, []);
Enter fullscreen mode Exit fullscreen mode

For a bit of a twist, I use setTimeout() to make it flexible to change the duration and the frequency of blinks rather than using setInterval() like this;

useEffect(() => {
    const blinkInterval = setInterval(() => {
        blinkEyes();
    }, 2500);

    return () => clearInterval(blinkInterval);
}, []);

Enter fullscreen mode Exit fullscreen mode

In the codes above, I should adjust the second argument value of setInterval() every time I change the duration and frequency of blinking, otherwise blinkEyes() can be called even when the previous blinking hasn't ended yet.

Here you go!

Notes: the ear motions are caused by another component. For more information, check this post; Web Animation: Twitch ears twice in a row - DEV Community
Blinking animation

What's next

There seems to be an issue that the useEffect was called without clearing the timeout process after I edited the codes running the vite server and it automatically refreshed the page.

Top comments (0)