I need to show a quick flash message for few seconds after ajax success. The message should be auto hidden after 1/2 seconds.
As reactjs does not promote for dom manipulation, I want to achieve the same using css3 animation.
Here is simple demo i prepared,
When you click on the button for the first time it shows the message, but on the subsequent click it does not do anything.
class Test extends React.Component {
state = {
show: false
}
showFlashMessage() {
this.setState({
show: true
})
}
render() {
return ( <
div >
<
div className = {
`message ${this.state.show ? "fadeOut": ""}`
} > Hello < /div> <
button id = "but"
onClick = {
this.showFlashMessage.bind(this)
} > Click Me < /button> <
/div>
)
}
}
ReactDOM.render( <
Test / > ,
document.getElementById('container')
);
#keyframes FadeAnimation {
0% {
opacity: 1;
visibility: visible;
}
100% {
opacity: 0;
visibility: hidden;
}
}
.fadeOut {
animation: FadeAnimation 1s ease-in .4s forwards;
}
.message {
visibility: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
JS Fiddle: http://jsfiddle.net/2kqv6fo7/9/
I am not sure if there is any other alternatives except css3 .
Any suggestion?
As I have answered to your previous question also, its the same, try handling with the state change. Since you have mentioned you are using a toggle button in your previous question, i have used a similar checkbox which will be helpful for you.
I ve made the code work only when the toggle is checked, I assume you would need to show some message on toggle off too. Do the same, by toggling between two classes. And try to avoid inline function bindings, cuz it will trigger for every render. Instead go for constructor, which will get executed just once when the component is loaded.
class Test extends React.Component {
constructor() {
super();
this.state = {
show: false,
showClass: undefined
}
this.showFlashMessage = (event) => this._showFlashMessage(event);
}
_showFlashMessage(event) {
this.setState({
show: !this.state.show,
showClass: (!this.state.show ? "fadeOut": "fadeOut2")
})
}
render() {
return ( <
div >
<
div className = {
`message ${this.state.showClass}`
} > Hello < /div>
<button id ="but" onClick = {this.showFlashMessage} > Click Me </button><
/div>
)
}
}
ReactDOM.render( <
Test / > ,
document.getElementById('container')
);
#keyframes FadeAnimation {
0% {
opacity: 1;
visibility: visible;
}
100% {
opacity: 0;
visibility: hidden;
}
}
#keyframes FadeAnimation2 {
0% {
opacity: 1;
visibility: visible;
}
100% {
opacity: 0;
visibility: hidden;
}
}
.fadeOut {
opacity: 1;
visibility: visible;
animation: FadeAnimation 1s ease-in .2s forwards;
}
.fadeOut2 {
opacity: 1;
visibility: visible;
animation: FadeAnimation2 1s ease-in .2s forwards;
}
.message {
opacity: 0;
visibility: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="container">
<!-- This element's contents will be replaced with your component. -->
</div>
You should capture the end of the animation and change the state there.
See this:
How can I animate a react.js component onclick and detect the end of the animation
Related
I have MenuBody component which is showed if state of isOpen is true. I want to add transition when menu is opening and also when is closing. Animation for opening is working but on closing it doesn't work.
This is my code
const [isOpen, setIsOpen] = useState(false);
<button onClick={()=>setIsOpen(!isOpen)}>open menu</button>
{isOpen ? <MenuBody /> : null}
....
const MenuBody = () => {
return (
<div className={styles.menuBody}>
//some content here to display when it's opened
</div>
)}
CSS
#-webkit-keyframes menuBody {
0% { opacity: 0; }
100% { opacity: 1; }
}
#-moz-keyframes menuBody {
0% { opacity: 0; }
100% { opacity: 1; }
}
#-o-keyframes menuBody {
0% { opacity: 0; }
100% { opacity: 1; }
}
#keyframes menuBody {
0% { opacity: 0; }
100% { opacity: 1; }
}
.menuBody {
....
-webkit-animation: menuBody 0.5s ease-in-out;
-moz-animation: menuBody 0.5s ease-in-out;
-o-animation: menuBody 0.5s ease-in-out;
animation: menuBody 0.5s ease-in-out;
}
I would appreciate any kind of help
Thanks
I think that the problem might be in the way how you wrote MenuBody or in isOpen since you didn't provide any code regarding closing I am gonna assume, that maybe it's missing. So I would recommend adding some button into the MenuBody which will be able to change the state of isOpen.
EDIT #1
I would recommend adding and removing a class that says if Modal is open or closed. I would recommend using this method described here: https://erikmartinjordan.com/react-on-disappear-animation
I trying animate a component when rendering and unmount it in React, but unsuccessfully.
Component to animate
One of the ways I've tried was create a state (fade) with the class of animation property, when rendering, the class is fadeIn and on will unmount the class is fadeOut
const handlePokemonDetails = async () => {
if (pokemonDetails === true){
setFade(styles.fadeOut)
await timeout(500)
}
else{
setFade(styles.fadeIn)
}
setPokemonDetails(!pokemonDetails)
}
...
<div className={pokemonDetails ? styles.pokemonDetails + " " + fade : ""}></div>
SASS code:
.pokemonDetails
position: absolute
top: 0
z-index: 3
display: flex
width: 35vmin
height: 39.5vmin
flex-direction: column
background-color: rgba(210, 210, 210, 0.85)
border-radius: 10px
#keyframes fade
0%
height: 0vmin
100%
height: 39.5vmin
.fadeIn
animation: fade 0.5s normal
.fadeOut
animation: fade 0.5s reverse
But, when I change state to other class, the component don't apply this new class animation style. Does anyone know how I can fix this or another way to do it?
Good question. I just finished writing hook for React that helps with that
look at https://github.com/perymimon/React-Anime-Manager
Just publish ver2-alpha so the documentation is still lake.
import {useAnimeManager,ADDED,REMOVED} from "#perymimon/react-anime-manager"
const phase2class = {
[ADDED]: "fadeIn",
[REMOVED]: "fadeOut",
}
function Pokemons({pokemones}){
const pokemonesStates = useAnimeManager(pokemones);
return (
<pokemones>
{ pokemonesStates.map(state =>(
<div className={phase2class[state.phase] } onAnimationEnd={state.done}/>
)
)}
</pokemones>
)
}
This is the exact use case for react-transition-group.
Wrap your component inside <CSSTransition>
function App() {
const [inProp, setInProp] = useState(false);
return (
<div>
<CSSTransition in={inProp} timeout={200} classNames="my-node">
<div>
{"I'll receive my-node-* classes"}
</div>
</CSSTransition>
<button type="button" onClick={() => setInProp(true)}>
Click to Enter
</button>
</div>
);
}
Then add CSS to your classes. If you set className of CSSTransition to my-node, you will have 4 classes to style:
.my-node-enter {
opacity: 0;
}
.my-node-enter-active {
opacity: 1;
transition: opacity 200ms;
}
.my-node-exit {
opacity: 1;
}
.my-node-exit-active {
opacity: 0;
transition: opacity 200ms;
}
i m using reactjs and i have a datepicker component that i hide when the user clicks outside of the component element.
the code snippet is like so:
`
class DatePicker extends Component{
constructor(props){
super(props)
state= { focus: false } // when focus: false i hide the dates component
}
.......
render(){
const { focus } = this.state
return(
<input type="text" placeholder="start date">
<input type="text" placeholder="start date">
{focus && <DatesContainer ...props>} // if focus==false i dont show the <DatesContainer> component.I need to hide it with fade out or something.
)
}
}`
So when the user clicks outside of the <DatesContainer/> the state.focus updates to false, the re-renders and, this time, the <DatesContainer/> is not render at all, so far so good. But i need to hide it with a 0.3s animation.
Any opinions on that ?
The difference between my answer and previous is you're able to set specific actions to certain animation keyframes.
The key point is to provide animation name as a function parameter
CSS
.Modal {
position: fixed;
top: 30%;
left: 25%;
transition: all 0.3s ease-out;
}
.ModalOpen {
animation: openModal 0.4s ease-out forwards;
}
.ModalClosed {
animation: closeModal 0.4s ease-out forwards;
}
#keyframes openModal {
0% { transform: translateY(-100%); }
100% { transform: translateY(0); }
}
#keyframes closeModal {
0% { transform: translateY(0); }
100% { transform: translateY(-100%);}
}
JS
const modal = ({
isShown, isMounted,
initiateUnmountAction, unmountAction
}) => {
const cssClasses = [
"Modal",
props.isShown ? "ModalOpen" : "ModalClosed"
];
return (
<Fragment>
{isMounted && <div className={cssClasses.join(' ')}
onAnimationEnd={event =>
{event.animationName == "closeModal" && unmountAction}
}>
<h1>A Modal</h1>
<button className="Button" onClick={initiateUnmountAction}>
Dismiss
</button>
</div>}
</Fragment>
);
};
I'd update the state to have a transitioning property. Using CSS animations or transitions, and some conditional rendering, we can apply a new class to the DatesContainer, detect when an animation is finished and change the focus state to false.
You'll have to set transitioning to true in the state whenever you click outside it.
It may look something like this:
CSS
#keyframes fadeOut {
0% {
opacity: 1;
}
100% {
opacity: 0;
}
}
.fading {
animation-name: fadeOut;
animation-duration: 0.3s;
animation-fill-mode: forwards;
will-change: opacity;
}
JS
//import your styles here
class DatePicker extends Component{
constructor(props){
super(props)
state= { focus: false, transitioning: false }
}
handleFade() {
this.setState({transitioning: false})
}
render(){
const { focus, transitioning } = this.state
return(
<input type="text" placeholder="start date">
<input type="text" placeholder="start date">
{focus && <DatesContainer
className={transitioning === true ? "fading" : null}
onAnimationEnd={transitioning === true ? () => this.handleFade() : null}
/>
}
)
}
}`
I am animating the entry and exit of an array of items using TransitionGroup and CSSTransition (with a fade effect). I would like the items to appear with a slight delay between them instead of all at the same time. Note that the delay can be lower than the duration of the animation.
With my current code, all the items are fading-in at the same time (as expected). In my render function, I have the following to animate the entry and exit of my components:
<TransitionGroup>
items.map((item,index) => (
<CSSTransition
key={item.id}
timeout={1000}
classNames="fade"
<ItemPreview item={item} />
</CSSTransition>
))
</TransitionGroup>
And the CSS:
.fade-enter{
opacity: 0;
visibility: hidden;
transition: all ease 1s;
}
.fade-enter.fade-enter-active{
opacity: 1;
visibility: visible;
transition: all ease 1s;
}
.fade-exit {
visibility: visible;
opacity: 0;
}
.fade-exit.fade-exit-active{
opacity: 0;
visibility: hidden;
transition: all ease 1s;
}
How would I go about adding a different delay for each item?
I solved my issue by adding a transitionDelay in the style of my ItemPreview as well as changing the timeout dynamically for each item.
The tricky part is to calculate the actual delay for each item, especially when loading new items afterwards. I ended up adding a isNew field in my items in the reducer which is set to true only for newly loaded items. This is not ideal as it involves changing my data just for animations.
render(){
const { items } = this.props;
let delay_index = -1;
let delay_jump = 100;
return (
<TransitionGroup>
items.map((item,index) => {
delay_index += offer.isNew ? 1 : 0;
const delay = Math.max(0, delay_index*100);
return (
<CSSTransition
key={item.id}
timeout={1000 + delay}
classNames="fade">
<ItemPreview
item={item}
style={{transitionDelay: `${delay}ms`}} />
</CSSTransition>
)
})
</TransitionGroup>
)
}
I might be too late but I was facing the same issue with entering animation and my solution might be useful for someone else.
I'm using <Transition> and I've solved using a for loop in SCSS:
.fade {
opacity: 0;
visibility: hidden;
transition: opacity 0.5s ease-in, visibility 0.5s;
// adjust for loop to total number of elements
#for $i from 1 through 5 {
&:nth-child(#{$i}n) {
transition-delay: #{$i * 0.25}s;
}
}
&.entering,
&.entered {
opacity: 1;
visibility: visible;
}
}
So I found this solution Using CSS for fade-in effect on page load And I've used Method 2 with raw JavaScript. Here's my code sample
JavaScript
var fadeOnLoad = function () {
document.getElementById("wrapper").className += "load";
};
fadeOnLoad();
CSS
#wrapper {
opacity: 0;
transition: opacity 2s ease-in;
}
.load {
opacity: 1;
}
Link to the website where it doesn't work https://skidle.github.io/projects/weather
And this text is crossed out in Google Dev tools
try to define
opacity: 1 !important;
id selector has higher priority than class
Here is a snippet with clear process logic. Element is invisible until body got loaded. As soon as event body onload fired, element gets opacity: 1;
function fadeOnLoad() {
document.getElementById("wrapper").className = "";
};
#wrapper {
transition: opacity 2s ease-in;
}
.not_loaded {
opacity: 0;
}
<body onload="fadeOnLoad()">
<div id="wrapper" class="not_loaded">text</div>
</body>
Add important to your class attribute.
.load{
opcacity: 1 !important; //because you have id selector with opacity to 0.
}
As a good practice, try to avoid using IDs for styling.
Instead of defining the transition in the #wrapper selector, create a class containing the transition property like so:
.opacity-transition {
transition: opacity 2s ease-in;
}
Once the transition ends, this class will not be needed any more and can be removed.
Create another class to initially hide the #wrapper element. When this class is removed it will trigger the transition.
.hidden {
opacity: 0;
}
Code Snippet:
function fadeOnLoad() {
//Cache the selector.
var wrapper = document.getElementById("wrapper");
console.log(wrapper.className);
//Add event listener for transition end.
wrapper.addEventListener("transitionend", function() {
//Remove the class which is not needed anymore.
this.classList.remove("opacity-transition");
console.log(this.className);
});
//Remove hidden class to start the transition.
wrapper.classList.remove("hidden");
};
.opacity-transition {
transition: opacity 2s ease-in;
}
.hidden {
opacity: 0;
}
<body onload="fadeOnLoad()">
<div id="wrapper" class="opacity-transition hidden">
text</div>
</body>
JSFIDDLE