如果单击表单按钮,则 Framer Motion 为父 FORM 元素设置动画

Framer Motion animate parent FORM element if form button is clicked

我最近开始使用 Framer Motion。我真的很喜欢它,它使动画 div 和添加页面转换变得容易得多。我在检查我的表单是否已切换 (open/closed) 以使用 Framer Motion 为父 <form> 标签设置动画时遇到问题。 <motion.form> 但是,我已经在检查 Toggle 是否处于活动状态,这样它就会触发并检查动画和切换状态这两个方面。

如果 toggleForm 处于活动状态,我如何简单地为表单设置动画?

父级 Article.tsx:

import React, { useState, useCallback } from "react";

import { NextPage } from "next";
import Head from "next/head";
import Link from "next/link";

import VideoModule from "@modules/VideoModule";
import HeroModule from "@modules/HeroModule";
import SliderModule from "@modules/SliderModule";
import ImageModule from "@modules/ImageModule";
import QuoteModule from "@modules/QuoteModule";
import PreFooterModule from "@modules/PreFooterModule";

import Footer from "@components/Footer";
import CommentForm from "@components/CommentForm";

const Article: NextPage = () => {
  const closeFormText = "Ik reageer later";
  const respondFormText = "Ik wil reageren";

  const [buttonText, setButtonText] = useState(respondFormText);
  const [toggleForm, setToggleForm] = useState(false);

  const onToggleForm = useCallback(() => {
    setToggleForm(!toggleForm);

    !toggleForm ? setButtonText(closeFormText) : setButtonText(respondFormText);
  }, [toggleForm, setToggleForm]);

  return (
    <>
      <Head>
        <title>Artikel Detail</title>
        <meta name="author" content="" />
        <meta name="description" content="Developed by Friends For Brands" />
        <link rel="icon" href="/favicon.ico" />
      </Head>

      <HeroModule
        title="De headline van deze tekstuele content"
        text="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat."
      />

      <div className="max-w-screen-xl px-6 pt-16 pb-12 mx-auto border-navyBlue border-b-1 lg:px-8 text-navyBlue ">
        <div className="grid w-full grid-cols-12 gap-6">
          <aside className=" lg:col-span-3 col-span-full">
            <div className="grid grid-cols-12 lg:sticky lg:top-5 lg:block">
              <div className="col-span-2 mb-4 avatar">
                <div className="w-16 h-16 sm:h-20 sm:w-20 md:w-24 md:h-24 rounded-full ring ring-[#65c3c8] ring-offset-base-100 ring-offset-2">
                  <img src="https://i.pravatar.cc/300" alt="" />
                </div>
              </div>

              <div className="col-span-8 leading-2">
                <p className="font-bold">John Doe</p>
                <p>Marketing Manager</p>

                <p className="mt-2 md:mt-6">00/00/0000</p>
                <p>Leestijd 10 minuten</p>

                <div className="mt-6 card-actions">
                  <Link href="/tag/fashion" passHref>
                    <span className="text-[11px] font-semibold uppercase cursor-pointer badge badge-outline hover:bg-navyBlue hover:text-white">
                      Fashion
                    </span>
                  </Link>
                  <Link href="/tag/products" passHref>
                    <span className="text-[11px] font-semibold uppercase cursor-pointer badge badge-outline hover:bg-navyBlue hover:text-white">
                      Products
                    </span>
                  </Link>
                </div>
              </div>
            </div>
          </aside>
          <main className="leading-relaxed col-span-full lg:col-span-9 md:text-normal">
            <p>
              Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
              eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
              enim ad minim veniam, quis nostrud exercitation ullamco laboris
              nisi ut aliquip ex ea commodo consequat.
            </p>

            <h2 className="mt-3 text-3xl md:text-4xl">Koptekst H2</h2>
            <h3 className="mt-3 text-2xl md:text-3xl">Koptekst H3</h3>
            <h4 className="mt-3 text-xl md:text-2xl">Koptekst H4</h4>

            <ul className="pl-6 my-4 list-disc">
              <li>Lorem ipsum dolor sit amet</li>
              <li>
                Consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
                labore
              </li>
              <li>Et dolore magna aliqua. Ut enim ad minim veniam</li>
              <li>
                Quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
                commodo consequat.
              </li>
            </ul>
            <h2 className="mt-6 text-3xl md:text-4xl">Tussentitel</h2>
            <ul className="pl-6 my-4 list-decimal">
              <li>Lorem ipsum dolor sit amet</li>
              <li>
                Consectetur adipiscing elit, sed do eiusmod tempor incididunt ut
                labore
              </li>
              <li>Et dolore magna aliqua. Ut enim ad minim veniam</li>
              <li>
                Quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea
                commodo consequat.
              </li>
            </ul>
            <h5 className="font-bold">Tussentitel paragraaf</h5>
            <p>
              Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
              eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
              enim ad minim veniam, quis nostrud exercitation ullamco laboris
              nisi ut aliquip ex ea commodo consequat.
            </p>
            <h3 className="mt-6 text-2xl md:text-3xl">Video Module</h3>
            <React.StrictMode>
              <VideoModule id="mkggXE5e2yk" platform="youtube" />
            </React.StrictMode>
            <p>
              Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
              eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
              enim ad minim veniam, quis nostrud exercitation ullamco laboris
              nisi ut aliquip ex ea commodo consequat.
            </p>
            <h3 className="mt-6 text-2xl md:text-3xl">Image Module</h3>
            <ImageModule
              url="https://images.pexels.com/photos/1193743/pexels-photo-1193743.jpeg"
              caption="Photo of multicolored abstract painting"
              alt="A Pexels image"
            />
            <p>
              Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
              eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
              enim ad minim veniam, quis nostrud exercitation ullamco laboris
              nisi ut aliquip ex ea commodo consequat.
            </p>
            <h3 className="mt-6 text-2xl md:text-3xl">Slider Module</h3>
            <SliderModule />
            <p>
              Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
              eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
              enim ad minim veniam, quis nostrud exercitation ullamco laboris
              nisi ut aliquip ex ea commodo consequat.
            </p>
            <h3 className="mt-6 text-2xl md:h2text-3xl">Quote Module</h3>
            <QuoteModule />
          </main>
        </div>
      </div>

      <CommentForm
        buttonText={buttonText}
        toggleForm={toggleForm}
        clickHandle={onToggleForm}
      />

      <PreFooterModule />
      <Footer />
    </>
  );
};

export default Article;

子组件CommentForm.tsx:

import { motion } from "framer-motion";

const variants = {
   open: { opacity: 1, x: 0 },
   closed: { opacity: 0, x: "-100%" },
}

interface CommentFormProps {
  buttonText: string;
  toggleForm: boolean;
  clickHandle: any;
}

const CommentForm = ({
  buttonText,
  toggleForm,
  clickHandle,
}: CommentFormProps) => (
  <div className="w-full max-w-4xl px-4 py-20 mx-auto">
    <div className="flex gap-4">
      <h2 className="text-2xl md:text-4xl">Reacties</h2>
      <button
        onClick={clickHandle}
        className={`inline-flex px-4 py-2 place-items-center text-xs font-medium uppercase transition duration-150 ease-in-out border rounded-full cursor-pointer border-navyBlue hover:bg-navyBlue hover:text-white md:mt-1`}
      >
        {buttonText}
      </button>
    </div>

    {toggleForm && (
      <motion.form className="pt-8" autoComplete="off" animate {toggleForm ? "open" : "closed"}
  variants={variants}>
        <div className="grid xl:grid-cols-2 xl:gap-6">
          <p className="mb-5 font-bold col-span-full md:m-0">Jouw gegevens</p>

          <div className="relative z-0 w-full mb-6">
            <input
              type="text"
              name="first_and_lastname"
              id="first_and_lastname"
              className="block w-full py-3 text-sm bg-transparent border-0 appearance-none text-navyBlue border-b-1 border-navyBlue focus:outline-none focus:ring-0 focus:border-bubblegum peer"
              placeholder=" "
              required
            />
            <label
              htmlFor="first_and_lastname"
              className="absolute text-md text-navyBlue duration-200 transform -translate-y-6 scale-75 top-3 -z-10 origin-[0] peer-focus:left-0 peer-focus:text-navyBlue peer-placeholder-shown:scale-100 peer-placeholder-shown:translate-y-0 peer-focus:uppercase peer-focus:scale-75 peer-focus:-translate-y-6"
            >
              Voor- en Acthernaam
            </label>
          </div>

          <div className="relative z-0 w-full mb-6">
            <input
              type="email"
              name="email"
              className="block w-full py-3 text-sm bg-transparent border-0 appearance-none text-navyBlue border-b-1 border-navyBlue focus:outline-none focus:ring-0 focus:border-bubblegum peer"
              placeholder=" "
              required
            />
            <label
              htmlFor="email"
              className="absolute text-md text-navyBlue duration-200 transform -translate-y-6 scale-75 top-3 -z-10 origin-[0] peer-focus:left-0 peer-focus:text-navyBlue peer-placeholder-shown:scale-100 peer-placeholder-shown:translate-y-0 peer-focus:uppercase peer-focus:scale-75 peer-focus:-translate-y-6"
            >
              E-mailadres
            </label>
          </div>
        </div>

        <div className="relative z-0 w-full mt-2 mb-6">
          <textarea
            id="comment"
            name="comment"
            rows={5}
            className="block w-full py-5 text-3xl bg-transparent border-0 appearance-none resize-none text-navyBlue border-b-1 border-navyBlue focus:outline-none focus:ring-0 focus:border-bubblegum peer"
            placeholder=" "
            required
          />

          <label
            htmlFor="comment"
            className="absolute text-md text-navyBlue duration-200 transform -translate-y-6 scale-75 top-3 -z-10 origin-[0] peer-focus:left-0 peer-focus:text-navyBlue peer-placeholder-shown:scale-100 peer-placeholder-shown:translate-y-0 peer-focus:uppercase peer-focus:scale-75 peer-focus:-translate-y-6"
          >
            Reactie
          </label>
        </div>

        <button
          type="submit"
          className="items-center justify-center px-6 py-1.5 text-sm font-medium uppercase transition duration-150 ease-in-out bg-transparent border rounded-full shadow-sm md:text-lg text-navyBlue border-navyBlue hover:bg-navyBlue hover:text-white"
        >
          Reageer op artikel
        </button>
      </motion.form>
    )}
  </div>
);

export default CommentForm;

您正在使用 toggleForm 状态更改表单上的 animate 属性,但您还使用它有条件地呈现表单。因此,一旦该状态切换到 false,表单就会从 DOM 中删除,而没有机会动画到“关闭”变体。

{toggleForm && (
      <motion.form className="pt-8" autoComplete="off" animate {toggleForm ? "open" : "closed"}
  variants={variants}>
 //...etc
)}

最简单的修复方法是删除条件渲染,并仅使用状态更改 motion.form 元素上的动画变体(“打开”或“关闭”)。

如果您确实需要像这样从 DOM 中删除表单,您可以使用 AnimatePresenceexit 动画(不要忘记给表单一个唯一的key prop!) 让元素在从 DOM.

中移除之前执行动画