John Gale
John Gale

Reputation: 113

React and CSS Animation - only animate the clicked element with a shared className

I am working on a simple web browser drawing app using React. The app uses images for the buttons to select which color to draw with. Each of these color button images has the same className='color-button'

I followed this article for how to animate the color button images on click using CSS animation: https://dev.to/samwatts98/how-to-easily-animate-your-react-components-on-click-with-css-keyframes-26mg

The problem is that clicking on any one color button image makes them all animate because they all share className='color-button'. I only want to animate the color button image that was clicked on.

The live version of the app in its current state is here: https://master.d3irlm8kk772s.amplifyapp.com/

Below is the code to set the "push" animation state with hooks plus an additional function I tried that didn't work:

const [push, setPush] = useState(0);

//This function was tried but didn't solve the problem:
  function changeSetPush(){
    setPush(1);
  }

Here is the code for the images themselves. The first image tries the changeSetPush function, but achieves the same result as the other images:

{/* Circle Color Buttons */}
            <div style={{width: 96}}>
                <img src={BlackCircle} alt='black' className='color-button'
                  onClick={() => {changePenColor('#000000');
                  changeSetPush()}} onAnimationEnd={() => setPush(0)} push={push}
                  />
                <img src={WhiteCircle} alt='white' className='color-button'
                  onClick={() => {changePenColor('#FFFFFF');
                  setPush(1)}} onAnimationEnd={() => setPush(0)} push={push}
                  />
                <img src={RedCircle} alt='red' className='color-button'
                  onClick={() => {changePenColor('#FE2B01');
                  setPush(1)}} onAnimationEnd={() => setPush(0)} push={push}
                  />
                <img src={GreenCircle} alt='green' className='color-button'
                  onClick={() => {changePenColor('#3FF913');
                  setPush(1)}} onAnimationEnd={() => setPush(0)} push={push}
                  />
                <img src={YellowCircle} alt='yellow' className='color-button'
                  onClick={() => {changePenColor('#FCFC0A');
                  setPush(1)}} onAnimationEnd={() => setPush(0)} push={push}
                  />
                <img src={BlueCircle} alt='blue' className='color-button'
                  onClick={() => {changePenColor('#2A2EFE');
                  setPush(1)}} onAnimationEnd={() => setPush(0)} push={push}
                  />
                <img src={OrangeCircle} alt='orange' className='color-button'
                  onClick={() => {changePenColor('#FF8B00');
                  setPush(1)}} onAnimationEnd={() => setPush(0)} push={push}
                  />
                <img src={PurpleCircle} alt='purple' className='color-button'
                  onClick={() => {changePenColor('#F03EFE');
                  setPush(1)}} onAnimationEnd={() => setPush(0)} push={push}
                  />
            </div>

And here is the CSS for the animation:

.color-button {
  width: 48px;
  height: 48px;
  vertical-align: bottom;
}

.color-button[push='1'] {
  animation: push 200ms 1;
}

@keyframes push {
  25% {
    transform:scale(0.95);
  }
  50% {
    transform:scale(0.90);
  }
  75% {
    transform:scale(0.85);
  }
  100% {
    transform:scale(0.80);
  }
}

The GitHub repo is here: https://github.com/Johnsensei/oekaki

The React component is a functional component, so I'm not able to use this.setState.

What do I change in the code to get the animation only on the color-button that was clicked?

Thanks for your help.

Upvotes: 1

Views: 2923

Answers (1)

John Gale
John Gale

Reputation: 113

Ok, it got sorted in a very "React" way to approach the problem.

First, we make a new component called ColorButton

import React, { useState } from 'react';

const ColorButton = ({ alt, onClick, src }) => {
    const [pushed, setPushed] = useState(false);
  
    return (<img 
             src={src}
             alt={alt}
             className={`color-button ${pushed ? "push" : ""}`}
             onClick={() => {
                onClick();
                setPushed(true);
             }}
             onAnimationEnd={() => setPushed(false)}
             />)
  }

  export default ColorButton;

Then we import and implement it into the Main component:

import ColorButton from './ColorButton';

...

<div style={{width: 96}}>

                <ColorButton
                  src={BlackCircle}
                  alt='black'
                  onClick={() => changePenColor('#000000')}
                />
                <ColorButton
                  src={WhiteCircle}
                  alt='white'
                  onClick={() => changePenColor('#FFFFFF')}
                />
                <ColorButton
                  src={RedCircle}
                  alt='red'
                  onClick={() => changePenColor('#FE2B01')}
                />
                <ColorButton
                  src={GreenCircle}
                  alt='green'
                  onClick={() => changePenColor('#3FF913')}
                />
                <ColorButton
                  src={YellowCircle}
                  alt='yellow'
                  onClick={() => changePenColor('#FCFC0A')}
                />
                <ColorButton
                  src={BlueCircle}
                  alt='blue'
                  onClick={() => changePenColor('#2A2EFE')}
                />
                <ColorButton
                  src={OrangeCircle}
                  alt='orange'
                  onClick={() => changePenColor('#FF8B00')}
                />
                <ColorButton
                  src={PurpleCircle}
                  alt='purple'
                  onClick={() => changePenColor('#F03EFE')}
                />

            </div>

And finally, update the CSS animation:

.color-button {
  width: 48px;
  height: 48px;
  vertical-align: bottom;
}

.push {
  animation: push 200ms 1;
}

@keyframes push {
  25% {
    transform:scale(0.95);
  }
  50% {
    transform:scale(0.90);
  }
  75% {
    transform:scale(0.85);
  }
  100% {
    transform:scale(0.80);
  }
}

The fix is now live: https://master.d3irlm8kk772s.amplifyapp.com/

This was my first time posting a question to Stack Overflow. Everyone was so helpful and supportive here and in the Discord and Slack channels I was asking for help in too. Special shoutout to Tyler from freeCodeCamp Nashville. Thanks!

Upvotes: 2

Related Questions