What are the steps to create a "gooey glide" animation using React and MUI?

I am looking to create a unique animation for my list of items on a web page. My goal is to have the items slide in one by one with rapid succession and then slightly shrink once they reach their final position, similar to how pillows might fall or like a stack of sliced deli meat (I remember seeing this animation before but can't find an example). If anyone knows where I can find a reference, please share it.

Below is my basic attempt at achieving this effect:


import {Button, Slide, Stack, StackProps} from '@mui/material'
import {Box} from '@mui/system'

interface ZoomStackProps extends PropsWithChildren<StackProps> {
    timeout: number
}

export default function SquishSlideStack({children, timeout, ...stackProps}: ZoomStackProps) {

    const [mountIndex,   setMountIndex] = useState(0)
    const [squozeIndex, setSquozeIndex] = useState(0)

    function increment(n: number) {
        if (n < React.Children.count(children) - 1) {
            setMountIndex(n)
            setTimeout(() => increment(n + 1), timeout)
        }
    }

    useEffect(() => increment(1), [])

    return (
        <Stack {...stackProps}>
            <Button onClick={() => setMountIndex(index => index + 1)}>Next</Button>
            {React.Children.map(children, (child, i) =>
                i > mountIndex ? (
                    null
                ) : (
                    <Slide
                        key={i}
                        in={true}
                        direction='up'
                        timeout={1000}
                        addEndListener={() => setSquozeIndex(i)}
                    >
                        <Box bgcolor='green'
                             width={600}
                             height={50}
                             sx={{
                                  transform: i > squozeIndex ? 'scale(1, 1.5)' : 'scale(1, 1)',
                                  transition: 'transform 2s ease'
                             }}
                        >
                            {child}
                        </Box>
                    </Slide>
                )
            )}
        </Stack>
    )
}

View the Codesandbox example here.

The sliding animation works as intended, but adding the scaling part breaks the sliding effect and doesn't scale correctly. How can I achieve this animation successfully in React (preferably using MUI)?

Answer №1

Utilizing CSS3 animation is the most effective approach.

If you require assistance with implementation in React and MUI, feel free to leave a comment.

A straightforward demonstration is available here, where you can adjust parameters related to translate3d and scaleY in the animation according to your preferences.

<h1 class="callout-title_first animate__animated">Animate.css</h1>
<h1 class="callout-title_bottom animate__animated">Animate.css</h1>
<style>
  h1 {
    margin:0;
  }

  @keyframes slideInDown_first {
  0%{
    transform: translate3d(0,-100%,0);
  }
  33%{
    transform: translate3d(0,50%,0) ;
  }
  40%{
    transform-origin: bottom;;
    transform: translate3d(0,50%,0) scaleY(0.5);
  }
  to{
      transform: scaleY(0.5) translate3d(0,100%,0);
  }
}

  @keyframes slideInDown_second {
  0%{
    transform: translate3d(0,-100%,0);
  }
  33%{
    transform: translate3d(0,0,0) ;
  }
  40%{
    transform-origin: bottom;;
    transform: translate3d(0,0,0) scaleY(0.5);
  }
  66%{
    transform: translate3d(0,-20%,0) scaleY(0.5);
  }
  to{
      transform: translateZ(0);
      transform: scaleY(0.5);
  }
}
.animate__slideInDown_first {
  animation-name: slideInDown_first
}
.animate__slideInDown_second {
  animation-name: slideInDown_second
}
.animate__animated {
    animation-duration: 3000ms;
    animation-fill-mode: both
}
</style>
<script>
  document.querySelector('.callout-title_first').classList.add('animate__slideInDown_first')
  document.querySelector('.callout-title_bottom').classList.add('animate__slideInDown_second')
</script>

Answer №2

To achieve this task, I will incorporate CSS animations:

import { useState, useEffect } from 'react';
import { Button, Stack } from '@mui/material';
import { styled } from '@mui/system';

const ITEM_HEIGHT = 50;

const Wrapper = styled('div')({
  position: 'relative',
  overflow: 'hidden',
  height: ITEM_HEIGHT * 3,
});

const Item = styled('div')(({ theme }) => ({
  position: 'absolute',
  width: '100%',
  height: ITEM_HEIGHT,
  background: 'green',
  display: 'flex',
  justifyContent: 'center',
  alignItems: 'center',
  fontSize: 24,
  fontWeight: 'bold',
  color: theme.palette.common.white,
  animation: 'slideIn 0.3s ease-out forwards, squish 0.3s ease-out forwards',
  animationDelay: '0.1s',
  transformOrigin: 'center bottom',
}));

const SquishSlideStack = ({ children }) => {
  const [index, setIndex] = useState(-1);

  useEffect(() => {
    let timer;
    if (index < children.length - 1) {
      timer = setTimeout(() => {
        setIndex((i) => i + 1);
      }, 200);
    }
    return () => clearTimeout(timer);
  }, [index, children]);

  return (
    <Stack>
      <Wrapper>
        {children.map((child, i) => (
          <Item
            key={i}
            style={{
              top: ITEM_HEIGHT * i,
              animationDelay: `${i * 0.1}s`,
              transform: `scale(${index === i ? '1,1.5' : '1,1'})`,
            }}
          >
            {child}
          </Item>
        ))}
      </Wrapper>
      <Button disabled={index >= children.length - 1} onClick={() => setIndex((i) => i + 1)}>
        Next
      </Button>
    </Stack>
  );
};

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

How can Typescript help enhance the readability of optional React prop types?

When working with React, it is common practice to use null to indicate that a prop is optional: function Foo({ count = null }) {} The TypeScript type for this scenario would be: function Foo({ count = null }: { count: number | null }): ReactElement {} Wh ...

Suggestions for resolving the issue of my header being truncated on mobile browsers?

I am facing an issue with my header and need assistance in fixing it. The problem occurs when scrolling down, as it cuts off a part of the header, but returns to normal when scrolling back up. I suspect the hamburger menu might be causing this issue becaus ...

Adjust the position of the remaining li elements to accommodate the increased height of a single li element

I am currently working on a dropdown menu and I'm encountering an issue where, in the second level of <ul>, if the height of an <li> increases due to accommodating another <ul>, the other <li> elements in the second level do no ...

Exposure to vulnerabilities in react-scripts

While working on my ReactJS app, I encountered a challenge related to vulnerability and security issues. The use of react-scripts library has highlighted numerous vulnerabilities in the latest version. Is it possible for me to directly update the depende ...

Customizing the color of a select dropdown in Bootstrap using CSS styling

I am having difficulty changing the default blue color for Bootstrap select dropdowns. Despite trying :hover and :active selectors on both option and select, I have not been successful in making the desired changes. Could anyone provide insight regarding ...

Issue with displaying rows in Bootstrap grid

Is there a way to display images in a row for my portfolio instead of a column? I've checked my code but can't seem to figure out what's causing them to display vertically. Any assistance would be appreciated. Below is the code snippet: < ...

transmitting information to a RESTful endpoint through an Ajax request

I've been attempting to send a small table of data to my REST endpoint through Ajax, but unfortunately, I am not seeing the results in the backend code as expected. Here is the snippet of my Ajax request: submitData:function(){ var self = this; var ...

React component combined with Material-UI library generates a warning message: Prop className does not match

Struggling with different styles between client-side and server-side rendering in Material-UI components due to inconsistencies in classNames assignment. Initially, the classNames are assigned correctly upon loading the page. However, upon refreshing the ...

Enhance my code to eliminate repetitive elements

Check out this unique plant array: export const uniquePlants = [ { name: 'monstera', category: 'classique', id: '1ed' }, { name: 'ficus lyrata&ap ...

An issue arises when utilizing a string variable in React-bootstrap's OverlayTrigger placement attribute

I encountered an unexpected issue with the OverlayTrigger component in React-Bootstrap version 5.1.1. I'm attempting to develop a custom button component using OverlayTrigger and a standard button. Everything is functioning as intended, except for whe ...

What are some effective tactics for reducers in react and redux?

Working on a React + Redux project to create a web app that communicates with an API, similar to the example provided at https://github.com/reactjs/redux/tree/master/examples/real-world. The API I'm using returns lists of artists, albums, and tracks, ...

Struggling to display Firebase Auth information resulting in 'undefined' value within React web application

When loading a user's profile page, I am trying to display their displayName and email information retrieved from Firebase Auth. I have implemented this logic within the 'componentDidMount' method by updating the state with the response dat ...

The art of masonry is not effective

I'm having trouble getting the Masonry cascading grid layout library to work in my code. Stylesheet: .post { background: #FFF; padding: 10px; border-bottom: 3px solid #e6e6e6; width: 30.7%; margin: 10px; } Source code: <div ...

Utilizing the :after pseudo-element for placing text in a specific location

Here's the code I'm working with: div#myImg{ background: url('myimage.png') left top no-repeat; } div#myImg:after{ content: 'TEXT UNDER IMAGE'; margin:0 auto; vertical-align:text-b ...

Tips for establishing breakpoints within Material UI grid for achieving responsiveness

I am currently utilizing Material ui's Grid component to organize my user interface elements. Here is the code snippet: <Grid container spacing={3}> <Grid container item xs={12} sm={12} md={12} style={{ marginTop: '-1.5%', marginRi ...

Is it possible to customize the background color of the 'rows per page' selector box in Bootstrap 4 bootstrap-table?

The text is set in white on a dark grey background by default and appears just below the formatted table. Best regards, Phil Please refer to the attached image:Section of table showing rows per page selector box ...

How come the spacing between my columns decreases as I expand the width of my container?

I'm struggling to grasp the concept behind the column gap in a multi-column layout. Take a look at the HTML/CSS code snippet I have set up in this Fiddle: <div class="flex-container"> <div class="flex-item">1</div& ...

How to isolate a function in React when mapping data

I am currently working on mapping data as a repeater in my React project. However, I am facing an issue with isolating the opening function, specifically with an accordion component. As I continue to learn React, I want to ensure that each accordion operat ...

The MUI classes in my React project differ between the local and live versions, creating inconsistency in styling

I've encountered a discrepancy in the Mui classes used in my React project between the local and live versions. For instance: Localhost MuiButtonBase-root MuiButton-root MuiButton-text MuiButton-textPrimary MuiButton-sizeSmall MuiButton-textSizeSmal ...

What techniques does DeviantArt use to adapt and adjust the content displayed on a page based on the width of the user's

Visit and observe the functionality where new content appears as you widen your browser window. The alignment of the content in the center is a key aspect of this design on DeviantArt's website. How is this achieved? I attempted to create a div with ...