DEV Community

mich0w0h
mich0w0h

Posted on

Web Animation: Twitch ears twice in a row

This post is a continuation of Web Animation: Create endless twitching ear motion with a certain interval

What I want to achieve

  • twitch a character's ears twice in a row in a web app project made of React + Typescript
  • twitching motion itself has already been done in the previous article

How to implement

Create a Promise to implement a delay.

const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
Enter fullscreen mode Exit fullscreen mode

Then, update the twitchEars function into async one and insert delays between twitches in that function.

const twitchEars = async () => {
    setTwitching(true);

    await delay(twitchDuration);
    setTwitching(false);

    await delay(200); // Delay between the twitches

    setTwitching(true);
    await delay(twitchDuration);

    setTwitching(false);
};
Enter fullscreen mode Exit fullscreen mode

For a bit of a twist, I used async and await instead of just implementing it by the sequence of several setTimeouts.

Now I did it!

Image description

Overall view of the component

The overall view of the component so far is here.

CharacterDisplay.tsx

import { useState, useEffect } from 'react';
import './CharacterDisplay.css';

const CharacterDisplay: React.FC = () => {
    const [twitching, setTwitching] = useState(false);
    const delay = (ms: number) => new Promise((resolve) => setTimeout(resolve, ms));
    const twitchDuration: number = 150;

    useEffect(() => {
        const twitchInterval = setInterval(() => {
            twitchEars();
        }, 2000);    // Interval between twitches

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


    const twitchEars = async () => {
        setTwitching(true);

        await delay(twitchDuration);
        setTwitching(false);

        await delay(200); // Delay between the twitches

        setTwitching(true);
        await delay(twitchDuration);

        setTwitching(false);
    };

    return (
        <div className="character">
             <img src="/src/assets/character-body.png" alt="Character body" className="character-body" />
            <img
                src="/src/assets/character-left-ear.png"
                alt="Left ear"
                className={`ear left ${twitching ? 'twitch-left' : ''}`}
                onClick={twitchEars}
            />
            <img
                src="/src/assets/character-right-ear.png"
                alt="Right ear"
                className={`ear right ${twitching ? 'twitch-right' : ''}`}
                onClick={twitchEars}
            />
        </div>
    );
};

export default CharacterDisplay;
Enter fullscreen mode Exit fullscreen mode

CharacterDisplay.css

.character {
    position: relative;
    width: 224px; 
    height: 246px; 
}

.character-body {
    position: absolute;
    width: 100%;
    height: 100%;
}

.ear {
    position: absolute;
    top: 2px;
    width: 40px;
    height: 40px;
    transition: transform 0.2s ease;
}

.left {    
    left: 40px;
}

.right {
    right: 43px;
}

.twitch-left {
    transform: rotate(-10deg);
}

.twitch-right {
    transform: rotate(10deg);
}
Enter fullscreen mode Exit fullscreen mode

Top comments (0)