ReactJS component: fadeIn / fadeOut on mouse-enter / mouse-leave - javascript

I've created a simple component on reactjs (kind of a value-picker: plunker)
Now, I want the upper and lower parts of the control to be hidden (opacity=0) and animate to opacity=1 when user hovers the central div (.range-picker-selection) with mouse.
Since there's no way I can operate with DOM directly from functions of my component (reactjs discourages this) I guess I should change the state ( to something like state.expanded = true) and rely on reactjs to reflect the change.
But how can I achieve the animation effect?
With jQuery I would have used element.animate({opacity:0}) but now I don't have access to DOM

Something like this?
http://plnkr.co/edit/5tLWNTtpsSWBUCgnKwQO?p=preview
I updated the state to have a hovered attribute and two mouse enter / leave methods to set the value which are bound the .range-picker-selection element in the render function
<div className="range-picker-selection" onMouseEnter={this.onMouseEnterHandler} onMouseLeave={this.onMouseLeaveHandler}>
onMouseEnterHandler: function () {
this.setState({
hovered: true
})
},
onMouseLeaveHandler: function () {
this.setState({
hovered: false
})
},
I also updated the render function to set the opacity to 0 or 1 accordingly for the elements.
style={{opacity: this.state.hovered ? 1 : 0}}
Finally, the actual animation is done with CSS animations
transition: opacity 1s;
I hope this helps

Related

Debounce jsx element's rendering in React

I am wondering if it is possible to debounce a jsx element's rendering. I have an animation of a panel expanding that has some content in it. If the panel is empty (just the background) the animation is smooth and works as expected. The animation and associated components are all from Material-UI. The issue arises when the content is in the panel already so the animation (width expansion) just skips out to the width of the content making the animation look choppy. Here is a similar example to what I am referring to. This code is my code I am using in my panel and they work the same in terms of the expansion. Only difference is the content in this example is just lorem ipsum so the animation appears to work fine. Is it possible to debounce the <CardContent /> component's rendering like this?
{ open &&
(
_.debounce(e => {
return (
<CardContent>
{/*content in here*/}
</CardContent>
)
}, 300)
)
}
or something similar (this doesn't work) so the panel will be fully expanded through the animation before the content is rendered?
I'd like to add a comment however my reputation isn't above 50 yet so unfortunately I can't :p.
JS Animation
To answer your question, if you are using JavaScript animations like GSAP or AnimeJS you can have a state flag called. isAnimating = null. Then when your animation starts set the flag to true and when it's finished set it to false. Then in your render() method write a conditional render like this.state.isAnimating === false && <CardContent />.
CSS Animation
If you are using CSS animation you can use the transitionend event to wait for the animation to end. Therefore you have to create a ref on the animating DOM element to which you can attach the event.
Does that help you a bit? Please let me know if you have any other questions.
Here is a solution using react spring library
I added a state variable in order to display or not the card content
const [contentVisible, setContentVisible] = React.useState(false);
I created a spring to handle the width transition, I set contentVisible to true as soon as the width approaches the 300
const { width } = useSpring({
width: open ? 300 : 120,
onFrame: ({ width }) => setContentVisible(width > 299.8)
});
Finally, I created an animated card component
const AnimatedCard = animated(Card);
...
<AnimatedCard classes={{ root: classes.card }} style={{ width }}>

Different behavior of transition when launched via event listener vs direct function call [duplicate]

I'm having some major headache trying to apply CSS3 transitions to a slideshow trough JavaScript.
Basically the JavaScript gets all of the slides in the slideshow and applies CSS classes to the correct elements to give a nice animated effect, if there is no CSS3 transitions support it will just apply the styles without a transition.
Now, my 'little' problem. All works as expected, all slides get the correct styles, the code runs without bugs (so far). But the specified transitions do not work, even though the correct styles where applied. Also, styles and transitions work when I apply them myself trough the inspector.
Since I couldn't find a logical explanation myself I thought someone here could answer it, pretty please?
I've put together a little example of what the code is right now: http://g2f.nl/38rvma
Or use JSfiddle (no images): http://jsfiddle.net/5RgGV/1/
To make transition work, three things have to happen.
the element has to have the property explicitly defined, in this case: opacity: 0;
the element must have the transition defined: transition: opacity 2s;
the new property must be set: opacity: 1
If you are assigning 1 and 2 dynamically, like you are in your example, there needs to be a delay before 3 so the browser can process the request. The reason it works when you are debugging it is that you are creating this delay by stepping through it, giving the browser time to process. Give a delay to assigning .target-fadein:
window.setTimeout(function() {
slides[targetIndex].className += " target-fadein";
}, 100);
Or put .target-fadein-begin into your HTML directly so it's parsed on load and will be ready for the transition.
Adding transition to an element is not what triggers the animation, changing the property does.
// Works
document.getElementById('fade1').className += ' fade-in'
// Doesn't work
document.getElementById('fade2').className = 'fadeable'
document.getElementById('fade2').className += ' fade-in'
// Works
document.getElementById('fade3').className = 'fadeable'
window.setTimeout(function() {
document.getElementById('fade3').className += ' fade-in'
}, 50)
.fadeable {
opacity: 0;
}
.fade-in {
opacity: 1;
transition: opacity 2s;
}
<div id="fade1" class="fadeable">fade 1 - works</div>
<div id="fade2">fade 2 - doesn't work</div>
<div id="fade3">fade 3 - works</div>
Trick the layout engine!
function finalizeAndCleanUp (event) {
if (event.propertyName == 'opacity') {
this.style.opacity = '0'
this.removeEventListener('transitionend', finalizeAndCleanUp)
}
}
element.style.transition = 'opacity 1s'
element.style.opacity = '0'
element.addEventListener('transitionend', finalizeAndCleanUp)
// next line's important but there's no need to store the value
element.offsetHeight
element.style.opacity = '1'
As already mentioned, transitions work by interpolating from state A to state B. If your script makes changes in the same function, layout engine cannot separate where state A ends and B begins. Unless you give it a hint.
Since there is no official way to make the hint, you must rely on side effects of some functions. In this case .offsetHeight getter which implicitly makes the layout engine to stop, evaluate and calculate all properties that are set, and return a value. Typically, this should be avoided for performance implications, but in our case this is exactly what's needed: state consolidation.
Cleanup code added for completeness.
Some people have asked about why there is a delay. The standard wants to allow multiple transitions, known as a style change event, to happen at once (such as an element fading in at the same time it rotates into view). Unfortunately it does not define an explicit way to group which transitions you want to occur at the same time. Instead it lets the browsers arbitrarily choose which transitions occur at the same time by how far apart they are called. Most browsers seem to use their refresh rate to define this time.
Here is the standard if you want more details:
http://dev.w3.org/csswg/css-transitions/#starting

Can we modify the div once component renders in react?

I am trying to find a solution to modify the height of the div for react based on how many elements it will consist to grow automatically.
So, to do the same i have chosen jquery
ul.each(function() {
const self = $(this);
const { listCount, ulCount } = getListItemsCount(self);
const ruleHeight = listCount * listDefaultHeight;
const children = $(self).children();
/* Swapping of elements to adjust the heights */
if (children.length - 1 === 2 && ulCount === 1) {
if ($(children[2]).data('count') > $(children[1]).data('count')) {
$($(self).children()[2]).insertBefore($($(self).children()[1]));
//ruleHeight += 25;
}
}
$(self)
.find('div')
.css({ height: `${ruleHeight}px` });
});
The above code is happening inside componentDidMount(). The reason I am doing here, we are not sure how much height we need to increase as the div position is absolute and also depending upon the first level content, we are swapping the divs as well as a high-level overview.
The blue and ping is an absolute div whose height is growing accordingly, if the first level data is less and second level has nested and more list items, the height was not adjusting, so swapping the divs and its working.
The question here is: Is it the right approach to handle this with jquery?
React DOM is not updated now? How to update the react dom as well, if suppose render method is called, will the jquery code is written become obsolete?
Is there a possibility to adjust the height using flex automatically without using jquery for absolute positions or by just css will be awesome fix?
Please guide the best practice to do the same
I would suggest do not use jquery instead , write a function to calculate height of the div in componentDidMount and set the height of the div in render method.
Here is an example
componentWillMount() {
this.setDivHeight();
}
return () {
render(
<div style={{height: this.state.divHeight}}>
</div>
)
}
This just for reference, you have to call a function to set change value of state divHeight to re render the component and the div height will also change on each render. Also would suggest user in JSX to code your components. Tips about JSX

How to prevent a bounce when swapping views with fadeIn and fadeOut

TL;DR:
Container has a view that has height of 10.
Height == Container(10) + Static(10) == 20
I then change the view in container, which has a height of 50
1) Add new view to container:
Height == OldView(10) + NewView(50) + Static(10) == 70
2) Remove old view from container
Height == NewView(50) + Static(10) = 60
So the height goes from 20 -> 70 -> 60, which produces a bounce.
I want to go directly from 20 -> 60
I have a container that swaps divs, depending on the state of a checkbox
Below it are three more static views (In this case, the "Timer Precision" and "Hide Progress" and "Shape" controls)
I swap the div in the container with this:
function showSurveyIf(isChecked)
{
if( isChecked ) {
$("#surveyDeathContainer").fadeIn(100);
$("#specifyDeathContainer").fadeOut(100);
} else {
$("#surveyDeathContainer").fadeOut(100);
$("#specifyDeathContainer").fadeIn(100);
}
}
But my issue is that this code makes the static views jump.
For example, if you check the checkbox, it will first add the survey questions, which bumps the static views way down, and then makes the date field disappear, which brings the static views back up.
What I would like to have is just move the static views once, downwards or upwards depending on which view is coming.
Logically to me, this would mean that I need the fadeIn and fadeOut to execute at the same exact time, but I don't think that's possible?
Or is there just another way entirely to do this better?
THE PROBLEM:
jQuery fadeIn and fadeOut methods actually manipulate the css display attribute - that is why the static elements bounce
THE SOLUTION:
// make the element invisible - display should still be with 'block' value
$("#specifyDeathContainer").animate({opacity: 0.0001}, 100, function()
{
$(this).fadeOut(); // now - make display -> 'none'
$("#surveyDeathContainer").fadeIn(100); // and make the new element visible with animation
});
So the issue was actually with using fadeIn and fadeOut, it didn't actually remove/add the divs until the opacity fully transitioned, causing this bounce.
By using show and hide instead, there isn't a bounce

FadeIn react component using javascript

According to react documentation only way to animate is by usingReactCSSTransitionGroup. Is there anyway to do animation using only Javascript?
Thanks.
Typical way to do a fadeIn animation is:
define a css-class that animates fadeIn in someway
in componentDidMount(), add a setState() method that adds the faded-in class to your component
your component will then re-render, with the new class. Applying any css animations that you have defined (in css of course)
PS: With simple javascript, you can only add animation when the component appears, or when it updates.
When your component leaves (so if you would want a fade-out animation), it is much, much harder to do that with javascript only.
Finally i found this npm package It has all the basic animation.
for fadeIn animation
constructor(props) {
super(props);
this.state = {
opacity: 0
}
// react state animation wrapper
this._animate = new ReactStateAnimation(this)
}
componentDidMount () {
this._fadeIn();
}
_fadeIn = () => {
this._animate.linearIn('opacity', 0.9, 300);
};
pretty simple!

Categories