1

To further understand React I have created a project using react-router-dom, styled-components and framer-motion.

My versions are like so.

package-json:

    ....
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "react-icons": "^4.7.1",
    "react-router-dom": "^6.8.1",
    "react-scripts": "5.0.1",
    "styled-components": "^5.3.6",
    ....

My modal has a simple animation with opacity and the container has backdrop-filter: blur(10px) applied to it. I want the blur effect to be applied as soon as the fading in of the modal starts. As it is now, the animation is carried out and only after is the backdrop-filter applied. I find this strange, as all other styles, such as background-color, are applied from the moment the animation starts.

My component Modal.js:

import { IntroModal, ModalContainer } from './Modal.styled';
import { useEffect, useRef } from 'react';
import { motion } from 'framer-motion';
import { AnimatePresence } from 'framer-motion';
import { IoMdClose } from 'react-icons/io';

const Modal = ({ isOpen, toggleModal, closeOnOutsideClick, children }) => {
  const modalRef = useRef(null);

  useEffect(() => {
    const handleClickOutside = (event) => {
      if (
        closeOnOutsideClick &&
        modalRef.current &&
        !modalRef.current.contains(event.target)
      ) {
        toggleModal();
      }
    };

    document.addEventListener('mousedown', handleClickOutside);
    return () => {

      document.removeEventListener('mousedown', handleClickOutside);
    };
  }, [modalRef, closeOnOutsideClick, toggleModal]);
  const closeButtonStyle = {
    color: 'white',
    fontSize: '1.5rem',
    cursor: 'pointer',
    filter: 'drop-shadow(0px 0px 2px #000)',
  };
  !isOpen
    ? (document.body.style.overflow = 'hidden')
    : (document.body.style.overflow = 'auto');
  return (
    <AnimatePresence>
      !isOpen && (
      <motion.div
        style={isOpen ? { display: 'none' } : null}
        initial={{ opacity: 0 }}
        animate={{ opacity: 1 }}
        exit={{ opacity: 0 }}
        key={isOpen}
      >
        <IntroModal>
          <ModalContainer ref={modalRef}>
            <button onClick={toggleModal}>
              <IoMdClose style={closeButtonStyle} />
            </button>
            {children}
          </ModalContainer>
        </IntroModal>
      </motion.div>
      )
    </AnimatePresence>
  );
};
export default Modal;

Modal.styled.js:

import styled from 'styled-components';

export const IntroModal = styled.div`
  position: fixed;
  display: flex;
  justify-content: center;
  align-items: center;
  z-index: 11;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  background-color: rgba(0, 0, 0, 0.7);

    button {
      position: absolute;
      top: 0.3rem;
      right: 0.3rem;
      padding: 0.5rem;
      border: none;
      background: transparent;
      transition: transform 0.2s ease-in-out;
      &:hover {
        transform: scale(1.1);
      }
    }

    img {
      width: 50%;
      box-shadow: 0 0 15px #000;
      margin-top: 1rem;
    }

    p {
      font-weight: bold;
      font-size: 1rem;
      padding: 0 1rem;
    }
`;

export const ModalContainer = styled.div`
  backdrop-filter: blur(10px);
  position: absolute;
  top: 50%;
  width: 30%;
  background-color: rgba(38, 50, 50, 0.5);
  text-align: center;
  transform: translateY(-50%);
  padding: 1rem 0.2rem 0.5rem;
  border-top: 0.7px solid #b6b6b666;
  border-right: 0.7px solid #8e8e8e66;
  border-bottom: 0.7px solid #000;
  border-left: 0.7px solid #77777766;
  border-radius: 18px;
  box-shadow: 0 0 1rem 0 #000;
  min-height: 200px;
  display: inherit;
  flex-wrap: wrap;
  flex-direction: row;
  justify-content: center;
  align-items: center;

  @media screen and (max-width: 768px) {
    width: 80vw;
    height: 80vh;
  }
`;

export const ModalWrapper = styled.div`
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  width: 50%;
  height: 25%;
  font-size: 2rem;
`;

Here is a gif of the appearance of it now:

Clicking on modal

I have tried to apply the styles directly to the component, eg style={backdropFilter: blur(10px), and also including it in the animation like so (tried both in outer motion.div and ModalContainer separately):

      <motion.div
        style={isOpen ? { display: 'none' } : null}
        initial={{ opacity: 0, backdropFilter: 'blur(0px)' }}
        animate={{ opacity: 1, backdropFilter: 'blur(10px)' }}
        exit={{ opacity: 0, backdropFilter: 'blur(0px)' }}
        key={isOpen}
      >
        <IntroModal>
          <motion.ModalContainer ref={modalRef}
              initial={{ opacity: 0, backdropFilter: 'blur(0px)' }}
              animate={{ opacity: 1, backdropFilter: 'blur(10px)' }}
              exit={{ opacity: 0, backdropFilter: 'blur(0px)' }}
              key={isOpen}
          >
            <button onClick={toggleModal}>
              <IoMdClose style={closeButtonStyle} />
            </button>
            {children}
          </motion.ModalContainer>
        </IntroModal>
      </motion.div>

Any tips and advice much appreciated!

johnvenkiah
  • 23
  • 1
  • 3

1 Answers1

1

I don't believe you can animate backdrop-filter: blur, so what you are only seeing two states of it - blur(0) and blur(10px) instead of all the intermediate animation steps, so it looks like it is applied after the other transitions complete.

This answer suggests animating from backdrop-filter: blur(10px) opacity(0); to backdrop-filter: blur(4px) opacity(1); since backdrop-filter: opacity can be animated.

Pavel Savva
  • 449
  • 2
  • 7
  • Hi, thanks for your reply. I understand that backdrop-filter is not animated, I have backdrop-filter applied, I don't want to animate blur - only animate the opacity of the blurred div, which I wish to be blurred from the beginning. – johnvenkiah Mar 22 '23 at 21:46