React Component and CSSTransitionGroup - javascript

early days with Facebook ReactJS. Simple CSS fade-in transition. It works as expected with ReactJS v0.5.1. It doesn't with v11.1, v12.0, v12.1. Here's the CSS and JSX:
CSS
.example-enter {
opacity: 0.01;
transition: opacity .5s ease-in;
}
.example-enter.example-enter-active {
opacity: 1;
}
.example-leave {
opacity: 1;
transition: opacity .5s ease-in;
}
.example-leave.example-leave-active {
opacity: 0.01;
}
JSX for ReactJS v12.1
/**#jsx React.DOM*/
var ReactTransitionGroup = React.addons.CSSTransitionGroup;
var HelloWorld = React.createClass({
render: function() {
return (
<ReactTransitionGroup transitionName="example">
<h1>Hello world</h1>
</ReactTransitionGroup>
);
}
});
React.render(<HelloWorld />, document.body);
Here's the list of Codepens:
v0.5.1 http://codepen.io/lanceschi/pen/ByjGPW
v0.11.1 http://codepen.io/lanceschi/pen/LEGXgP
v0.12.0 http://codepen.io/lanceschi/pen/ByjGOR
v0.12.1 http://codepen.io/lanceschi/pen/YPwROy
Any help appreciated.
Cheers,
Luca

It looks like CSSTransitionGroup used to animate on initial mount, but it doesn't any more as of React v0.8.0 or so. See https://github.com/facebook/react/issues/1304 for a bit more info.
One solution is to simply mount the <h1> after <HelloWorld> is mounted, like so:
/**#jsx React.DOM*/
var ReactTransitionGroup = React.addons.CSSTransitionGroup;
var HelloWorld = React.createClass({
getInitialState: function() {
return { mounted: false };
},
componentDidMount: function() {
this.setState({ mounted: true });
},
render: function() {
var child = this.state.mounted ?
<h1>Hello world</h1> :
null;
return (
<ReactTransitionGroup transitionName="example">
{child}
</ReactTransitionGroup>
);
}
});
React.render(<HelloWorld />, document.body);
Live example: http://codepen.io/peterjmag/pen/wBMRPX
Note that CSSTransitionGroup is intended for transitioning child components as they're dynamically added, removed, and replaced, not necessarily for animating them on initial render. Play around with this TodoList Codepen (adapted from this example in the React docs) to see what I mean. The list items fade in and out as they're added and removed, but they don't fade in on the initial render.
EDIT: A new "appear" transition phase has been introduced recently to allow for animation-on-mount effects. See https://github.com/facebook/react/pull/2512 for details. (The commit has already been merged into master, so I imagine it'll be released with v0.12.2.) Theoretically, you could then do something like this to make the <h1> fade in on mount:
JS
...
<ReactTransitionGroup transitionName="example" transitionAppear={true}>
<h1>Hello world</h1>
</ReactTransitionGroup>
...
CSS
.example-appear {
opacity: 0.01;
transition: opacity .5s ease-in;
}
.example-appear.example-appear-active {
opacity: 1;
}

I looked into the issue a little deeper. With the current version of ReactJS it seems not possible to make an initial CSS transition. More info and thoughts here.
Most probably things are gonna change with v0.13.x. You can have a look at the source code which features a transitionAppear prop.
EDIT: I downloaded from GitHub the latest ReactJS (v0.13.0 - alpha) and built it. Everything now works accordingly if you make use of transitionAppear prop (is to be set true explicitly). Here below you'll find the updated CSS and JSX as well as the live example:
CSS:
.example-appear {
opacity: 0.01;
transition: opacity 0.5s ease-in;
}
.example-appear.example-appear-active {
opacity: 1;
}
JSX for ReactJS v0.13.0 - alpha:
/**#jsx React.DOM*/
var ReactTransitionGroup = React.addons.CSSTransitionGroup;
var HelloWorld = React.createClass({
render: function() {
return (
<ReactTransitionGroup transitionName="example" transitionAppear={true}>
<h1>Hello world</h1>
</ReactTransitionGroup>
);
}
});
React.render(<HelloWorld />, document.body);
Live example: http://codepen.io/lanceschi/pen/NPxoGV
Cheers,
L

appear
Normally a component is not transitioned if it is shown when the <Transition> component mounts. If you want to transition on the first mount set appear to true, and the component will transition in as soon as the <Transition> mounts.
from react-transition-group Docs
Example of usage
JSX:
<TransitionGroup>
<CSSTransition classNames="fade" appear={true} >
<h1>Hello world!</h1>
</CSSTransition>
</TransitionGroup>
CSS:
.fade-appear {
opacity: 0.01;
z-index: 1;
}
.fade-appear.fade-appear-active {
opacity: 1;
transition: opacity 300ms ease-in;
}
As of:
React v16.6.3
react-transition-group v2.5.1

Related

The way of triggering transition in Vue.js

I am trying to make transition in Vue, and I have a question how to trigger it.
I saw normally transition is triggered by v-show or v-if. but is there another way to execute?
I want to
・Keep my element's opacity as 0.2 and becomes 1 when the transition is triggered
・Also I am using The Element.getBoundingClientRect() to decide the area where transition should happen.
But obviously, v-show or v-if do not let me to do since they make the element disappear or display: none( so I can not measure the element by .getBoundingClientRect())
This is my template
<ul class="outer-wrapper" >
<li class="slide one" >
<div>
<a href="#">
<transition name="fade-animation">
<img v-show="show" ref="slider1" src="../assets/test.png">
</transition>
</a>
</div>
</li>
.
.
.
</ul>
and Vue script
export default {
name: 'test',
data(){
return{
show: false
}
},
methods: {
opacityChange: function () {
let slider = this.$refs.slider1.getBoundingClientRect();
let sliderLeft = Math.floor(slider.left);
let sliderRight = Math.floor(slider.right);
let centerPoint = Math.floor(window.innerWidth / 2);
let sliderWidth = slider.width;
if(sliderRight <= centerPoint + sliderWidth && sliderLeft >= centerPoint - sliderWidth) {
this.show = true;
} else {
this.show = false;
}
}
}
}
and css
.fade-animation-enter,
.fade-animation-enter-leave-to {
opacity: 0.2;
}
.fade-animation-enter-to,
.fade-animation-enter-leave {
opacity: 1;
}
.fade-animation-enter-active,
.fade-animation-enter-leave-active {
transition: opacity, transform 200ms ease-out;
}
Thanks for your help :)
I think you can use usual CSS transition (or JS in some cases) for this, not Vue transitions. Vue transitions are used for lists/appear/disappear. In any other cases it's better to use CSS/JS.
They wrote more about state transition here

post-css dynamic variable from javascript

I have a question, it is possible to set dynamic variable or change variable from JS in post-css?
I have react component and inside css3 animation, I want set dynamic delay for each animation individually for each component.
I found similar solution, that I can pass property and read it from css for example JS code:
<div className={style.component} delay={5}>
and I can read it in CSS but only like if statement:
&[delay="5"] {
animation: show 0.1s linear forwards 5s;
}
and it works!
but I want something like:
animation: show 0.1s linear forwards [delay]s; //dynamic delay value here
it is possible?
Cheers
Just pass the dynamic value '5' as a prop like this:
const App = (props) => {
return (
<div className={style.component} style={{animation: "show 0.1s linear forwards " + (props.delayStyle + 's')}}>
// Div Content Here
</div>
)
};
ReactDOM.render(<App delayStyle='5' />, document.getElementById('someDiv'));
CodePen: https://codepen.io/andrewl64/pen/dKzzaJ

ReactJS ReactCSSTransitionGroup only animating on initial load

I am trying to set up animated transitions on route change within my React / Redux app. I have been using examples on SO and the official tutorials as a guide but have so far only been able to get the fade in effect to work on the first load. When changing routes, the components are displayed without any animation in or out.
Am I missing something obvious- and is this the best path to follow when trying to animate the content in / out?
/*
** Loop through ACF components to layout page
*/
blocks = () => {
if (this.props.pages.pages[0]) {
return this.props.pages.pages[0].acf.components &&
this.props.pages.pages[0].acf.components.map((block, index) => {
let Component = componentMapping[block.acf_fc_layout];
return <Component key={index} data={block} id={index} />;
});
}
};
/*
** Render page on state change
*/
render() {
return (
<div className="App">
<NavContainer />
<ReactCSSTransitionGroup
transitionName="example"
transitionAppear={true}
transitionAppearTimeout={500}
transitionEnterTimeout={500}
transitionLeaveTimeout={300}
>
{this.blocks()}
</ReactCSSTransitionGroup>
</div>
);
}
The css I have included is as follows :
.example-enter {
opacity: 0.01;
}
.example-enter.example-enter-active {
opacity: 1;
transition: opacity 500ms ease-in;
}
.example-leave {
opacity: 1;
}
.example-appear {
opacity: 0.01;
}
.example-appear.example-appear-active {
opacity: 1;
transition: opacity .5s ease-in;
}
.example-leave.example-leave-active {
opacity: 0.01;
transition: opacity 300ms ease-in;
}
I appreciate any help that anybody could give. Thanks in advance!
Let's go through some troubleshooting steps to get to the bottom of this, but first, let me give you some basic tips on how the ReactCSSTransitionGroup component works.
<ReactCSSTransitionGroup/> needs to be present on the page with it's children being added and removed after mount. e.g. You have a list of employees that you are filtering based on an input box. As the filter controls what's being added and removed those items are coming and going and being animated. The <ReactCSSTransitionGroup/> must exist first and the component must be mounted.
With React Router, you're mounting and unmounting components as you change routes. If the <ReactCSSTransitionGroup/> is inside each component that is being mounted and unmounted the transition will not fire. I think this is what you are experiencing. I would suggest trying out this NPM package to help you take care of animations with route changes.
The issue with your animations only firing once is a separate issue than above. I'm concerned about this part:
return this.props.pages.pages[0].acf.components &&
this.props.pages.pages[0].acf.components.map((block, index) => {
let Component = componentMapping[block.acf_fc_layout];
return <Component key={index} data={block} id={index} />;
});
Returning a variable with the && operator can give you funny results. Try doing some explicit if statements. Or honestly just change your code to this:
if (this.props.pages.pages[0]) {
return this.props.pages.pages[0].acf.components.map((block, index) => {
let Component = componentMapping[block.acf_fc_layout];
return <Component key={index} data={block} id={index} />;
});
}
Another thought I had was to see what happens when you omit that component creation (componentMapping[block.afc_fc_layout]) and just return a <div>. See what happens and see if that's what's causing you trouble.

Angular way to do an Element Directive animation when Object is deleted?

I'm rendering an scoped Array of Objects(payments in this case), and passing each one to a payment Directive like this:
<div id="payable" ng-controller="PaymentsController">
<payment ng-repeat="payment in payments" data="payment" class="payment"></payment>
</div>
This works really well! So when I delete an element from the scoped Array from the controller like this:
app.controller('PaymentsController', function($scope) {
//The Payments Array(each object passed to a Directive)
$scope.payments = [ { id: 1, amount: 10 }, { id: 2, amount: 15 } ];
$scope.deletePayment = function(index) {
//This deletes the Array Element and removes associated
//Directive template from the DOM
$scope.payments.splice(index, 1);
}
});
The CSS (uses compass mixins for simplicity)
.payment.ng-enter {
#include transition(all 2s ease-out);
opacity: 0;
}
.payment.ng-enter-active {
opacity: 1;
}
.payment.ng-leave {
#include transition(all 2s ease-out);
}
.payment.ng-leave-active {
opacity: 0;
}
Again, the above works as expected, I delete an element from the payments Array and the directive-template/view corresponding to the deleted Array element is removed from the DOM, This is PERFECT, except for the fact it's removed instantly!
EDIT:
The reason the animations like fadeOut don't work and the result is that the ( < payment > ) is removed instantly(after a specified time in the CSS) is that the animation is acting over the ( < payment >) custom tag, which is just a wrapper for the actual element.
Directive JS definition:
(function() {
var app = angular.module('paymentDirectives', []);
app.directive('payment', function() {
return {
restrict: 'E',
scope: {
payment: '=data'
},
templateUrl: 'partials/payment.html'
};
});
})();
The animation should act on the template referenced/wrapped by the directive custom tag( < payment > )
partials/payment.html
<div class="a-payment">
<div class="content">
<p>
<label>{{payment.amount}}</label>
</p>
</div>
</div>
In this case it would be the div with class="a-payment" of course and when the animation is complete it should then remove the payment tag element
What is the Angular way(for the latest version) to do an animation for this case(ie. Element Directive is removed from the DOM)?
Thank you very much in advance, and let me know if you need more from the code I'm using.
This is likely to do with the fact that most custom tags, such as your <payment>, are display: inline; by default.
You should set their style to be display: block in the CSS/SASS.
You can do this in many ways, for example, you can create a class that will trigger the CSS animation, and before deleting the object, you first assign it that class. Here's how:
var deleteAnimDuration = 1000; // let's use one second for our example
$scope.deletePayment = function(index) {
//This deletes the Array Element and removes associated
//Directive template from the DOM
$scope.payments[index].deleteAnim = true; // or whatever property makes sense to you
$timeout(function(){
$scope.payments.splice(index, 1);
}, deleteAnimDuration);
}
Then on the directive, you can use ng-class:
<payment
ng-repeat="payment in payments"
data="payment"
ng-class="{deleting: payment.deleteAnim}">
</payment>
Then in the CSS:
payment.deleting {
transition: opacity 1s linear; // again, one second
opacity: 0;
}
Since this sample animation (opacity fade) will run for one second, you need to set deleteAnimDuration for the $timeout to one second (1000 in milliseconds).
So, what happens:
you click delete on a payment
it sets payment.deleteAnim to true, which assigns the deleting class to the element
the timeout for the animation duration is set
animation starts
animation ends
element removed from the DOM
This is the concept from the DOM standpoint:
var divs = document.getElementsByTagName('div');
for (var i = 0; i < divs.length; i++) {
divs[i].addEventListener('click', function(e) {
e.target.className = 'deleting';
deleteEl(this)
})
}
function deleteEl(el) {
setTimeout(function() {
el.parentElement.removeChild(el);
}, 1000);
}
.deleting {
transition: opacity 1s linear;
opacity: 0;
}
div {
width: 50px;
height: 50px;
display: inline-block;
background: #eee;
}
<div>Click me</div>
<div>Click me</div>
<div>Click me</div>
<div>Click me</div>
Of course, this can work with JS animations as well.
Load angular-animate.min.js in your HTML.
Add ngAnimate into your module dependencies.
Add a class to your payment directive element: e.g. <payment class="my-animation" ...></payment>.
Add the following CSS (referencing your class in step 3):
.my-animation.ng-leave { opacity: 1; transition: opacity 300ms linear; }
.my-animation.ng-leave.ng-leave-active { opacity: 0; transition: opacity 300ms linear; }
Celebrate

what is ng-hide-add, ng-hide-active

I'm animating a div. It has the following definition:
<div ng-show="showTranslations" ng-swipe-right="showTranslationsBlock=false">...</div>
I have the following css defined:
div.ng-hide {
transition: 0.5s linear opacity;
opacity: 0;
}
div.ng-hide-add,
div.ng-hide-remove {
/* this needs to be here to make it visible during the animation
since the .ng-hide class is already on the element rendering
it as hidden. */
display:block!important;
}
This is taken from this tutorial. The animation works. But:
Why do I need these classes .ng-hide-add and .ng-hide-remove?
Why I don't see them added to div's classes?
Why there are also classes ng-hide-add-active and ng-hide-remove-active?
Why there is no transition when the div becomes visible although I've added the following css rule:
div.ng-hide-remove {
opacity: 1;
}
UPDATE
As I can see from the table provided by google's tutorial these classes are added to trigger animation frame (this performs a reflow). Is my understanding correct? Why is animation frame is mentioned there?
I tried to increase the transition period but it didn't add the classes. I didn't see the classes ng-hide-add-active and ng-hide-remove-active added either.
As I understand from the table these are the classes that trigger transition?
UPDATE1
I've explored the Angular's source code and found the following for the ng-hide directive:
var ngHideDirective = ['$animate', function($animate) {
return function(scope, element, attr) {
scope.$watch(attr.ngHide, function ngHideWatchAction(value){
$animate[toBoolean(value) ? 'addClass' : 'removeClass'](element, 'ng-hide');
});
};
}];
As I understand the ng-hide class is added through animation service. But what happens if I don't use animations and $animate service is not available? How Angular is going to handle this situation given the code above and how it is going to add ng-hide class? Or is this $animate.addClass() simply adds a callback to addClass event?
Put your CSS transition on ng-hide-remove, ng-hide-remove-active:
div.ng-hide-remove {
transition: 0.5s linear opacity;
opacity: 0;
}
div.ng-hide-remove-active {
opacity: 1;
}
Similarly, for ng-hide-add and ng-hide-add-active:
div.ng-hide-add {
transition: 0.5s linear opacity;
opacity: 1;
}
div.ng-hide-add-active {
opacity: 0;
}

Categories

Resources