DEV Community

mich0w0h
mich0w0h

Posted on • Updated on

Web Animation: Create endless twitching ear motion with a certain interval

This post is a continuous of the previous one;
CSS: Position separate elements properly within a character's body - DEV Community

What I want to achieve

  • twitch a character's ears in a web app made by React + Typescript
  • the twitch motion starts just after the page is loaded
  • the motion continues endlessly in a certain interval.

Screen visual of prev state

prev screen visual

Codes and Explanation

Set up a state of boolean variable twitching

const [twitching, setTwitching] = useState(false);
Enter fullscreen mode Exit fullscreen mode

When the twitching variable turns true, it applies a CSS class(twitch-left or twitch-right) to the respective ear, causing it to rotate slightly.

<img
    src="/src/assets/character-left-ear.png"
    alt="Left ear"
    className={`ear left ${twitching ? 'twitch-left' : ''}`}
/>
Enter fullscreen mode Exit fullscreen mode

Here's CSS.

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

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

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

All that's left is to toggle the state variable.
Use the useEffect hook to trigger the toggle shortly after the component mounts.

useEffect(() => {
  // toggle variable
}, []);
Enter fullscreen mode Exit fullscreen mode

Inside the effect, use setInterval to trigger the twitching motion at regular intervals.

useEffect(() => {
    const twitchInterval = setInterval(() => {
        setTwitching(true);
    }, 3000);    // Interval between twitches
}, []);
Enter fullscreen mode Exit fullscreen mode

By returning a cleanup function inside the useEffect, it can be ensured that the interval is stopped and cleaned up when the component unmounts.

useEffect(() => {
    const twitchInterval = setInterval(() => {
        setTwitching(true);
        setTimeout(() => {
            setTwitching(false);
        }, twitchDuration);    // Duration of each twitch motion
    }, 3000);    // Interval between twitches

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

Enter fullscreen mode Exit fullscreen mode

Now it's done!

twitching

Complete form of codes

import { useState, useEffect } from 'react';
import './CharacterDisplay.css'; // Import your CSS for character styling

const CharacterDisplay: React.FC = () => {
    const [twitching, setTwitching] = useState(false);
    const twitchDuration: number = 200;

    useEffect(() => {
        const twitchInterval = setInterval(() => {
            setTwitching(true);
            setTimeout(() => {
                setTwitching(false);
            }, twitchDuration);    // Duration of each twitch motion
        }, 3000);    // Interval between twitches

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

    const twitchEars = () => {
        setTwitching(true);
        setTimeout(() => {
            setTwitching(false);
        }, twitchDuration);
    };

    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

What's next

Twitch ears twice in a row

Top comments (0)