9

I am trying to animate an image with Framer Motion:

utils/MonkeyPicture.js

import React from 'react';

const MonkeyPic = () => {

        return (
            <div>               
                  <img
                    transition={{ duration: 0.5 }}
                    animate={{ rotate: [0, -30, 0]}}
                    id='monkeyFace'
                    src='/images/Monkey.png' />
                               
            </div>);         
}
export default MonkeyPic; 

So I would need a function that only adds or activates the atributes: transition={{ duration: 0.5 }} animate={{ rotate: [0, -30, 0]}} When I click on a Button.

The picture is rendered the whole time, I just wish to rotate it when I click a button.

The onClick method is in the AddTodo.js container:

            <button id='addTodo' onClick={() => {
                monkeySound.play(); 
                setShowFistBump(true);
                setTimeout(() => { 
                    setShowFistBump(false);
                }, 1000);
FilipZafran
  • 315
  • 1
  • 5
  • 14

2 Answers2

17

You could use variants, for example like that:

// At first you need to pass `rotate` prop to MonkeyPic component inside your AddTodo
// You can use existing showFistBump state for that

<MonkeyPic rotate={showFistBump} />

// ...

// In the component create variants of your animation state
const variants = {
  rotate: { rotate: [0, -30, 0], transition: { duration: 0.5 } },
  // You can do whatever you want here, if you just want it to stop completely use `rotate: 0`
  stop: { y: [0, -10, 0], transition: { repeat: Infinity, repeatDelay: 3 } }
};

// Then switch animation variant depending on that `rotate` prop

const MonkeyPic = ({ rotate }) => {
  return (
    <div>
      <motion.img
        variants={variants}
        animate={rotate ? 'rotate' : 'stop'}
        id="monkeyFace"
        src="/images/Monkey.png"
      />
    </div>
  );
};

Codesandbox link: https://codesandbox.io/s/httpsstackoverflowcomquestions63864386-rd1xh?file=/src/utils/MonkeyPicture.js

Danila
  • 15,606
  • 2
  • 35
  • 67
2

In React changing an element's key makes React treat it as an entirely new component.

While framer provides us three types of animation use cases

  1. Enter animation { when component renders }
  2. Exit animation { when component is about to leave the dom tree }
  3. Gesture based animation

However there is a lack of like, trigger animation which will help us play an animation when we like, but anyways we will use the key prop of the component we are interested in and will set it's value to a ref, then to trigger it, we will change the ref's value so each time the component is re rendered and the entry animation is played, the example convers the theory

import React, { useRef, useState } from "react";
import { motion } from "framer-motion";

function ShakingButton({
  children,
  className,
  onClick,
}: {
  onClick?: React.MouseEventHandler<HTMLDivElement>;
  children: React.ReactNode;
  className: string;
}) {
  
  const ref = useRef(0);
  return (
    <motion.div
      key={ref.current}
      animate={ref.current === 0 ? {} : {
        x : [0, 10,-10,0]
      }}
      transition={{
        type: "spring",
      }}
      onClick={(e) => {
        onClick && onClick(e);
        ref.current++;
      }}
      className={className}
    >
      {children}
    </motion.div>
  );
}

export default ShakingButton;