I have an overlayed view in my React Native app which I need to animate on and off screen when the user pushes a button. I know how to position the view and animate it but I can't work out how to trigger the animation.
My redux store has a very simple state with an isOpen flag saying whether the panel is open or closed. I map the state to the panel component's props and when the isOpen prop changes I want to trigger the open or close animation. Obviously if the user presses the toggle button mid animation the currently running animation needs to be cancelled.
This should be simple but I can't find any examples. Any help would be much appreciated.
React Native
To begin an animation on a change of props you can simply start your animation in componentDidUpdate. Here's an example:
componentDidUpdate(prevProps) {
if (this.props.isOpen !== prevProps.isOpen) {
this.state.animation.start();
}
}
Assuming your animation is defined in the component's state.
React (Browser):
[Not relevant to this question but potentially useful.]
A simple way to do this is using CSS transitions. What you can do is give the panel component a CSS class for closed (or open but I find using the closed/collapsed as a style easier because then the default is open).
Then in your panel's render:
render() {
const { isOpen } = this.props;
return <div className={ 'panel' + (isOpen ? '' : ' closed') }></div>
}
And in your CSS:
.panel {
/* some other styles */
transition: .5s ease-in;
}
.closed {
height: 0;
}
This way CSS can handle the animation logic and your concern of clicking the open/close button before the current animation has finished is addressed.
Here are the CSS transition docs: https://developer.mozilla.org/en-US/docs/Web/CSS/transition
Edit: A disadvantage of this method is that height must be explicitly set when the panel is open.
Here's a little example snippet:
function togglePanel() {
const panel = document.querySelector('div.panel');
if (panel.classList.contains('closed')) {
panel.classList.remove('closed');
} else {
panel.classList.add('closed');
}
}
.panel {
background-color: #00c0de;
height: 4rem;
overflow: hidden;
transition: .5s ease-in;
}
.closed {
height: 0;
}
<button onclick='togglePanel()'>Toggle Panel</button>
<div class='panel closed'>
<span>Hello, I'm the panel</span>
</div>
Related
I'm trying to implement on click scroll to top in react, but since I'm using custom scrollbar 2 I couldn't able to achieve it. The pageYoffset value is always 0 since I made the parent component max-height as 100vh and child component as scrollable. Here is what I got till now
<Scrollbars onScrollFrame={handleScroll}>
<ChildComponent/>
{
showButton && <button onClick={handleClick}><button/>
}
</Scrollbars>
In handleScroll
const {top} = values;
if(top > 0.02){
setShowButton(true);
}else{
setShowButton(false);
}
Depending on the top value I can show the button now I want to implement the scroll function when the button got clicked and scroll to the child component top
In handleClick I've tried to scroll it to top
window.scrollTo({ top: 0, behavior: "smooth" });
I've tried scrollIntoView with the top element as well it's not working.
I use react-modal to create a open modal form (similar create post modal form facebook).
My expect:
When modal is open: scrollbar of window still show but thumb disabled (user can't scroll)
When modal is close: scrollbar of window show and thumb show.
My problem:
When modal is open, scrollbar always on top (top = 0). I know because my css body { position: fixed } but I want modal look like my expect
Here is my codesandbox for my problem: https://codesandbox.io/s/scroll-modal-form-rw2sf
Sorry about my bad english and thanks for your help.
I set the style of class ReactModalPortal and adjust the z-index. In addition remove the style of body. There is a sandbox
/* index.css */
.ReactModalPortal {
height: 100%;
width: 100%;
position: absolute;
top: 0;
z-index: -1;
}
// App.js
...
const portal = document.querySelector(".ReactModalPortal");
portal.style.zIndex = 0;
...
const portal = document.querySelector(".ReactModalPortal");
portal.style.zIndex = -1;
Maybe it is not as your expectation, but you could modify it.
I'm using ag-grid-react like this:
import { AgGridReact } from "ag-grid-react";
import "ag-grid-community/dist/styles/ag-grid.css";
import "ag-grid-community/dist/styles/ag-theme-balham.css";
and declares it like this in render(some details removed for clarity):
return <div className="MarketGrid ag-theme-balham" >
<AgGridReact domLayout='autoHeight'
enableCellChangeFlash={true}
rowSelection={'single'}
onSelectionChanged={this.onSelectionChanged.bind(this)}
onGridReady={this.onGridReady.bind(this)} />
</div>;
the "enableCellChangeFlash" property works fine, and I know how to change the color of it, but what if I want to change the color depending on whether the new value is higher or lower than the previous?
My update code looks somewhat like this:
let row = this.gridApi.getRowNode(this.props.productIds.indexOf(id));
for (var f in data) {
row.setDataValue(f, data[f]);
}
}
I guess I should set the cell style somewhere, but I'm not sure where.
UPDATE: According to this page https://www.ag-grid.com/javascript-grid-data-update/#flashing I should do this:
"If you want to override how the flash presents itself (eg change the background color, or remove the animation) then override the relevant CSS classes."
Anyone knows how to do this?
I almost got this now. The answer I found out is based on CSS. Set the classname for blinking/stop-blinking like:
return (<div key={Math.random()}
className={some logic to set classname here, like "blink-red" or "blink-blue"}
onAnimationEnd={() => this.setState({ fade: false })}
> {this.state.value} </div>);
and use onAnimationEnd to set some state variable that will affect that logic.
Then add the fading logic like this to CSS:
.blink-red {
animation: redtotransparent 3s ease 5s;
}
#keyframes redtotransparent {
from {
background-color: red;
color: white;
}
to {
background-color: transparent;
color: black;
}
}
It's still not perfect, but almost there. Changing the key makes React think the DOM has changed. When I come up with anything better I will add it here.
Click on the Flashing Cell option in the dropdown at the top and then you can choose a different color for an up change and a down change. you can also set how long it will flash for.
I have to create some complex animations. It was cool to develop them with jQuery or VanillaJS, but I guess that I should choose another way with React. Google gave me ReactCSSTransitionGroup but it seems to be broken and unmaintained (according to this message: github.com). E.g. I can't make a delay before starting the animation.
I also found a library called React-motion but I'm not sure if it's actually a tool that I need. So I want to ask whether you can recommend me something about it. Probably I should use VanillaJS (using refs and other ReactDOM functions)? Or there is another approach to it? Thanks in advance.
The easiest way to do animations in React, or, in fact, anywhere on the web, is to use CSS Transitions.
CSS Transitions actually has nothing to do with React. Quoting MDN,
CSS Transitions is a module of CSS that lets you create
gradual transitions between the values of specific CSS properties. The
behavior of these transitions can be controlled by specifying their
timing function, duration, and other attributes.
Because CSS transitions is a pure CSS, they can be used in React applications, Angular, plain Javascript or even old-school server-rendered pages with no Javascript at all.
It is not the most versatile or powerful technique. But since in most cases the animations we want are pretty simple, why looking for something more complicated when a simple will do the job?
CSS Transitions are also well-supported by all major browsers with a notable exception of Opera Mini and IE below version 10.
CSS Transitions give us an ability to animate between the two CSS states. Let's say you want to animate opening and closing of a drawer (triggered by a click on a button). Let's assume we have a flex container around the drawer. When the drawer is opened, we want it to occupy 100% of the container width, therefore its max-width should be 100%. When it is closed, its width should be 0. We can create two CSS styles:
/* This CSS style is applied when the drawer is opened */
const openedStyle = {
maxWidth: '100%' /* max-with is 100% when the drawer is opened */,
};
/* This CSS style is applied when the drawer is closed */
const closedStyle = {
maxWidth: 0 /* max-width is 0 in the closed drawer */,
};
Then we handle opening / closing event apply one of those classes to the drawer object on opening / closing:
class Drawer extends React.Component {
state = {
opened: false // Initially search form is Closed
};
toggleOpened = () =>
// Toggle opened / closed state.
// Because we rely on the previous state, we need to use
// a functional setState form
// https://ozmoroz.com/2018/11/why-my-setstate-doesnt-work/
this.setState(state => ({ ...state, opened: !state.opened }));
render() {
const { opened } = this.state;
return (
<div className="drawer-container col-12 col-md-4">
<input
type="text"
className="drawer"
// Apply 'openedStyle' CSS class if the drawer is opened,
// and 'closedStyle' if the drawer is closed.
style={opened ? openedStyle : closedStyle}
/>
<button
type="button"
className="open-close-button btn btn-primary"
onClick={this.toggleOpened}
>
{opened ? 'Close' : 'Open'}
</button>
</div>
);
}
}
export default Drawer;
When we press the "Open / Close" button, the drawer will flip between 0 and 100% width, effectively opening and closing.
All we need now is to animate it.
For that we need a secret ingredient - a CSS transition property. All we need to do is to add the following style to the drawer:
/* This CSS style is applied when the drawer is opened */
const openedStyle = {
maxWidth: '100%' /* max-with is 100% when the drawer is opened */,
/* Upon transitioning to Open,
animate `max-width' for 0.5s*/
transition: 'max-width 0.5s'
};
/* This CSS style is applied when the drawer is closed */
const closedStyle = {
maxWidth: 0 /* max-width is 0 in the closed drawer */,
/* Upon transitioning to Closed,
animate `max-width' for 0.5s */
transition: 'max-width 0.5s'
};
VoilĂ ! We've got our animation - upon clicking the button our drawer is now expanded and collapsed within half a second!
This is it in the nutshell, but there is more to it:
You can animate more than one CSS attribute with a CSS transition.
You can apply delay to transitionable properties, i.e. animate opacity first, and then animate width of the same element after 0.5 second delay.
You can apply various timing functions to transitions.
I wrote an expanded blog post explaining all the above: Painless React Animations via CSS Transitions.
Check out this easy to use and popular package:
https://www.npmjs.com/package/react-transition-group
Install:
npm install react-transition-group
Usage:
import { CSSTransition } from 'react-transition-group';
<CSSTransition
in={toShow} // boolean value passed via state/props to either mount or unmount this component
timeout={300}
classNames='my-element' // IMP!
unmountOnExit
>
<ComponentToBeAnimated />
</CSSTransition>
NOTE: Make sure to apply below styles using the class property in CSS:
.my-element-enter {
opacity: 0;
transform: scale(0.9);
}
.my-element-enter-active {
opacity: 1;
transform: translateX(0);
transition: opacity 300ms, transform 300ms;
}
.my-element-exit {
opacity: 1;
}
.my-element-exit-active {
opacity: 0;
transform: scale(0.9);
transition: opacity 300ms, transform 300ms;
}
Maybe react-magic can help you.
I'm using facebooks fixedDataTable to show stock dashboard. what i need to achieve is flash fixed data table cell (say price column) green or red based on the change of price. How do we achieve animation inside fixed data table ?
You could do this with css in the children components of the datatable <Cell>. Something like:
componentWillReceiveProps(nextProps) {
// if the price has changed, add a class to do some animation stuff
if (nextProps.price != this.props.price) {
this.setState( className: "fancy-flash-class-in" );
}
}
componentDidUpdate() {
// after the component has updated, set the class back
if (this.state.className == "fancy-flash-class-in") {
setTimeout(() = > this.setState(
{className: "fancy-flash-class-out"}),
500);
}
}
componentDidUpdate() is called really fast, so you will do a setTimeout, otherwise the animation won't work.
In css, you can add animations:
fancy-flash-class-in: give the component a highlight-color, and a css transition
fancy-flash-class-out: the basic component color, with css animation to go back from red or green to basic color
So in your css file:
.fancy-flash-class-out {
background-color: grey;
transition: background-color 2s;
-webkit-transition: background-color 2s; /* Safari */
}
Simple demo codepen here