React: Blink Character's Eyes
Tags: react, javascript, beginners, webdev
Published: 2024-01-08 04:46:20

This is a continuation of this post: [Web Animation: Twitch ears twice in a row - DEV Community](https://dev.to/mich0w0h/web-animation-twitch-ears-twice-in-a-row-50bb)

## 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.
```tsx
const [blinking, setBlinking] = useState(false);
```

And the eyes' elements are here;
```tsx
<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>
```

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

```tsx
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);
}

```

## Blink several times
It's easy to increase the number of blink times
```tsx
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);
    }
}
```

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

```tsx
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
``` 

## 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.

```tsx
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);
}
```

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.

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

    return () => {
        clearTimeout(timeoutId); 
    };
}, []);
```

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;

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

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

```

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](https://dev.to/mich0w0h/web-animation-twitch-ears-twice-in-a-row-50bb)
![Blinking animation](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rpiz18gjbdmnnt91neea.gif)

## 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.