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);
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>
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);
}
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);
}
}
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
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);
}
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);
};
}, []);
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);
}, []);
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
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)