简体   繁体   中英

How to use transition effect on accordion

I created simple accordion using React and styled components. I have a problem with making the effect so that the lower part can slide out slowly and not pop up right away. I used a transition but I don't know why it doesn't work in my case. There is part of my code:https://codesandbox.io/s/sweet-blackwell-v78wt?file=/src/App.js

For the best effect you'll need to transition the max-height from 0px to the height of the content and vice versa. You'll need to use the useRef and useEffect hooks to select the elements and manually set the styles on them.

You'll also need an inner wrapper that knows the height of the content. This will be a reference for setting the max-height on the collapsing container.

With the onTransitionEnd event listener you can remove the max-height property when the accordion is expanded. This enables the element to resize responsively based on the content.

The height of the content is determined with the getBoundingClientRect() function. It returns an object with the height, width (and more) of an element. With this you can determine the height of the content container.

The example below is a workable example based on your code.

import React, { useState, useRef, useEffect } from "react";
import "./styles.css";
import styled from "styled-components";

const Accordion = () => {
  const [isExpanded, setIsExpanded] = useState(false);
  const [isAnimating, setIsAnimating] = useState(false);
  const collapseRef = useRef(null);
  const contentRef = useRef(null);

  const handleClick = () => {
    /**
     * isAnimating prevents the accordion from changing
     * state when it is in the middle of a transition.
     */
    if (!isAnimating) {
      setIsAnimating(true);
      setIsExpanded((isExpanded) => !isExpanded);
    }
  };

  const handleTransition = () => {
    if (isExpanded) {
      /**
       * Removing the max-height when expanded makes sure
       * that the element still resizes responsively correct.
       */
      collapseRef.current.style.maxHeight = "";
    }
    setIsAnimating(false);
  };

  useEffect(() => {
    const { height } = contentRef.current.getBoundingClientRect();
    collapseRef.current.style.maxHeight = `${height}px`;
    if (!isExpanded) {
      /**
       * This makes sure that the element first is expanded
       * and in the next frame paint is transitioned to 0px.
       * Otherwise the 0px height would overwrite to dynamic height
       * and there'd be no transition.
       */
      requestAnimationFrame(() => {
        collapseRef.current.style.maxHeight = "0px";
      });
    }
  }, [isExpanded]);

  return (
    <ContentWrapper onClick={handleClick}>
      <TitleCard>
        <h2>Example text</h2>
      </TitleCard>
      <DescriptionCard
        ref={collapseRef}
        isVisible={isExpanded}
        style={{ maxHeight: "0px" }}
        onTransitionEnd={handleTransition}
      >
        <DescriptionContent ref={contentRef}>
          <p>Example text 2</p>
          <p>Now it expands based on the height of the</p>
          <h3>content.</h3>
          <p>Now it expands based on the height of the</p>
          <h3>content.</h3>
        </DescriptionContent>
      </DescriptionCard>
    </ContentWrapper>
  );
};

export default function App() {
  return (
    <div className="App">
      <Accordion />
    </div>
  );
}

const DescriptionContent = styled.div`
  display: flex;
  flex-direction: column;
`;

const DescriptionCard = styled.div`
  background: grey;
  overflow: hidden;
  align-items: center;
  justify-content: flex-start;
  transition: max-height 0.35s;
`;

const TitleCard = styled.div`
  cursor: pointer;
  height: 4.8rem;
  background: #ffffff;
  box-sizing: border-box;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0 1.5em;
`;

const ContentWrapper = styled.div`
  border-radius: 16px;
  border: 2px solid black;
  overflow: hidden;
`;

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM