使用 React,我怎样才能让模态动画出现,而不是已经在 DOM 中?
Using React, how can I make the modal do an animated appear, without already being in the DOM?
我有一个模式,它会在按下按钮时出现,并在显示时显示动画。
这工作正常,但只有当按下按钮时模态代码已经在 DOM 中。
您可以在此处查看示例:
https://codesandbox.io/s/loving-dan-7fwrkr
这就是问题所在:如果按钮将模态代码添加到 DOM,那么模态只会出现而没有动画。
我花了很多时间尝试各种方法来完成这项工作,我能想到的最好的方法是使用 window.setTimeout 在模态代码添加到 [= 后 200 毫秒触发动画38=]。我不喜欢这样的解决方案,因为它看起来像是一个 hack - 我不清楚为什么这样的 hack 会起作用。
下面的例子展示了这两种情况。
没有注释的代码,动画有效。
使用注释代码,模态仅显示而没有动画。
如果有人知道如何解决这个问题,我们将不胜感激。
我的具体目标是在按下按钮使其出现之前 DOM 中不包含模态代码。
我已经非常努力地制作了下面可能的最小示例,但它仍然相当大,我深表歉意。如果您建议在仍然相关的情况下进一步削减它,请告诉我。
import ReactDOM from 'react-dom';
import React, {useState} from 'react';
const theStyle = `
.md-modal {
position: fixed;
top: 50%;
left: 50%;
width: 50%;
height: auto;
z-index: 2000;
visibility: hidden;
transform: translateX(-50%) translateY(-50%);
}
.md-show {
visibility: visible;
}
.md-overlay {
position: fixed;
width: 100%;
height: 100%;
visibility: hidden;
top: 0;
left: 0;
z-index: 1000;
opacity: 0;
background: rgba(143, 27, 15, 0.8);
transition: all 0.3s;
}
.md-show ~ .md-overlay {
opacity: 1;
visibility: visible;
}
.md-content {
color: #fff;
background: #e74c3c;
position: relative;
border-radius: 3px;
margin: 0 auto;
}
.md-content h3 {
opacity: 0.8;
}
.md-effect-1 .md-content {
transform: scale(0.7);
opacity: 0;
transition: all 0.3s;
}
.md-show.md-effect-1 .md-content {
transform: scale(1);
opacity: 1;
}
`
function App() {
const [getVisible, setVisible] = useState(false);
/*
THE MODAL APPEAR ANIMATION DOES NOT WORK WHEN THIS IS UNCOMMENTED
if (!getVisible) {
return (
<button onClick={() => setVisible(true)}>
show modal
</button>)
}
*/
return (
<>
<style>
{theStyle}
</style>
<div className={`md-modal md-effect-1 ${(getVisible) && "md-show"}`}>
<div className="md-content">
This is a modal window.<br/>
<button onClick={() => setVisible(false)} className="md-close">close</button>
</div>
</div>
<div onClick={() => setVisible(false)} className="md-overlay"/>
<button onClick={() => setVisible(true)} className="md-trigger">
show modal
</button>
</>
);
}
ReactDOM.render(<App/>, document.getElementById('root'));
我遇到过类似的问题,原因是当您将模态添加到 DOM 时,如果模态立即获得可见的 end value
,transition
不会触发.
我通过将过渡放入 @keyframes
动画中解决了这个问题。然后,将模态添加到 DOM 后,使用 classList.add()
触发动画。
像这样
.modal {
opacity:0
}
.animated {
animation: showModal 1s forwards easeOut
}
@keyframes showModal {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
模态添加后的JS DOM:
myModel.classList.add("animated")
对未来的自己的自我回答。
以@Kokodoko 的回答作为我的起点,我更好地理解了动画在 CSS/JS 中的工作原理,并完全重写了我的模态,现在它可以做我想要的了。
代码如下:
import ReactDOM from 'react-dom';
import React, {useState} from 'react';
const theStyle = `
.animated {
animation: showModal .2s forwards
}
@keyframes showModal {
from {
opacity: 0;
transform: scale(0.7);
}
to {
opacity: 1;
transform: scale(1);
}
}
.modalOverlay {
z-index: 1500;
background: rgba(40,91,218,0.5); /* you must use this and not opacity because opacity changes the front color */
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
margin: 0;
padding: 0;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: center;
align-content: stretch;
align-items: center;
}
.modalContainer {
z-index: 1600;
order: 0;
flex: 0 1 auto;
align-self: auto;
}
#modalContent {
z-index: 1700;
opacity: 0;
color: #fff;
width: 500px;
height: 200px;
background: #e74c3c;
position: relative;
border-radius: 3px;
margin: 0 auto;
}
`
function Button() {
const [getVisible, setVisible] = useState(false);
return (
<div>
<button onClick={() => setVisible(true)}>
show modal
</button>
{(getVisible) && <Modal setVisible={setVisible}/>}
</div>
)
}
function Modal({setVisible}) {
React.useEffect(
//() => window.setTimeout(document.getElementById("modalContent").classList.add("animated"))
() => document.getElementById("modalContent").classList.add("animated")
, [])
const handleClickOnOverlay = (e) => {
// clicks on the are sent through to the background so we must prevent that
e.stopPropagation()
setVisible(false)
}
const handleClickOnContainer = (e) => {
// clicks on the modal are sent through to the background so we must prevent that
e.stopPropagation()
}
const handleClickOnModal = (e) => {
console.log('clicked on modal')
}
return (
<>
<style>
{theStyle}
</style>
<div onClick={handleClickOnOverlay} className="modalOverlay">
<div className={`modalContainer`} onClick={handleClickOnContainer}>
<div id="modalContent" onClick={handleClickOnModal}>
This is a modal window.<br/>
<button onClick={() => setVisible(false)} className="md-close">close</button>
</div>
</div>
</div>
</>
);
}
ReactDOM.render(<Button/>, document.getElementById('root'));
我有一个模式,它会在按下按钮时出现,并在显示时显示动画。
这工作正常,但只有当按下按钮时模态代码已经在 DOM 中。
您可以在此处查看示例: https://codesandbox.io/s/loving-dan-7fwrkr
这就是问题所在:如果按钮将模态代码添加到 DOM,那么模态只会出现而没有动画。
我花了很多时间尝试各种方法来完成这项工作,我能想到的最好的方法是使用 window.setTimeout 在模态代码添加到 [= 后 200 毫秒触发动画38=]。我不喜欢这样的解决方案,因为它看起来像是一个 hack - 我不清楚为什么这样的 hack 会起作用。
下面的例子展示了这两种情况。
没有注释的代码,动画有效。
使用注释代码,模态仅显示而没有动画。
如果有人知道如何解决这个问题,我们将不胜感激。
我的具体目标是在按下按钮使其出现之前 DOM 中不包含模态代码。
我已经非常努力地制作了下面可能的最小示例,但它仍然相当大,我深表歉意。如果您建议在仍然相关的情况下进一步削减它,请告诉我。
import ReactDOM from 'react-dom';
import React, {useState} from 'react';
const theStyle = `
.md-modal {
position: fixed;
top: 50%;
left: 50%;
width: 50%;
height: auto;
z-index: 2000;
visibility: hidden;
transform: translateX(-50%) translateY(-50%);
}
.md-show {
visibility: visible;
}
.md-overlay {
position: fixed;
width: 100%;
height: 100%;
visibility: hidden;
top: 0;
left: 0;
z-index: 1000;
opacity: 0;
background: rgba(143, 27, 15, 0.8);
transition: all 0.3s;
}
.md-show ~ .md-overlay {
opacity: 1;
visibility: visible;
}
.md-content {
color: #fff;
background: #e74c3c;
position: relative;
border-radius: 3px;
margin: 0 auto;
}
.md-content h3 {
opacity: 0.8;
}
.md-effect-1 .md-content {
transform: scale(0.7);
opacity: 0;
transition: all 0.3s;
}
.md-show.md-effect-1 .md-content {
transform: scale(1);
opacity: 1;
}
`
function App() {
const [getVisible, setVisible] = useState(false);
/*
THE MODAL APPEAR ANIMATION DOES NOT WORK WHEN THIS IS UNCOMMENTED
if (!getVisible) {
return (
<button onClick={() => setVisible(true)}>
show modal
</button>)
}
*/
return (
<>
<style>
{theStyle}
</style>
<div className={`md-modal md-effect-1 ${(getVisible) && "md-show"}`}>
<div className="md-content">
This is a modal window.<br/>
<button onClick={() => setVisible(false)} className="md-close">close</button>
</div>
</div>
<div onClick={() => setVisible(false)} className="md-overlay"/>
<button onClick={() => setVisible(true)} className="md-trigger">
show modal
</button>
</>
);
}
ReactDOM.render(<App/>, document.getElementById('root'));
我遇到过类似的问题,原因是当您将模态添加到 DOM 时,如果模态立即获得可见的 end value
,transition
不会触发.
我通过将过渡放入 @keyframes
动画中解决了这个问题。然后,将模态添加到 DOM 后,使用 classList.add()
触发动画。
像这样
.modal {
opacity:0
}
.animated {
animation: showModal 1s forwards easeOut
}
@keyframes showModal {
from {
opacity: 0;
}
to {
opacity: 1;
}
}
模态添加后的JS DOM:
myModel.classList.add("animated")
对未来的自己的自我回答。
以@Kokodoko 的回答作为我的起点,我更好地理解了动画在 CSS/JS 中的工作原理,并完全重写了我的模态,现在它可以做我想要的了。
代码如下:
import ReactDOM from 'react-dom';
import React, {useState} from 'react';
const theStyle = `
.animated {
animation: showModal .2s forwards
}
@keyframes showModal {
from {
opacity: 0;
transform: scale(0.7);
}
to {
opacity: 1;
transform: scale(1);
}
}
.modalOverlay {
z-index: 1500;
background: rgba(40,91,218,0.5); /* you must use this and not opacity because opacity changes the front color */
position: fixed;
width: 100%;
height: 100%;
top: 0;
left: 0;
margin: 0;
padding: 0;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
justify-content: center;
align-content: stretch;
align-items: center;
}
.modalContainer {
z-index: 1600;
order: 0;
flex: 0 1 auto;
align-self: auto;
}
#modalContent {
z-index: 1700;
opacity: 0;
color: #fff;
width: 500px;
height: 200px;
background: #e74c3c;
position: relative;
border-radius: 3px;
margin: 0 auto;
}
`
function Button() {
const [getVisible, setVisible] = useState(false);
return (
<div>
<button onClick={() => setVisible(true)}>
show modal
</button>
{(getVisible) && <Modal setVisible={setVisible}/>}
</div>
)
}
function Modal({setVisible}) {
React.useEffect(
//() => window.setTimeout(document.getElementById("modalContent").classList.add("animated"))
() => document.getElementById("modalContent").classList.add("animated")
, [])
const handleClickOnOverlay = (e) => {
// clicks on the are sent through to the background so we must prevent that
e.stopPropagation()
setVisible(false)
}
const handleClickOnContainer = (e) => {
// clicks on the modal are sent through to the background so we must prevent that
e.stopPropagation()
}
const handleClickOnModal = (e) => {
console.log('clicked on modal')
}
return (
<>
<style>
{theStyle}
</style>
<div onClick={handleClickOnOverlay} className="modalOverlay">
<div className={`modalContainer`} onClick={handleClickOnContainer}>
<div id="modalContent" onClick={handleClickOnModal}>
This is a modal window.<br/>
<button onClick={() => setVisible(false)} className="md-close">close</button>
</div>
</div>
</div>
</>
);
}
ReactDOM.render(<Button/>, document.getElementById('root'));