Animations with React - reactjs

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.

Related

animate text in React

I'm trying to animate text in React by using scss (probably it doesn't matter). It doesn't work.
In SCSS file:
.animate {
animation: 3s linear 1s infinite alternate slidein;
}
Aplied on my text:
<h2 className="animate">Order is active.</h2>
Should I do something more?
Thanks for the answer!
You can use marquee:
<marquee behavior="slide" direction="down">Order is active.</marquee>
For more attributes: https://www.w3schools.in/html-tutorial/marquee-tag/
You have to use animation with #keyframe. #keyframe specifies what to do, animation when and how
https://www.w3schools.com/css/tryit.asp?filename=trycss3_animation2.
Or you can use "transition" with additional class name
https://developer.mozilla.org/ru/docs/Web/CSS/transition
Somethin like this:
#scss
.main-class {
opacity: 1;
transition: 1s;
}
.hide-classname {
opacity: 0;
transition: 1s;
}
#jsx \ tsx
const [hide, setHide] = useState(false)
return(
<div className={`main-class ${hide ? 'hide-classname' : null}`}>
i will disappear
</div>
)
You can handle any kind of animation like low level to high level animation using this npm package built for react. react-transition-group.
mountOnEnter is one of the future available in it. which mount the component and start animate based on Boolean condition.
These animation states be will handled in class prefix from css followes.
entering
entered
exiting
exited
all kind of animation possibilities which are done in animate.css can be handled in above states.
You could try to checkout react-animated-text-builders. It's a small lib that supports some text animations. Nothing fancy, but maybe those animations are sufficient for your purposes:
<FloatingLettersTextBuilder
floatingSpeed={500}
lettersAppearanceDelay={250}
> Floating Letters
</FloatingLettersTextBuilder>
This will slide the text in from right to left.

React-Transition-Group add a delay before the *-enter class is added

I am trying to use react-transition-group to animate the entry/exit of two components. The Transitions are working fine, but the only issue is that when *-exit is fired for the exiting component, the other component also enters the DOM and the exiting component is pushed down before leaving. You can see it happening in the codesandbox below. How can I have a delay so that the *-enter is fired only after *-exit completes? Any help is appreciated.
Code - https://codesandbox.io/s/csstransition-component-06bpo
I made something similar with react-spring. My solution was to use absolute positioning. This way the two components are on each other and the entering and exiting animation is in the same time. I added a style in the Child.js
const style = { position: 'absolute', width: '100%' };
return (
<div style={style}>
{props.type.list ? (...
And I also modified the enter animation to be left to right, it seems better I think.
.alert-enter {
transform: translateX(-100%);
}
Here is the example: https://codesandbox.io/s/csstransition-component-xuw2t

React dropzone - dragLeave event fired when dragging file over dropzone

I am using React dropzone for file upload
<DropZone
accept='.pdf,.pptx,.ppt,.docx,.doc,.xls,.xlsx,.xslx,.png,.xsl,.jpg,.jpeg,.gif,.zip'
onDrop={ files => {
this.handleFileDrop(files);
this.dragLeaveHandler();
} }
onDragEnter={ this.dragOverHandler }
onDragLeave={ this.dragLeaveHandler }
multiple={ false }
style={ { height: '100%' } }
>
dragOverHandler = () => {
console.log('enter');
this.setState({
isDragOver: true,
});
};
dragLeaveHandler = () => {
console.log('exit');
this.setState({
isDragOver: false,
});
};
When a file is moving above the drop zone onDragLeave event fires simultaneously.
Should I use some other events?
How can I fix this issue?
You could use pointer-events: none; on the element(s) that are firing the drag leave. That should still allow the dropped event and getting the accepted file though would stop overriding the dropzone events.
The problem you're facing is most likely caused by the DOM events dragEnter and dragLeave getting messed up instead of any flaw in the react-dropzone package. Some elements may cause hovering over them in certain positions not to register as hovering over their parent element. For example, there is a thin sliver at the top edge of any plain string rendered inside a block displayed element. Most commonly this happens inside a <p> tag.
Without seeing the children rendered inside your dropzone, it is impossible to give a specific fix. Generally, you will have to mess with the styling of the children, though. <p> tags for example will not be a problem if their size is set to 0 pixels or if they're replaced with <span> tags. Both options will disrupt the displaying of the children, which is unfortunatley unavoidable.
As for using other events, you're out of luck. The DropZone component relies on the onDragEnter and onDragLeave HTML DOM events. Therefore any fix you might come up with won't fix the component itself.
All in all, it's an unfortunate issue that just has to be dealt with. The simplest way to deal with it is to just have at most one piece of text inside the dropzone and to set its size to 0 pixels with css: height: 0px;. Regular <div> elements won't cause issues, so you can craft an intricate dropzone using them.

How to start animation when props change (via redux) in React Native?

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>

Event propagation does not happen like standard html

I know that react uses its own synthetic implementation of events. However it appears to me that they are not exactly like standard html and this is a problem. In my case I have a checkbox that is a sibling of some img tags. There are two img tags, one for representing checked and another unchecked. I have some css styling that does a display none when the checkbox is in an unchecked state. Trying to get this html/css working with react is hard. It appears that the click event is not propagating onto the checkbox sibling with a react component, although it works from standard html. Note as there's some confusion I know that the non-react version of this is using css and not events. But I'm trying to implement a react evented version of the same thing, and was expecting normal html event propagation behavior--which I'm not seeing. Also note the checkbox is a sibling of the label. It's also transparent, so the user never actually clicks on the checkbox they click on the img tags.
.checkbox-image input[type="checkbox"] + label img.selected {
display: none;
}
.checkbox-image input[type="checkbox"] + label img.unselected {
display: block;
}
<div class="checkbox-image"><input id="portfolio-standard-dev" type="checkbox" data-name="PortfolioStandardDeviation"><label for="mp-chart3"><img class="unselected" src="/images/img-843599.png"><img class="selected" src="/images/img-1b9f30.png"><span>Portfolio Standard Deviation</span></label></div>
CSS has nothing to do with events. An event propagation cannot impact your styles.
You have to handle when the user check/uncheck the checkbox and manually update the class of the image.
Or you can try the following pure CSS:
.checkbox-image input[type="checkbox"]:checked + label img {
display: none;
}
.checkbox-image input[type="checkbox"] + label {
display: block;
}

Resources