在 React 中用过渡覆盖导航栏
overlay navbar with transition in React
我在使用应该从顶部滑入的叠加导航栏时遇到问题。
不知道为什么,但如果我单击汉堡菜单,则会应用正确的 css class,但不会发生转换(它的作用类似于 show/hidden) .
但是,如果我从开发工具更改顶部 属性 值,它会按预期工作。
我错过了什么?
这是反应组件
import React, { useState } from "react";
import NavMenuIcon from "../../public/svg/nav.svg";
import styles from '../../styles/Navbar.module.scss'
import Link from 'next/link'
function Navbar() {
const [menu, setToggle] = useState(false);
const toggleMenu = () => setToggle(!menu);
const Menu = props => (
<div className={ props.toggle ? `${styles.menu__container} ${styles.toggle}` : `${styles.menu__container}` }>
<div className={styles.menu__content}>
<ul className={styles.menu__list}>
<li className={styles.menu__item} onClick={toggleMenu}> <Link href="/"><span>Home</span></Link></li>
<li className={styles.menu__item} onClick={toggleMenu}> <Link href="/services"><span>Services</span></Link></li>
<li className={styles.menu__item} onClick={toggleMenu}> <Link href="/services#contact"><span>Contact</span></Link></li>
</ul>
</div>
</div>
);
return (
<div className={styles.navbar}>
<i>Meuartelie</i>
<NavMenuIcon className={styles.hamburger} onClick={toggleMenu} />
<Menu toggle={menu}></Menu>
</div>
);
}
export default Navbar;
这是 css
@import './variables.scss';
.navbar {
box-sizing: border-box;
position: fixed;
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
padding: 5%;
z-index: 5;
}
.menu__container {
font-family: 'Jokerman Std';
position: absolute;
top: -300px;
left: 0;
width: 100vw;
height: 300px;
z-index: -1;
background-image: url("../public/png/welcome_mobile.png");
background-color: black;
background-size: cover;
overflow: hidden;
transition: 850ms;
}
.menu__content {
font-size: 2rem;
display: flex;
align-items: center;
justify-content: center;
margin-top: 20%;
}
.menu__container.toggle {
top: 0;
transition: all 1s ease-in;
}
.menu__list {
width: 80%;
text-align: center;
list-style: none;
padding: 0px;
line-height: 2;
margin: 0px;
margin-bottom: 20px;
}
.menu__item {
transition: padding-left .7s ease, color .7s ease;
/* underline css*/
text-decoration: none;
position: relative;
}
.menu__item:hover {
color: $base-color;
padding-left: 20px;
cursor: pointer;
}
.menu__item:after {
content: '';
height: 2px;
/* adjust this to move up and down. you may have to adjust the line height of the paragraph if you move it down a lot. */
bottom: 0px;
/* center - (optional) use with adjusting width */
margin: 0 auto;
left: 0;
right: 0;
width: 40%;
background: #fff;
/* optional animation */
-o-transition:.5s;
-ms-transition:.5s;
-moz-transition:.5s;
-webkit-transition:.5s;
transition: .5s;
}
/* optional hover classes used with anmiation */
.menu__item:hover:after {
background: $base-color;
}
.hamburger {
z-index: 1000;
cursor: pointer;
}
这与 React 如何确定要重新渲染的内容有关。
每当 React 在渲染树中确定一个新引用时,它会自动重新渲染该元素。
CSS 另一方面,转换将从起始状态转换为结束状态。
如果开始和结束相同,则不应用过渡。
那么实际发生了什么:
- 按钮被点击。
- 渲染树已修改。具体来说
const Menu
被分配了一个新的引用。
- React 从 dom 中删除了所有旧元素,在本例中为
<div class="menu__container">
- React 将新元素添加到 dom,在本例中为
<div class="menu_container toggle">
- 浏览器呈现元素并应用初始 css 规则。该元素接收
top:0
作为初始状态。由于删除了旧元素,因此没有过渡。
如何修复:
确保 React 知道我们仍在渲染相同的元素,因此它将更新现有的 dom 元素,而不是删除和添加新元素。
有几种方法可以做到这一点,推荐的方法是从渲染函数中提取元素。然后引用变为静态
//extracted from render function:
const Menu = (props) => (
<div key='1' className={ props.toggle ? `menu__container toggle` : `menu__container` }>
<div className={'menu__content'}>
<ul className={'menu__list'}>
<li className={'menu__item'} onClick={props.toggleMenu}> <Link href="/"><span>Home</span></Link></li>
<li className={'menu__item'} onClick={props.toggleMenu}> <Link href="/services"><span>Services</span></Link></li>
<li className={'menu__item'} onClick={props.toggleMenu}> <Link href="/services#contact"><span>Contact</span></Link></li>
</ul>
</div>
</div>
);
function Navbar() {
const [menu, setToggle] = useState(false);
const toggleMenu = () => setToggle(!menu);
return (
<div className={'navbar'}>
<i>Meuartelie</i>
<button className={'hamburger'} onClick={toggleMenu} >☰</button>
<Menu toggle={menu} toggleMenu={toggleMenu}></Menu>
</div>
);
}
有时从渲染函数中提取组件不方便,例如如果组件是动态的created/determined。
然后,您可以使用钩子 useMemo
告诉 React 存储引用,并且仅在依赖数组中的道具发生变化时才重新计算该值。
function NavbarAndMenuMemoized() {
const [menu, setToggle] = useState(false);
const toggleMenu = () => setToggle(!menu);
// useMemo will not recalculate Menu each render, instead it will keep the same reference for Menu.
const Menu = useMemo(()=>{
return(props) => (
<div key='1' className={ props.toggle ? `menu__container toggle` : `menu__container` }>
<div className={'menu__content'}>
<ul className={'menu__list'}>
<li className={'menu__item'} onClick={toggleMenu}> <Link href="/"><span>Home</span></Link></li>
<li className={'menu__item'} onClick={toggleMenu}> <Link href="/services"><span>Services</span></Link></li>
<li className={'menu__item'} onClick={toggleMenu}> <Link href="/services#contact"><span>Contact</span></Link></li>
</ul>
</div>
</div>
);
}, [])
return (
<div className={'navbar'}>
<i>Meuartelie</i>
<button className={'hamburger'} onClick={toggleMenu} >☰</button>
<Menu toggle={menu} ></Menu>
</div>
);
}
我在使用应该从顶部滑入的叠加导航栏时遇到问题。
不知道为什么,但如果我单击汉堡菜单,则会应用正确的 css class,但不会发生转换(它的作用类似于 show/hidden) . 但是,如果我从开发工具更改顶部 属性 值,它会按预期工作。
我错过了什么?
这是反应组件
import React, { useState } from "react";
import NavMenuIcon from "../../public/svg/nav.svg";
import styles from '../../styles/Navbar.module.scss'
import Link from 'next/link'
function Navbar() {
const [menu, setToggle] = useState(false);
const toggleMenu = () => setToggle(!menu);
const Menu = props => (
<div className={ props.toggle ? `${styles.menu__container} ${styles.toggle}` : `${styles.menu__container}` }>
<div className={styles.menu__content}>
<ul className={styles.menu__list}>
<li className={styles.menu__item} onClick={toggleMenu}> <Link href="/"><span>Home</span></Link></li>
<li className={styles.menu__item} onClick={toggleMenu}> <Link href="/services"><span>Services</span></Link></li>
<li className={styles.menu__item} onClick={toggleMenu}> <Link href="/services#contact"><span>Contact</span></Link></li>
</ul>
</div>
</div>
);
return (
<div className={styles.navbar}>
<i>Meuartelie</i>
<NavMenuIcon className={styles.hamburger} onClick={toggleMenu} />
<Menu toggle={menu}></Menu>
</div>
);
}
export default Navbar;
这是 css
@import './variables.scss';
.navbar {
box-sizing: border-box;
position: fixed;
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
padding: 5%;
z-index: 5;
}
.menu__container {
font-family: 'Jokerman Std';
position: absolute;
top: -300px;
left: 0;
width: 100vw;
height: 300px;
z-index: -1;
background-image: url("../public/png/welcome_mobile.png");
background-color: black;
background-size: cover;
overflow: hidden;
transition: 850ms;
}
.menu__content {
font-size: 2rem;
display: flex;
align-items: center;
justify-content: center;
margin-top: 20%;
}
.menu__container.toggle {
top: 0;
transition: all 1s ease-in;
}
.menu__list {
width: 80%;
text-align: center;
list-style: none;
padding: 0px;
line-height: 2;
margin: 0px;
margin-bottom: 20px;
}
.menu__item {
transition: padding-left .7s ease, color .7s ease;
/* underline css*/
text-decoration: none;
position: relative;
}
.menu__item:hover {
color: $base-color;
padding-left: 20px;
cursor: pointer;
}
.menu__item:after {
content: '';
height: 2px;
/* adjust this to move up and down. you may have to adjust the line height of the paragraph if you move it down a lot. */
bottom: 0px;
/* center - (optional) use with adjusting width */
margin: 0 auto;
left: 0;
right: 0;
width: 40%;
background: #fff;
/* optional animation */
-o-transition:.5s;
-ms-transition:.5s;
-moz-transition:.5s;
-webkit-transition:.5s;
transition: .5s;
}
/* optional hover classes used with anmiation */
.menu__item:hover:after {
background: $base-color;
}
.hamburger {
z-index: 1000;
cursor: pointer;
}
这与 React 如何确定要重新渲染的内容有关。
每当 React 在渲染树中确定一个新引用时,它会自动重新渲染该元素。
CSS 另一方面,转换将从起始状态转换为结束状态。
如果开始和结束相同,则不应用过渡。
那么实际发生了什么:
- 按钮被点击。
- 渲染树已修改。具体来说
const Menu
被分配了一个新的引用。 - React 从 dom 中删除了所有旧元素,在本例中为
<div class="menu__container">
- React 将新元素添加到 dom,在本例中为
<div class="menu_container toggle">
- 浏览器呈现元素并应用初始 css 规则。该元素接收
top:0
作为初始状态。由于删除了旧元素,因此没有过渡。
如何修复:
确保 React 知道我们仍在渲染相同的元素,因此它将更新现有的 dom 元素,而不是删除和添加新元素。
有几种方法可以做到这一点,推荐的方法是从渲染函数中提取元素。然后引用变为静态
//extracted from render function:
const Menu = (props) => (
<div key='1' className={ props.toggle ? `menu__container toggle` : `menu__container` }>
<div className={'menu__content'}>
<ul className={'menu__list'}>
<li className={'menu__item'} onClick={props.toggleMenu}> <Link href="/"><span>Home</span></Link></li>
<li className={'menu__item'} onClick={props.toggleMenu}> <Link href="/services"><span>Services</span></Link></li>
<li className={'menu__item'} onClick={props.toggleMenu}> <Link href="/services#contact"><span>Contact</span></Link></li>
</ul>
</div>
</div>
);
function Navbar() {
const [menu, setToggle] = useState(false);
const toggleMenu = () => setToggle(!menu);
return (
<div className={'navbar'}>
<i>Meuartelie</i>
<button className={'hamburger'} onClick={toggleMenu} >☰</button>
<Menu toggle={menu} toggleMenu={toggleMenu}></Menu>
</div>
);
}
有时从渲染函数中提取组件不方便,例如如果组件是动态的created/determined。
然后,您可以使用钩子 useMemo
告诉 React 存储引用,并且仅在依赖数组中的道具发生变化时才重新计算该值。
function NavbarAndMenuMemoized() {
const [menu, setToggle] = useState(false);
const toggleMenu = () => setToggle(!menu);
// useMemo will not recalculate Menu each render, instead it will keep the same reference for Menu.
const Menu = useMemo(()=>{
return(props) => (
<div key='1' className={ props.toggle ? `menu__container toggle` : `menu__container` }>
<div className={'menu__content'}>
<ul className={'menu__list'}>
<li className={'menu__item'} onClick={toggleMenu}> <Link href="/"><span>Home</span></Link></li>
<li className={'menu__item'} onClick={toggleMenu}> <Link href="/services"><span>Services</span></Link></li>
<li className={'menu__item'} onClick={toggleMenu}> <Link href="/services#contact"><span>Contact</span></Link></li>
</ul>
</div>
</div>
);
}, [])
return (
<div className={'navbar'}>
<i>Meuartelie</i>
<button className={'hamburger'} onClick={toggleMenu} >☰</button>
<Menu toggle={menu} ></Menu>
</div>
);
}