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}
/>
}
)
}
}`
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 trying to get my sidebar to slide in by toggling the state of my Sidebar (going from open: false to open: true) on component load. I'm doing this is an attempt to get a slide-in effect once the user loads the page.
Unfortunately, the way I'm currently doing this, it appears as though the state is changed and the component re-rendered essentially immediately, preventing the slide-in effect from happening. Here's my code:
export default class Sidebar extends React.Component {
constructor(props) {
super(props);
this.state = {open: false};
}
componentDidMount() {
this.setState({open: !this.state.open})
}
handleToggle = () => this.setState({open: !this.state.open});
render() {
return (
<div>
<Drawer width={'25%'} open={this.state.open}>
<AppBar title="Wealth Management" onLeftIconButtonClick={this.handleToggle}/>
<MenuItem>Menu Item</MenuItem>
<MenuItem>Menu Item 2</MenuItem>
</Drawer>
</div>
);
}
}
How do you get this to work? Am I updating on the wrong lifecycle method?
It is called CSS transition.
You could have a look at this medium post or check the MDN documentation.
We use a sidebar that slide open or closed based on user settings, the trick is to set a negative margin on the base css-class "sidebar" in this case.
Then add/remove the toggle class (on render or button event). That is all that is required to get a smoth animation on load
css-classes:
.sidebar {
z-index: 1000;
position: fixed;
left: 200px;
top: 0px;
bottom: 0px;
width: 250px;
margin-left: -250px;
-webkit-transition: all 0.5s ease;
-moz-transition: all 0.5s ease;
-o-transition: all 0.5s ease;
transition: all 0.5s ease;
}
.sidebarToggled {
width: 65px;
}
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
I am using Vue 2 and attempting to include CSS animations on elements that are created and destroyed frequently. Below is an example of my code:
export default {
name: 'MyElement',
methods: {
enterStart: function (el) {
console.log('about to enter');
el.classList.add('testing-enter');
},
enter: function (el) {
console.log('entered');
},
leaveStart: function (el) {
console.log('starting to leave!');
},
leave: function (el) {
console.log('leaving!');
},
}
};
.testing-enter {
animation: enter .2s;
}
.testing-leave {
animation: leave .2s;
}
#keyframes enter {
0% {
opacity: 0;
transform: scale(0);
}
100% {
opacity: 1;
transform: scale(1);
}
}
#keyframes leave {
0% {
opacity: 1;
transform: scale(1);
}
100% {
opacity: 0;
transform: scale(0);
}
}
<template>
<div>
<transition
#before-enter="enterStart"
#enter="enter"
#leave="leaveStart"
#leave-active="leave"
appear
>
<div>My element is here!</div>
</transition>
</div>
</template>
First off, none of this works unless I include appear in my <transition ...> element. I know that this makes the transition happen on initial rendering, but I want them to happen any time the element is created or destroyed.
Next, in my console. I can see enterStart and enter both run, but leaveStart and leave never run, even when the elements are destroyed. What am I doing wrong?
The element inside the transition needs a state (show or hide). Also your transition needs a name that must much the transition in the CSS and it should be named with
name="transitionName"
e.g:
new Vue({
el: "#app",
data: function() {
return {
showThisElement: false
}
},
methods: {
toggleShow: function() {
this.showThisElement = !this.showThisElement
}
}
});
.testing-enter-active {
animation: enter .2s;
}
.testing-leave-active {
animation: leave .2s;
}
#keyframes enter {
0% {
opacity: 0;
transform: scale(0);
}
100% {
opacity: 1;
transform: scale(1);
}
}
#keyframes leave {
0% {
opacity: 1;
transform: scale(1);
}
100% {
opacity: 0;
transform: scale(0);
}
}
<div id="app">
<div #click="toggleShow">Show/Hide</div>
<transition
name="testing">
<div v-if="showThisElement">My element is here!</div>
</transition>
</div>
In the codepen, click on 'show/hide' to toggle the transition.
http://codepen.io/anon/pen/WpZPJp
Problem solved!
So I took out the transition from the individual component and created a transition-group instead around the container component that rendered them.
Then, after a bit more reading I realized I wanted to add the mode="out-in" field to my transition-group so that the leaving components fully animate before the new ones are rendered.
I also looked at the HTML when the animations were supposed to be happening to see what classes Vue added. It looks like Vue added v-enter-active, v-enter-to, and v-leave-to. Instead of customizing any names I just stuck with those classes and added my animations to them in the styling.
Hopefully if anybody else wants a similar effect this helps them decrease their stress levels a bit...