在this amazing tutorial的帮助下,我通过稍微修改了代码创建了一个dropUP菜单。我的问题是,本教程中只有两个级别。
当只有两个级别时,例如A-> B,B和A只有一个输入和一个退出动画,因此可以轻松地使用其过渡类进行动画处理。
如果我想拥有5个级别,该怎么办?如果这些级别是这样的:A-> B-> C-> D-> E,我将不得不以4种方式对菜单C进行动画处理。
谢谢,我需要知道如何实现。
这是我的代码:
app.js
import './index.css';
import { ReactComponent as CaretIcon } from './icons/caret.svg';
import { ReactComponent as ChevronIcon } from './icons/chevron.svg';
import { ReactComponent as ArrowIcon } from './icons/arrow.svg';
import React, { useState, useEffect, useRef } from 'react';
import { CSSTransition } from 'react-transition-group';
function App() {
return (
<NavItem icon={<CaretIcon />}>
<DropdownMenu></DropdownMenu>
</NavItem>
);
}
function NavItem(props) {
const [open, setOpen] = useState(false);
return (
<div className="nav-item">
<a href="#" className="icon-button" onClick={() => setOpen(!open)}>
{props.icon}
</a>
{open && props.children}
</div>
);
}
function DropdownMenu() {
const [activeMenu, setActiveMenu] = useState('main');
const [menuHeight, setMenuHeight] = useState(null);
const dropdownRef = useRef(null);
useEffect(() => {
setMenuHeight(dropdownRef.current?.firstChild.offsetHeight)
}, [])
function calcHeight(el) {
const height = el.offsetHeight;
setMenuHeight(height);
}
function DropdownItem(props) {
return (
<a href="#" className="menu-item" onClick={() => props.goToMenu && setActiveMenu(props.goToMenu)}>
{props.leftIcon && <span className="icon-button">{props.leftIcon}</span>}
{props.children}
<span className="icon-right">{props.rightIcon}</span>
</a>
);
}
return (
<div className="dropdown" style={{ height: menuHeight }} ref={dropdownRef}>
<CSSTransition
in={activeMenu === 'main'}
timeout={500}
classNames="menu-primary"
unmountOnExit
onEnter={calcHeight}>
<div className="menu">
<DropdownItem
rightIcon={<ChevronIcon />}
goToMenu="PLAYBACK SPEED">
PLAYBACK SPEED
</DropdownItem>
<DropdownItem
rightIcon={<ChevronIcon />}
goToMenu="CLOSED CAPTIONS">
CLOSED CAPTIONS
</DropdownItem>
</div>
</CSSTransition>
<CSSTransition
in={activeMenu === 'PLAYBACK SPEED'}
timeout={500}
classNames="menu-secondary"
unmountOnExit
onEnter={calcHeight}>
<div className="menu">
<DropdownItem goToMenu="main" leftIcon={<ArrowIcon />}>
<h4>PLAYBACK SPEED</h4>
</DropdownItem>
<DropdownItem >0.75x</DropdownItem>
<DropdownItem >1x (Default)</DropdownItem>
<DropdownItem >1.5x</DropdownItem>
<DropdownItem >2x</DropdownItem>
</div>
</CSSTransition>
<CSSTransition
in={activeMenu === 'CLOSED CAPTIONS'}
timeout={500}
classNames="menu-secondary"
unmountOnExit
onEnter={calcHeight}>
<div className="menu">
<DropdownItem goToMenu="main" leftIcon={<ArrowIcon />}>
<h4>CC SETTINGS</h4>
</DropdownItem>
<DropdownItem
rightIcon={<ChevronIcon />}
goToMenu="FONT COLOR">
FONT COLOR</DropdownItem>
<DropdownItem rightIcon={<ChevronIcon />}>FONT SIIZE</DropdownItem>
<DropdownItem rightIcon={<ChevronIcon />}>FONT STYLE</DropdownItem>
<DropdownItem rightIcon={<ChevronIcon />}>BACKGROUND COLOR</DropdownItem>
<DropdownItem rightIcon={<ChevronIcon />}>BACKGROUND OPACITY</DropdownItem>
</div>
</CSSTransition>
<CSSTransition
in={activeMenu === 'FONT COLOR'}
timeout={500}
classNames="menu-secondary"
unmountOnExit
onEnter={calcHeight}>
<div className="menu">
<DropdownItem goToMenu="CLOSED CAPTIONS" leftIcon={<ArrowIcon />}>
<h4>FONT COLORS</h4>
</DropdownItem>
<DropdownItem >RED</DropdownItem>
<DropdownItem >BLUE</DropdownItem>
<DropdownItem >GREEN</DropdownItem>
<DropdownItem >YELLOW</DropdownItem>
<DropdownItem >BLACK</DropdownItem>
<DropdownItem >WHITE (Default)</DropdownItem>
</div>
</CSSTransition>
</div>
);
}
export default App;
index.css
body {
margin: 0;
background: #151616;
font-family: roboto;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
code {
font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New',
monospace;
}
:root {
--bg: #rgba(0, 0, 0, 0.85);
--bg-accent: #484a4d;
--text-color: #dadce1;
--nav-size: 60px;
--border: 0px solid #474a4d;
--border-radius: 2px;
--speed: 500ms;
}
ul {
list-style: none;
margin: 0;
padding: 0;
}
a {
color: var(--text-color);
text-decoration: none;;
}
/* Top Navigation Bar */
/* <ul> */
.navbar-nav {
max-width: 100%;
height: 100%;
display: flex;
justify-content: flex-end;
}
#root{
display: flex;
align-items: center;
justify-content: center;
}
/* <li> */
.nav-item {
width: calc(var(--nav-size) * 0.8);
display: flex;
align-items: center;
justify-content: center;
}
/* Icon Button */
.icon-button {
--button-size: calc(var(--nav-size) * 0.5);
width: var(--button-size);
height: var(--button-size);
background-color: #484a4d;
border-radius: 50%;
padding: 5px;
margin: 2px;
display: flex;
align-items: center;
justify-content: center;
transition: filter 300ms;
}
.icon-button:hover {
filter: brightness(1.2);
}
.icon-button svg {
fill: var(--text-color);
width: 20px;
height: 20px;
}
/* Dropdown Menu */
.dropdown {
position: absolute;
bottom: 50%;
width: 300px;
transform: translateX(-45%);
background-color: var(--bg);
border: var(--border);
border-radius: var(--border-radius);
overflow: hidden;
transition: height var(--speed) ease;
outline: white solid 1px;
}
.menu {
width: 100%;
bottom: 0;
position: absolute;
}
.menu-item {
height: 40px;
display: flex;
align-items: center;
border-radius: var(--border-radius);
transition: background var(--speed);
padding: 0.5rem;
}
.menu-item .icon-button {
margin-right: 0.5rem;
}
.menu-item .icon-button:hover {
filter: none;
}
.menu-item:hover {
background-color: rgba(255, 255, 255, 0.1);
}
.icon-right {
margin-left: auto;
}
/* CSSTransition classes */
.menu-primary-enter {
position: absolute;
transform: translateX(-110%);
}
.menu-primary-enter-active {
transform: translateX(0%);
transition: all var(--speed) ease;
}
.menu-primary-exit {
position: absolute;
}
.menu-primary-exit-active {
transform: translateX(-110%);
transition: all var(--speed) ease;
}
.menu-secondary-enter {
transform: translateX(110%);
}
.menu-secondary-enter-active {
transform: translateX(0%);
transition: all var(--speed) ease;
}
.menu-secondary-exit {
}
.menu-secondary-exit-active {
transform: translateX(110%);
transition: all var(--speed) ease;
}