User avatar

Jeroen Reumkens PRO

An unnamed playground on Frontend.fyi

Unlock Playground Access

Playgrounds let you build, test, and share frontend ideas instantly — right in your browser. Access is included in PRO. Just want Playgrounds? Pick a plan below.

Maker

€5,- / month

Unlimited Playgrounds access to build, experiment, and share without limits.

Join for €5 per month

Champion

€10,- / month

All the benefits of Maker, plus you help Frontend.fyi grow and get a Champion badge on your profile.

Join for €10 per month

Cancel anytime — no commitment.
Upgrade to PRO anytime and get a partial refund.We'll credit 100% of your first 3 months and 50% of the rest toward the PRO lifetime price.

Already a member? Login

Playground settings

Title

Description

Public

Editor settings


Packages

These packages can be imported in your JavaScript files.

Package name

Version

@

@

@

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
import { useAnimate } from "motion/react";
import { useEffect, useState, useRef } from "react";


export const App = () => {
  const [showDetails, setShowDetails] = useState(false);
  const [scope, animate] = useAnimate();
  const didMountRef = useRef(false);

  useEffect(() => {
  
  
    // If the below part would be active, the animation won't run on mount
    // this would cause the first subsequent run to NOT animate any transform
    // properties, but rather jump to the end. 
    // Using the hack to run it always, but with a duration of 0 seconds, will 
    // actually make it work the second time.
  
    /**
    if (!didMountRef.current) {
      didMountRef.current = true;
      return;
    }
    */
    
    const animationShow = [
      [".square", { scale: 1 }],
      [".text", { opacity: 1, y: 0 }],
    ];

    const animationHide = [
      [".text", { opacity: 0, y: 12 }],
      [".square", { scale: 0.5 }],
    ];


    animate(showDetails ? animationShow : animationHide, { 
      duration: !didMountRef.current ? 0 : 0.4 
    });
    
    // just as redundancy so the ref is still set to true if the top part is commented.
    if (!didMountRef.current) {
      didMountRef.current = true;
    }
  }, [showDetails]);

  return (
    <div>
      <div ref={scope}>
        <div className="square mb-3 w-[200px] h-[200px] scale-[0.5] bg-[#eee] p-6">
          <svg
            className="h-full w-full"
            xmlns="http://www.w3.org/2000/svg"
            fill="#000000"
            viewBox="-52.01 0 560.035 560.035"
          >
            <path d="M380.844 297.529c.787 84.752 74.349 112.955 75.164 113.314-.622 1.988-11.754 40.191-38.756 79.652-23.343 34.117-47.568 68.107-85.731 68.811-37.499.691-49.557-22.236-92.429-22.236-42.859 0-56.256 21.533-91.753 22.928-36.837 1.395-64.889-36.891-88.424-70.883-48.093-69.53-84.846-196.475-35.496-282.165 24.516-42.554 68.328-69.501 115.882-70.192 36.173-.69 70.315 24.336 92.429 24.336 22.1 0 63.59-30.096 107.208-25.676 18.26.76 69.517 7.376 102.429 55.552-2.652 1.644-61.159 35.704-60.523 106.559M310.369 89.418C329.926 65.745 343.089 32.79 339.498 0 311.308 1.133 277.22 18.785 257 42.445c-18.121 20.952-33.991 54.487-29.709 86.628 31.421 2.431 63.52-15.967 83.078-39.655" />
          </svg>
        </div>

        <p className="text translate-y-3 text-white opacity-0">
          Logo mark
        </p>
      </div>

      <button
        className="col-span-1 col-start-2 mx-auto bg-white p-4 mt-8"
        onClick={() => setShowDetails((a) => !a)}
      >
        Toggle Animation
      </button>
    </div>
  );
};

export default App;
One sec — editor's thinking…