React Virtual Dom Diff resets Css Animation - javascript

I am building a game, with css animations.
I have this css rule for move animation:
.animate {
animation: play-move 1s steps(10) infinite;
}
I update my state every half second so state changes as:
State: [0, 1, 0]
View: <div/><div class="animate"/><div />
State: [0, 0, 1]
View: <div/><div/><div class="animate">
The problem is as the dom changes, the css animation resets, so I can't see the full animation.
see react version:
http://jsfiddle.net/CGmCe/12998/
However if you reverse the move direction, animation doesn't reset.
mithriljs version:
http://jsfiddle.net/CGmCe/12982/
jquery:
http://jsfiddle.net/CGmCe/12968/

instead of updating entire DOM you can just change class attr value as like fuddle link below.
http://jsfiddle.net/CGmCe/12974/
on render: you can render <div class="tile temp100"></div>.
on state change you can change the class as sequence as temp100,temp010,temp001

Related

How do I apply CSS transitions (e.g. fade-ins) to a list of items in React so their animations are sequenced one after the other on render?

I've been searching Stack Overflow and the whole internet for this and couldn't find the right answer, so sorry if this is a duplicate question.
I've got a list:
How do I apply CSS transition to the list of elements, one by one, only on page load in React?
I was able to use ReactCSSTransitionGroup, and it works fine, but it applies the transition to the entire list at the same time.
React:
<ul className="item-list">
<ReactCSSTransitionGroup transitionName="fade" transitionAppear={true}
transitionAppearTimeout={2000}>
{props.items.map((item, i) => (<li><someComponent/></li>)}
</ReactCSSTransitionGroup>
</ul>
...
CSS:
.fade-appear{
opacity: 0;
}
.fade-appear-active{
opacity: 1;
transition: opacity 500ms ease-out;
}
As I previously mentioned, I need to apply the above transition to the list of items one after another.
I think the effect is usually referred to as “staggered,” “cascading,” or “sequenced.”
Rather than using ReactCSSTransitionGroup, you could do this mostly with CSS.
First, I'd animate your cards using animation property and #keyframes instead of transition property. So to start, you could add something like this to your CSS:
CSS
#keyframes fadeIn {
0% {
opacity: 0;
}
100% {
opacity: 1;
}
}
Javascript
The crux of the solution is to set an animation CSS style on each list item, and use the item index as a multiplier for a specified delay value.
I started by creating an array of objects called items, where each object contains a title and a text field (mainly just needed an array to map for the example).
I also created a couple of constants for abstracting the two numerical values for the animation, duration and delay (note we're only doing math with delay in the example to follow, but it looked cleaner to me to pull out duration as well):
const duration = 1000; // ms
const delay = 500; // ms
Made a template that returns a formatted string to be used as the value of each transition element's animation CSS property:
const animStr = (i) => `fadeIn ${duration}ms ease-out ${delay * i}ms forwards`;
Mapping the data during render time, and setting the CSS animation style based on the index value (i) via animStr:
{items.map((item, i) => (
<li key={i} style={{ animation: animStr(i) }}>
<SomeComponent item={item} />
</li>
))}
The animation will become active as soon as that element is injected into the DOM (as per the CSS animation spec). Syntax is based on the css animation shorthand. Note that the default behavior for the animation is to run once. Adding forwards to the rule causes the animation to retain the properties of the last keyframe when it stops (fully visible).
Edit: Personally, I think it looks better to start the delay index at 1 instead of 0, so you could set your animation value to this:
`fadeIn ${duration}ms ease-out ${delay * (i + 1)}ms forwards`
Working CodeSandbox
Here's a working codesandbox.
Screen Recording
This is what the above code looks like in action. It's a screen recording of the page being reloaded on CodeSandbox.
Another way to solve this would be to use a library. Both of the following libraries can achieve this effect with some added features:
react-drizzle
react-awesome-reveal
If you're committed to keeping ReactCSSTransitionGroup then you could probably just add a custom transition-delay property on each item, similar to the solution in my other answer.
const delay = 500;
And do something like this:
{props.items.map((item, i) => (
<li style={{ transitionDelay: `${delay * i}ms` }}>
<SomeComponent item={item} />
</li>
)}

animate.css loop 2 different animation

I'm running recently into a problem with animate.css on my latest project.
Basically what I'm trying to do is refreshing a paragraph text on my webpage every five/ten seconds with jQuery, but I don't want to simply change the text. I would like that the previous text disappears using animate.css fadeOut animation and the new one appears using the fadeIn animation.
Currently I'm using this code (is only an exmple):
setInterval(function() {
$("#myp").addClass('fadeOut');
$("#myp").text(sometext);
$("#myp").removeClass('fadeOut');
$("#myp").addClass('fadeIn');
$("#myp").removeClass('fadeIn');
}, 5000);
Obviously sometext is every cycle different for simplicity.
At first, this code gave me some problem because the animation was not smooth but flickery. I tried to slow down the process by sleeping the programm using setTimeout between the add and the remove class, because I was thinking that the removing of the class before the end of css animation could cause the problem but is still flickery.
You can nest some setTimeOut methods inside the setInterval function.
So you can control the time of each step of the animation.
In addition, since animate.css uses animation property, you also need to determine the animation-duration and animation-fill-mode in CSS.
animation-duration specifies how long the animation cycle should take.
animation-fill-mode with "forwards" will prevent the element from being reset to the previous state after animation completion.
var sometext = "another text";
setInterval(function() {
var myp = $("#myp");
myp.addClass('fadeOut');
setTimeout(function() {
myp.text(sometext);
myp.removeClass('fadeOut');
myp.addClass('fadeIn');
setTimeout(function() {
myp.removeClass('fadeIn');
}, 1000);
}, 1000);
}, 5000);
#myp {
animation-duration: 1s;
animation-fill-mode: forwards;
}
<link href="https://cdnjs.cloudflare.com/ajax/libs/animate.css/3.7.2/animate.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div id="myp">lorem ipsum dolor sit</div>

Decide order of CSS Transitions

Funky problem i'm having, although i bet theres a slick way to solve it.
The circumstance is that i have a table that has three column sort states: completely unsorted, in which case i dont want any icon present, sorted ascending, in which i want an up arrow, and sorted descending, in which i want a down arrow.
clicking the column header should take you through these three states.
starts off => click => ascending => click => descending => click => back to off
This all works fine and dandy, except for the fact that i want to use the same Caret element, and then use css transition's to fade it in from opacity: 0 to opacity: 1 , then on click rotate it 180deg to show a down arrow for descending, then finally when clicked again to remove the sort, i want it to fade out WITHOUT ROTATING BACK TO 0 DEG
That last part is where the problem lies.
EDIT
I have only recreated the behavior in the sandbox, but i am really using react-table, so there are only three possible states since it is controlled by the package:
initial state: {showCaret: false, flipped: false}
first click: {showCaret: true, flipped: false}
second click: {showCaret: true, flipped: true}
third click, back to initial: {showCaret: false, flipped: false}
the state changes are controlled by react-table, so i cant setTimeout on the flipped variable.
I am looking for a purely CSS way to achieve this goal without manipulating the way the state changes, if possible
END EDIT
I've attached a codesandbox to demonstrate. First click Show Caret, then Flip Caret, then Hide Caret. The css is set up basically the same as mine is currently in my actual project too.
https://codesandbox.io/embed/admiring-rain-svsc9?fontsize=14&hidenavigation=1&theme=dark
It sounds like what you want is for the arrow to disappear, but not to rotate back to its starting orientation as it disappears.
Since you are handling and tracking all this with React state, you could just set those two states separately, timed .3s apart (since that is your CSS transition time).
You could do this in a number of ways. To demonstrate it, in this fork of your example I have you just setting the on/off visibility to off, and then separately, in componentDidUpdate, I have it watching for whenever it's turned off, at which point it waits 300ms (.3s) and then sets the rotate state back.
componentDidUpdate(oldProps, oldState) {
if (oldState.showCaret && !this.state.showCaret) {
//it was just hidden
setTimeout(() => {
this.setState({
flipped: false
});
}, 300);
}
}
https://codesandbox.io/s/sparkling-pine-9igec
EDIT, with CSS only solution
https://codesandbox.io/s/pensive-wilbur-opxzf
/* flipped taking care of rotating the img tag */
.image {
transition: transform 0.3s linear 2s;
}
.flipped {
transform: rotate(180deg);
transition: transform 0.3s;
}
Change
onClick={() => this.setState({ showCaret: false, flipped: false })}
To
onClick={() => this.setState({ showCaret: false })}
and it should work.

Activate a CSS Class by an array sequence with AngularJS

I have 6 divs that I want to activate a heartbeat CSS animation, however, I would like to do that by a sequence.
For example, I have an array:
self.generatedSequence = [0,3,5,3];
Each item in the array means the position of the div that will receive the heartbeat function.
Each dive is a UI component from AngularJs:
<div class="grid-cell"
ng-class="{'heartbeat': $ctrl.isActive
}"
></div>
I'm trying to use the controller to activate the HeartBeat animation in every position by a sequence.
So in that example will be like
The position 0 will takes 1s
The position 3 will takes 1s after the position 0
The position 5 will takes 1s after the position 3
The position 3 will takes 1s after the position 5
So all the animation will take 4s.
I tried to do that with $timeout, but it does the both at the same time.
https://codepen.io/guifeliper/pen/pwdKKj
You want to use $interval, not $timeout since $timeout only runs once.
$ctrl.stopInterval = $interval(function () {
$ctrl.heartbeatId = $ctrl.generatedSequence.shift();
// be sure to clean up afterwards
if($ctrl.generatedSequence.length == 0) {
$interval.cancel($ctrl.stopInterval);
}
}, 1000);
And change your grid cells ng-class directives like this:
<div class="grid-cell" ng-repeat="grid in $ctrl.gridBox"
ng-class="{'heartbeat': $ctrl.heartbeatId == grid }"></div>
https://codepen.io/jdoyle/pen/xrPJwW

JavaScript: An efficient way to handle events (Firefox-specific)

I'm running a scroll event that triggers TweenMax animations, and I'm noticing that, while it looks good on Chrome, there is a considerable amount of lag on Firefox. Does anyone have a suggestion about how to handle this scroll event as efficiently as possible? Also, is there something about Firefox's rendering that I'm not aware of that might be causing this? Any leads would be appreciated!
The gist is that I'm looking for containers on my page called "customers", which each contain three individual "customer" elements. When a div that matches "customers" scrolls into view, trigger a TweenMax animation, and add a class called "animated", which prevents the element from re-animating subsequently.
Here is a fiddle with the basic demonstration:
http://jsfiddle.net/epp37jsq/
EDIT
To clarify, the fiddle only demonstrates the behavior of my animation function. The lag does not occur there because the file size is quite small. On the actual site, I have 11 groups of 3 "customers." The image is the same, but pulled in 33 times. In the future, the images will be unique. In essence, the animation is being called for each of these 11 groups. I'm looking for suggestions on how to improve the speed of my page.
And my code:
var scrollTimer = null;
$(window).scroll(function () {
if (scrollTimer) {
clearTimeout(scrollTimer); // clear any previous pending timer
}
scrollTimer = setTimeout(handleScroll, 500); // set new timer
console.log("fired!");
});
function handleScroll() {
scrollTimer = null;
$('.customers').each(function() {
if (!$(this).hasClass('animated')) {
if ($(this).isOnScreen(0.45, 0.45)) {
TweenMax.staggerFromTo($(this).find('.customer'), 0.3, {
y: 50,
opacity: 0
}, {
y: 0,
opacity: 1,
ease: Power2.easeOut
}, 0.15);
$(this).addClass('animated');
}
}
});
}
Usually with Firefox, translating on the x or y axis can cause some jank. Sometimes adding a slight rotation:0.001 to your tween can help make your tween more smooth in Firefox.
http://jsfiddle.net/pwkja058/
Also using the GSAP special property autoAlpha instead of opacity can help increase performance
TweenMax.staggerFromTo($(this).find('.customer'), 0.3, {
y: 200,
rotation:0.01, /* add a slight rotation */
autoAlpha: 0 /* use instead of opacity */
}, {
y: 0,
rotation:0.01, /* add a slight rotation */
autoAlpha: 1, /* use instead of opacity */
ease: Power2.easeOut
}, 0.15);
autoAlpha is part of the GSAP CSSPlugin:
http://greensock.com/docs/#/HTML5/GSAP/Plugins/CSSPlugin/
autoAlpha - Identical to opacity except that when the value hits 0 the visibility property will be set to "hidden" in order to improve browser rendering performance and prevent clicks/interactivity on the target. When the value is anything other than 0, visibility will be set to "inherit". It is not set to "visible" in order to honor inheritance (imagine the parent element is hidden - setting the child to visible explicitly would cause it to appear when that's probably not what was intended). And for convenience, if the element's visibility is initially set to "hidden" and opacity is 1, it will assume opacity should also start at 0. This makes it simple to start things out on your page as invisible (set your css visibility:hidden) and then fade them in whenever you want.

Categories

Resources