I am creating a simple Dropdown menu in React. The initial event of the drop down works as expected. However, when retracting the dropdown there is a noticeable delay in the transition despite equal default values.
The CSS transition properties are identical so I am not sure why there is a delay. I have also set the transition delay value of both explicitly to 0, to be sure.
here is the component
class DrawerLink extends React.Component {
constructor() {
super();
this.state = { collapse: false };
this.handleLinkToggle = this.handleLinkToggle.bind(this);
}
handleLinkToggle(e) {
this.setState({ collapse: !this.state.collapse });
}
render() {
const { collapse } = this.state;
return (
<div className="DrawerLinkContainer">
<div onClick={this.handleLinkToggle}>
<div className="image-container">
<img src="../public/images/user.jpeg" />
</div>
<p>Steave Jobs</p>
</div>
<div
className={
collapse ? "sub-menu-container collapse" : "sub-menu-container"
}
>
<p>First Sub Menu</p>
<p>Second Sub Menu</p>
</div>
</div>
);
}
}
and the CSS:
.sub-menu-container {
padding-left: 40px;
max-height: 0px;
overflow: hidden;
transition: max-height 1s linear 0s;
&.collapse {
max-height: 500px;
transition: max-height 1s linear 0s;
}
p {
padding: 7px 35px 7px 15px;
}
}
Because you have set max-height to 500px and your content is only about 60px, the first transition is still executing causing what seems like a delay. I.E increasing to a "max height" of 500px.
You can see better if you set a background colour on the sub container and set the height equal to the max height in the collapse class. Something like:
.sub-menu-container {
padding-left: 40px;
max-height: 500px;
overflow: hidden;
transition: max-height 1s linear 0s;
background: red;
&.collapse {
max-height: 0px;
}
p {
padding: 7px 35px 7px 15px;
}
}
The real solution is to use an animation library because transitions have issues (they just don't work) when element are removed from the DOM by React.
If you need a solution to your example, you need to know the height of the sub container and use that as a max height. It's not very dynamic though.
Related
I'm making a drop-down menu now. When I put my mouse on a specific part, the drop-down menu opens and when the mouse goes out of the browser, I want to make the drop-down menu disappear. I made a state value called isMouseOver to return true when the mouse goes up and to use jquery to return false when the mouse goes out of the browser. When the mouse goes up, I used animation to implement it as I wanted, but I want to implement animation that slowly disappears up when the mouse goes out of the browser, but this part doesn't work well. I put this part in an animation as well, but the animation overlapped and it was executed strangely. To sum up the question, how do I put the animation when isMouseOver becomes false and the display becomes none?
import { useState } from 'react';
import styled, {keyframes} from 'styled-components';
import $ from 'jquery';
// animation when mouseover
const dropdownAnimation = keyframes`
0% {
transform: translateY(-30%);
}
100% {
transform: translateY(0);
}
`
function HeaderRight () {
// Check mouseover
const [isMouseOver, setIsMouseOver] = useState(false)
// return true when mouse over
const ActMouseOver = () => {
setIsMouseOver(true)
}
// return false when mouse over
const ActMouseLeave = () => {
setIsMouseOver(false)
}
// return false when the mouse is out of the browser
$(document).mouseleave(function () {
ActMouseLeave();
});
return (
<HeaderRightWrap isMouseOver={isMouseOver} onMouseEnter={ActMouseOver}>
<ul className='HeaderRightWrapUl'>
<li className='HeaderRightWrapUlLi'>
<ul className='HeaderRightWrapUlLiUl'>
<li className='HeaderRightWrapUlLiUlLi'>
Hello
</li>
</ul>
</li>
</ul>
</HeaderRightWrap>
)
}
export default HeaderRight;
const HeaderRightWrap = styled.div`
position: relative;
box-sizing: border-box;
bottom: 40px;
.HeaderRightWrapUl {
height: 100%;
margin: 0 auto;
padding: 0;
list-style: none;
box-sizing: border-box;
display: block;
margin-inline-end: 0px;
margin-inline-start: 0px;
margin-block-start: 1em;
margin-block-end: 1em;
padding-inline-start: 40px;
}
.HeaderRightWrapUlLi {
z-index: 1000;
position: relative;
float: left;
width: auto;
height: 100%;
padding-right: 80px;
transition: padding .3s;
}
.HeaderRightWrapUlLiUl {
display: ${props=>props.isMouseOver===true?'auto':'none'};
position: absolute;
animation: ${dropdownAnimation} 0.3s ease;
}
.HeaderRightWrapUlLiUlLi {
margin-top: 0;
}
`
How to smoothly transition from text to svg in react.
As soon as I click on next button shown in pic as ">" ,the text changes to svg.But there is some sort of jumping due to change in overall height.
How can I smoothly transition between two.
Note: Unfortunately, I can't share the code as this is not a personal
project.
You can set the height of the containing element to the heighest element. Therefore you need to place them in a row with a negative margin-right so that they overlay each other. Only the element that you make visible will be shown when you hide the other elements with visibility: hidden;
Here is an example:
var elements = document.querySelectorAll('.block__element');
document.querySelector('button').addEventListener('click', () => {
elements.forEach(element => {
if(element.classList.contains('block__element--visible')) {
element.classList.remove('block__element--visible');
} else {
element.classList.add('block__element--visible');
}
})
});
.block {
background: #aaa;
display: flex;
}
.block__element {
width: 100%;
margin-right: -100%;
visibility: hidden;
opacity: 0;
transition: visibility 1s, opacity 1s;
}
.block__element--visible {
visibility: visible;
opacity: 1;
}
<div class="block">
<div class="block__element block__element--visible">
<div style="background: #f00">Content 1</div>
</div>
<div class="block__element">
<div style="height: 200px; background: #0f0">Longer content 2</div>
</div>
</div>
<button>Toggle between elements</button>
inside my react app, users are able to enter data that is saved on the server. The data is saved immediatly, users don't have to press any "save" button. I want to display a short animation (similar to https://codepen.io/Sixclones/pen/VBdeXL) whenever I'm sending data to the server. After doing some research, I figured out that I should use react-transition-group (http://reactcommunity.org/react-transition-group/css-transition).
I made a component savingIcon:
const SavingIcon: React.SFC<SavingIconProps> = ({
className,
saving,
}) => {
return (
<div className={ classNames(className, "saving-icon") }>
<CSSTransition
timeout={ 200 }
classNames="saving"
in={ saving }
>
<div className="saving-balls">
<div className="saving-balls__item" />
<div className="saving-balls__item" />
<div className="saving-balls__item" />
</div>
</CSSTransition>
</div>
);
}
Whenever something is being saved, saving is set to true and I'd like to display the animation.
Inside saving-icon.scss I put the following CSS (I tried out several approaches, therefore there might be some unnecessary css):
#keyframes bouncing {
0% {
transform: translate3d(0, 10px, 0) scale(1.2, 0.85);
}
100% {
transform: translate3d(0, -20px, 0) scale(0.9, 1.1);
}
}
div.saving-icon {
position: sticky;
top: 15vh;
display: flex;
flex-direction: column;
justify-content: end;
height: 84vh;
$anim-drt: 0.4s;
$anim-ease: cubic-bezier(.6, .05, .15, .95);
.saving-enter {
div.saving-balls{
&__item {
background-color: $success-color;
transition: background-color 20ms;
}
}
}
.saving-enter-active {
div.saving-balls{
&__item {
background-color: $active-color;
transition: background-color 20ms;
}
&:nth-child(1) {
animation: bouncing $anim-drt alternate infinite $anim-ease;
transition: animation 1000ms;
}
&:nth-child(2) {
animation: bouncing $anim-drt $anim-drt/4 alternate infinite
$anim-ease backwards;
transition: animation 1000ms;
}
&:nth-child(3) {
animation: bouncing $anim-drt $anim-drt/2 alternate infinite
$anim-ease backwards;
transition: animation 1000ms;
}
}
}
.saving-exit {
div.saving-balls{
&__item {
background-color: $active-color;
transition: background-color 20ms;
}
&:nth-child(1) {
animation: bouncing $anim-drt alternate infinite $anim-ease;
transition: animation 1000ms;
}
&:nth-child(2) {
animation: bouncing $anim-drt $anim-drt/4 alternate infinite
$anim-ease backwards;
transition: animation 1000ms;
}
&:nth-child(3) {
animation: bouncing $anim-drt $anim-drt/2 alternate infinite
$anim-ease backwards;
transition: animation 1000ms;
}
}
}
.saving-exit-active {
div.saving-balls{
&__item {
background-color: $success-color;
transition: background-color 20ms;
transition: animation 1000ms;
}
}
}
div.saving-balls {
width: 3em;
height: 10vh;
z-index: 5;
display: flex;
justify-content: space-between;
align-items: center;
&__item {
width: 0.7em;
height: 0.7em;
border-radius: 50%;
background: $success-color;
}
}
}
My issue is the following:
Usually saving something only takes very short time; not enough time to actually run the animation once. My balls just become red for a split second and return being green (active and success color are some type or red and green). I would like to have the animation running a longer period of time, something around 2 seconds. I tried some hacks using effects, states and timeouts, but they didn't work well and I'd rather use a correct solution instead of some dirty hack.
I'm not very familiar with css transitions and animations, neither with react-transition-group. I hope for some existing easy way to play an animation for a certain minimum amount of time (if the connection is weak, the animation should show until the data is saved).
As an alternative, I accept different suggestions on how to tell the user that his input has been saved instead of an animation at the corner of the page. Currently, everything the user enters is saved, but he might not notice that this happened and search for a "save" button.
For anybody with a similar issue: I solved the issue by using useState and only changing the state value for saving if it indeed changed. I didn't use a transition group.
The working saving component:
const SavingIcon: React.SFC<SavingIconProps> = ({
className,
saving,
}) => {
const [ isSaving, setIsSaving ] = useState(false);
useEffect(() => {
if (isSaving !== saving) setIsSaving(saving);
}, [ saving ]);
return (
<div className={ classNames(className, "saving-icon", {
"saving-active": isSaving
}) }>
<div className="saving-balls">
<div className="saving-balls__item" />
<div className="saving-balls__item" />
<div className="saving-balls__item" />
</div>
</div>
);
}
scss:
#keyframes bouncing {
0% {
transform: translate3d(0, 10px, 0) scale(1.2, 0.85);
}
100% {
transform: translate3d(0, -20px, 0) scale(0.9, 1.1);
}
}
div.saving-icon {
position: fixed;
top: 86px;
right: 1.3vw;
$anim-drt: 0.4s; /* duration */
$anim-ease: cubic-bezier(.6, .05, .15, .95);
&.saving-active {
div.saving-balls {
div.saving-balls__item {
background-color: $active-color;
transition: background-color 20ms;
&:nth-child(1) {
animation: bouncing $anim-drt alternate infinite $anim-ease;
}
&:nth-child(2) {
animation: bouncing $anim-drt $anim-drt/4 alternate infinite
$anim-ease backwards;
}
&:nth-child(3) {
animation: bouncing $anim-drt $anim-drt/2 alternate infinite
$anim-ease backwards;
}
}
}
}
div.saving-balls {
width: 3em;
height: 30px;
z-index: 5;
display: flex;
justify-content: space-between;
align-items: center;
&__item {
width: 0.7em;
height: 0.7em;
border-radius: 50%;
background: $success-color;
}
}
}
I have the following component:
<ReactCSSTransitionGroup
transitionName={transitionName}
transitionAppear={true}
transitionLeave={true}
>
{children}
</ReactCSSTransitionGroup>
And the following css classes:
.slide-appear {
max-height: 0;
}
.slide-appear.slide-appear-active {
max-height: 100vh;
overflow: visible;
transition: max-height 500ms ease-in;
}
.slide-leave {
max-height: 100vh;
}
.slide-leave.menu-leave-active {
overflow: hidden;
max-height: 0px;
transition: max-height 500ms ease;
}
The transitions are working for appear but not for leave.
According to the documentation, you disabled leave animation by using this prop:
transitionLeave={false}
Just remove it, problem solved.
Also as quick reminder, transitionAppear is used to enable animation after initial mount of the component, not to enable enter animation.
Check the snippet at codepen http://codepen.io/anon/pen/EZJjNO
Click on Add button, it will add another item but its appearing immediately without any fade effect.
JS:
class App extends React.Component {
constructor(props) {
super(props);
this.addItem = this.addItem.bind(this);
this.state = {
items: [1,2,3]
}
}
addItem() {
var items = this.state.items;
items.push(4);
this.setState({
items: items
})
}
render() {
return (
<div className="App">
{
this.state.items.map(function(i) {
return <div className="item fade">Testing</div>
})
}
<button onClick={this.addItem}>Add</button>
</div>
);
}
}
React.render(<App />, document.body);
CSS:
.App {
background-color: rgba(0,0,0, 0.5);
text-align: center;
height: 100vh;
}
div.item {
border: 1px solid #ccc;
padding: 10px;
background: #123456;
color: #fff;
opacity: 0;
transition: all 0.4s ease-out;
}
.fade {
opacity: 1 !important;
}
Since the fade class is added by default, you don't get the transition effect. If you open your browser's developer tools and remove the class, you'll see it fade away nicely.
There's a few ways to get what you want, but I'd just use a keyframe CSS animation like so:
.fade {
animation: 0.4s ease-out fadeIn 1;
}
#keyframes fadeIn {
0% {
opacity: 0;
visibility: hidden;
}
100% {
opacity: 1;
visibility: visible;
}
}
Here's a fork of your code pen showing how it works :)