I have a list of Words, shown or hidden by *ngFor according to their hidden property.
Please check plunker.
The problem is that when there are a huge amount of words in list, it is difficult to find where the new one appeared. So I would like to highlight them.
My idea was to set a .highlight class by default and to remove it after item was added, with smooth transition:
HTML
<div
*ngFor="let item of words | shownWords"
[ngClass]="{'item':true, 'highlihted': item.hidden}">
{{item.value}}
</div>
CSS
.item {
background-color: #ffffff;
transition: background .3s ease-out;
-moz-transition: background .3s ease-out;
-webkit-transition: background .3s ease-out;
-o-transition: background .3s ease-out;
}
.item.highlihted {
background-color: #ea90aa;
}
Unfortunately this doesn't work, as new item is already created without .highlight class.
So, here is the question: how to detect when a new item was added and which item exactly was added? Is it even possible? Or may be there is another way to achieve what I want?
P. S. please tell me if I attached incorrect plunker or something else wrong with my question.
Solution
First of all sorry for incomplete description, I forget to mention that I want to switch off a hightlight after a while.
Please check #Boyan Kostadinov's answer and then return here for a final solution and final plunker:
new toggleHidden():
toggleHidden(item:Word) {
item.hidden= !item.hidden;
if (!item.hidden) {
item.highlihted = true;
setTimeout(() => {
item.highlihted = false;
}, 500);
}
}
This will get you what you want: http://plnkr.co/edit/QxvCte7SvYg3hljBPIlB?p=preview
Explanation:
Added new "highlighted" property to your words class:
export class Word {
value:string;
hidden = false;
highlihted = false;
constructor(value: string) {
this.value = value;
}
}
Changed the toggleHidden function:
#Component({
selector: 'my-app',
pipes: [ShownWords],
template: `
<h2>Click on a word above line to show/hide word below line:</h2>
<div *ngFor="let item of words" (click)="toggleHidden(item)">{{item.value}}</div>
<hr>
<div
*ngFor="let item of words | shownWords"
[ngClass]="{item:true, highlihted: item.highlihted}">
{{item.value}}
</div>
`
})
export class AppComponent {
words: Word[] = [new Word('one'),new Word('two'),new Word('three'),new Word('four'),new Word('five')];
toggleHidden(item:Word) {
this.words.forEach((w:Word) => { w.highlihted = false });
item.hidden= !item.hidden;
if (!item.hidden) item.highlihted = true;
}
}
Related
I have a React app that is using the CSSTransition component from react-transition-group: when the component appears, everything behaves as expected (a 0.5s transition from opacity: 0 to opacity: 1), however when the component exits, the transition is not applied and it just immediately disappears. Can anybody help me figure out why?
Render method in component:
render(){
const countries = geoUrl.objects.ne_50m_admin_0_countries.geometries;
const { handleEnter, handleList, list } = this.props;
return (
<CSSTransition
classNames="transition"
transitionAppearTimeout={50000}
timeout={500000}
key={ list }
in={ list } // this is a boolean value passed from the parent component, it is initially set to false but changes to true when this component is rendered
unmountOnExit
appear
>
<div className="overlay" id="list">
<div className="wrapper" ref={this.setWrapperRef}>
<aside className="list">
<a className="close" href="#home" onClick={handleList}>×</a>
<ul className="countryList">
{ countries.sort((a, b) => (a.properties.NAME > b.properties.NAME) ? 1 : -1).map(geo =>
geo.properties.COUNTRY ?
<li className="listItem" key={ `${geo.properties.ISO_A3}${geo.properties.name}` }><a href="#country" onClick={() => {
const { NAME, DISH, DESCRIPTION, PHOTO, RECIPE } = geo.properties;
handleEnter(NAME, DISH, DESCRIPTION, PHOTO, RECIPE);
}}>{ geo.properties.NAME }</a></li>
: null
)}
</ul>
</aside>
</div>
</div>
</CSSTransition>
);
}
CSS:
.transition-appear {
opacity: 0.01;
}
.transition-appear.transition-appear-active {
opacity: 1;
transition: opacity .5s ease-in;
}
.transition-enter {
opacity: 0;
}
.transition-enter-active {
opacity: 1;
transition: opacity .5s ease-in;
}
.transition-exit {
opacity: 1;
}
.transition-exit-active {
opacity: 0;
transition: opacity .5s ease-in;
}
I also struggled with the transition out, but have come up with a solution. I can't tell what your final result should look like in the UI, whether you want to transition in between conditionally rendered elements on the page, or just a transition when the component mounts and unmounts, but I think you want to do the former.
To transition between two or more elements on the same page, you will need to wrap your <CSSTransition> tags in <TransitionGroup> tags (and import it alongside CSSTransition). When you do this, you will need to provide a unique key property to the <CSSTransition> tag, it can't just be a boolean like it looks like you have. You also will need to slightly modify your CSS.
.transition-enter {
opacity: 0;
}
.transition-enter.transition-enter-active {
opacity: 1;
transition: opacity 0.5s ease-in;
}
.transition-enter-done {
opacity: 1;
}
.transition-exit {
opacity: 1;
}
.transition-exit.transition-exit-active {
opacity: 0;
transition: opacity 0.5s ease-in;
}
.transition-exit-done {
opacity: 0;
}
And the Transition tags:
import {CSSTransition, TransitionGroup} from "react-transition-group"
//...the rest of your code
return (
<TransitionGroup>
<CSSTransition
key={foo} //something unique to the element being transitioned
classNames="transition"
timeout={500}
>
<div className="overlay" id="list">
<div className="wrapper" ref={this.setWrapperRef}>
<aside className="list">
<a className="close" href="#home" onClick={handleList}>×</a>
<ul className="countryList">
{ countries.sort((a, b) => (a.properties.NAME > b.properties.NAME) ? 1 : -1).map(geo =>
geo.properties.COUNTRY ?
<li className="listItem" key={ `${geo.properties.ISO_A3}${geo.properties.name}` }><a href="#country" onClick={() => {
const { NAME, DISH, DESCRIPTION, PHOTO, RECIPE } = geo.properties;
handleEnter(NAME, DISH, DESCRIPTION, PHOTO, RECIPE);
}}>{ geo.properties.NAME }</a></li>
: null
)}
</ul>
</aside>
</div>
</div>
</CSSTransition>
</TransitionGroup>
)
Please note: You do not need appear, in, or unmountOnExit properties for this method, but you will have issues with duplicate elements appearing in the DOM during the transition, becuase react-transition-group actually clones the element and deletes the old one (which is why it needs a unique key). The only way to achieve a cross-fade transition is to take your elements out of the document flow with position absolute, so they overlap as one transitions in, and the other out.
I can't test your exact code, because there is not enough information, but I put together a very basic codesandbox that demonstrates the method with 2 conditionally rendered elements:
https://codesandbox.io/s/long-sea-9rtw9?file=/src/Test.js
I have a component where when o clicking the button, the div with more info will slideUp and slideDown.
Below the code and css style
import { CSSTransition } from "react-transition-group";
const Card = () => {
const [showMoreInfo, setShowMoreInfo] = useState(false);
return (
<div className="Card">
<ButtonShowMore isOpen={showMoreInfo} click={() => setShowMoreInfo(!showMoreInfo)} />
<CSSTransition in={showMoreInfo} classNames="Card-Details" timeout={1000}>
<div>
{showMoreInfo && (
<>
<p>details</p>
<p>details</p>
</>
)}
</div>
</CSSTransition>
</div>
);
};
.Card-Details-enter {
height: 0px;
}
.Card-Details-enter-active {
height: 100%;
-webkit-transition: height 1s ease;
-moz-transition: height 1s ease;
-o-transition: height 1s ease;
transition: height 1s ease;
}
.Card-Details-enter-done {
height: 100%;
}
.Card-Details-exit {
height: 100%;
}
.Card-Details-exit-active {
height: 0px;
-webkit-transition: height 1s ease;
-moz-transition: height 1s ease;
-o-transition: height 1s ease;
transition: height 1s ease;
}
.Card-Details-exit-done {
height: 0px;
}
But it doesnt work, I don't know why. I tred to put the transition to the parent element like here
and add transition to the class *-exit-done like here, and nothing helped.
The reason why it doesn't work is because transitioning on percentage height isn't quite what you would expect.
Percentage height and width in CSS refer to their parent's height and width rather than their own height.
MDN Percentage
The CSS data type represents a percentage value. It is
often used to define a size as relative to an element's parent object.
Numerous properties can use percentages, such as width, height,
margin, padding, and font-size.
Example with the same element transitioning using a percentage height/width. One has a container element with a height/width of 50px, and the other doesn't.
div.container {
width: 50px;
height: 50px;
}
div.transition {
width: 100%;
height: 100%;
background: red;
transition: width 2s, height 4s;
}
div.transition:hover {
width: 300%;
height: 500%;
}
Transition div with a 50px container
<div class="container">
<div class="transition">
<p>test</p>
</div>
</div>
Transition div without a container:
<div class="transition">
<p>test</p>
</div>
What we'd actually want is to transition from 0px to auto height. Unfortunately browsers don't support transitioning on auto height.
A good write up of this is in Using CSS Transitions Auto Dimensions include some approaches to get what you want and their downsides.
Why hasn’t this problem been fixed at the browser level?
According to the Mozilla Developer Network docs, auto values have been
intentionally excluded from the CSS transitions spec. It looks like
it’s been requested by a few people, but when you think about it, it
makes at least a little sense that it hasn’t been included. The
browser process that re-calculates the sizes and positions of all
elements based on their content and the way they interact with each
other (known as “reflow”) is expensive. If you were to transition an
element into a height of auto, the browser would have to perform a
reflow for every stage of that animation, to determine how all the
other elements should move. This couldn’t be cached or calculated in a
simple way, since it doesn’t know the starting and/or ending values
until the moment the transition happens. This would significantly
complicate the math that has to be done under the hood and probably
degrade performance in a way that might not be obvious to the
developer.
How can I transition height: 0; to height: auto; using CSS? also has some good workarounds, though there really is no magic bullet for this.
It is definitely a very well known issue, and there's a request for the spec to change to allow transitions on auto, though I don't think it's gone anywhere yet.
As for support for the type of transition you are working on in React Transition Group:
Slide Down Animation and Trying to fade out element then slide up both have the same answer overall: pointing at how React Bootstrap's Collapse component does it.
You need to rely on finding the dom node's actual height and using that as part of the transition:
getDimension() {
return typeof this.props.dimension === 'function'
? this.props.dimension()
: this.props.dimension;
}
// for testing
_getScrollDimensionValue(elem, dimension) {
return `${elem[`scroll${capitalize(dimension)}`]}px`;
}
/* -- Expanding -- */
handleEnter = (elem) => {
elem.style[this.getDimension()] = '0';
}
handleEntering = (elem) => {
const dimension = this.getDimension();
elem.style[dimension] = this._getScrollDimensionValue(elem, dimension);
}
handleEntered = (elem) => {
elem.style[this.getDimension()] = null;
}
/* -- Collapsing -- */
handleExit = (elem) => {
const dimension = this.getDimension();
elem.style[dimension] = `${this.props.getDimensionValue(dimension, elem)}px`;
triggerBrowserReflow(elem);
}
handleExiting = (elem) => {
elem.style[this.getDimension()] = '0';
}
A quick and dirty example of using the functionality from the Collapse class for a working example of the code using a less fully featured solution (note, based heavily on the Collapse.js code linked above):
const { Transition } = ReactTransitionGroup;
const { EXITED, ENTERED, ENTERING, EXITING } = Transition;
const { useState } = React;
// Quick and dirty classNames functionality
const classNames = (...names) => names.filter((name) => name).join(' ');
const ButtonShowMore = ({ isOpen, click }) => {
return <button onClick={click}>{isOpen ? 'Close' : 'Open'}</button>;
};
// Heavily based on https://github.com/react-bootstrap/react-bootstrap/blob/next/src/Collapse.js#L150
// for the purpose of demonstration without just pulling in the module:
function triggerBrowserReflow(node) {
node.offsetHeight; // eslint-disable-line no-unused-expressions
}
const collapseStyles = {
[EXITED]: 'collapse',
[EXITING]: 'collapsing',
[ENTERING]: 'collapsing',
[ENTERED]: 'collapse in',
};
const Collapse = ({ children, ...props }) => {
const handleEnter = (elem) => (elem.style.height = '0');
const handleEntering = (elem) =>
(elem.style.height = `${elem.scrollHeight}px`);
const handleEntered = (elem) => (elem.style.height = null);
const handleExit = (elem) => {
elem.style.height = `${elem.scrollHeight}px`;
triggerBrowserReflow(elem);
};
const handleExiting = (elem) => (elem.style.height = '0');
return (
<Transition
{...props}
onEnter={handleEnter}
onEntering={handleEntering}
onEntered={handleEntered}
onExit={handleExit}
onExiting={handleExiting}
>
{(state, innerProps) =>
React.cloneElement(children, {
...innerProps,
className: classNames(
props.className,
children.props.className,
collapseStyles[state]
),
})
}
</Transition>
);
};
const Card = () => {
const [showMoreInfo, setShowMoreInfo] = useState(false);
return (
<div className="Card">
<ButtonShowMore
isOpen={showMoreInfo}
click={() => setShowMoreInfo(!showMoreInfo)}
/>
<Collapse in={showMoreInfo} className="Card-Details" timeout={1000}>
<div style={{ height: 0 }}>
<p>details</p>
<p>details</p>
</div>
</Collapse>
</div>
);
};
ReactDOM.render(<Card />, document.querySelector('#root'));
.collapsing {
-webkit-transition: height 1s ease;
-moz-transition: height 1s ease;
-o-transition: height 1s ease;
transition: height 1s ease;
overflow: hidden;
}
.collapse {
overflow: hidden;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.13.1/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.13.1/umd/react-dom.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-transition-group/4.4.1/react-transition-group.min.js"></script>
<div id="root" />
I've written a React app, using CSS transitions. But those transitions does not work correctly in some of the components. In my app, only the components who are moving upwards works well, those who are moving downwards moves instantly without animation. (I want them both moves with animation.)
Here is the CSS I used there:
div.canvas {
position: absolute;
top: 90px;
left: 60px;
width: 640px;
height: 480px;
border: 1px solid #999;
background: white;
}
div.canvas-rect {
position: relative;
margin-left: 20px;
margin-top: 10px;
height: 20px;
background: green;
transition: all 1s linear;
-moz-transition: all 1s linear; /* Firefox 4 */
-webkit-transition: all 1s linear; /* Safari 和 Chrome */
-o-transition: all 1s linear; /* Opera */
}
UPDATED:
I also built a codepen.io project to show the problem. It has the complete code of this demo project.
I've tried to add a log entry to componentDidUpdate, componentDidMount and componentWillUnmount methods to show whether these component are re-created or updated, it shows that they are all updated (not re-created, or removed) every second.
Well, after I started a bounty because I also have this problem I finally found what seems to be the problem.
When you are using absolute position (or relative, as in your case), if you re-render the whole list every time, React will re-order the elements in the DOM (as you said, the elements are not being recreated, just updated). But this creates the problem with the transitions... apparently, if you move an element while the transition is running then you end up cutting the animation.
So, for cases in which you want to use position absolute, the key concept is to render the containers of your elements once (in this case, just divs) and only change the inner contents based on the new order. If you need to add more elements, just add them at the end.
I modified your codepen so that it reflects what I am saying. My example is very dumb because I just created 4 ad-hoc divs, but it illustrates the idea: create as many containers as you need, but DO NOT use a map that recreates them every time, or your transitions will be cut.
https://codepen.io/damianmr/pen/boEmmy?editors=0110
const ArrList = ({
arr
}) => {
return (
<div style={{position: 'relative'}}>
<div className={`element element-${arr[0]} index-${arr[0]}`}>{arr[0]}</div>
<div className={`element element-${arr[1]} index-${arr[1]}`}>{arr[1]}</div>
<div className={`element element-${arr[2]} index-${arr[2]}`}>{arr[2]}</div>
<div className={`element element-${arr[3]} index-${arr[3]}`}>{arr[3]}</div>
</div>
);
}
So, the problem is basically how you create a static list of containers and how you iterate through that list so that the first container renders the first element of your data, the second container the second element, etc.
Hope that it helps, this problem was driving me crazy too! :)
I know this wasn't the case, but since I got here also looking for React css transition does not work correctly, I just wanted to share:
If you create an element using arrow functions inside render, it won't get properly animated, since a new componente is always being created.
You should create a function outside and invoke it in 'render'.
You can trick React by using index as key. If you think about el, and index as starting position (index) and end position (el), the element has moved to the old end position by the end of the transition, and by when it's there, it's taken over by the new start position and (index) is switched to match the new setup. This is because when you set key in an element in react, the virtual DOM will always interpret it as it is the same element. And for the sake of it, you're right in setting index as the "id" in general.
I made a working example only by switching index/el (and setting element position to absolute).
const {combineReducers, createStore} = Redux;
const { Provider, connect } = ReactRedux;
const ArrList = ({
arr
}) => (
<div>{
arr.map((el, index)=>
<div
key={""+index}
className={`element element-${el}` + ` index-${el}`}
>
{el}
</div>) }
</div>
)
const mapStateToArrList = (state) => {
return {
arr: state.appReducer.arr
}
};
const App = connect(mapStateToArrList, null)(ArrList);
const initialState = {
arr: [1, 2, 3, 4]
}
const appReducer = (state = initialState, action) => {
switch(action.type) {
case "tick":
return {
...state,
arr: _.shuffle(state.arr)
}
default:
return state
}
}
const reducer = combineReducers({
appReducer
})
const store = createStore(reducer)
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root')
)
const dispatcher = () => {
store.dispatch({
type: "tick"
})
setTimeout(dispatcher, 1000)
}
dispatcher()
.element {
position: absolute;
height: 20px;
background: green;
margin-left: 20px;
margin-top: 20px;
text-align: right;
color: white;
line-height: 20px;
transition: all 1s ease-in;
-moz-transition: all 1s ease-in; /* Firefox 4 */
-webkit-transition: all 1s ease-in; /* Safari 和 Chrome */
-o-transition: all 1s ease-in; /* Opera */
}
.element-1 {
width: 20px;
}
.element-2 {
width: 40px;
}
.element-3 {
width: 60px;
}
.element-4 {
width: 80px;
}
.index-1 {
top: 20px;
}
.index-2 {
top: 40px;
}
.index-3 {
top: 60px;
}
.index-4 {
top: 80px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/redux/3.7.2/redux.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-redux/5.0.6/react-redux.js"></script>
<div id="root"></div>
If you are removing the element from the virtual DOM, then the react will update its contents, so you won't see the animations. What you can do is either use react-transition-group OR tell your app to wait x ms before updating the dom once the event is called OR use visibility to toggle between hidden and showing instead of removing it completely from the DOM.
You did recreate DOM elements each time.
You should define collect key value.
I changed your key value '' + el to '' + index.
<div key={'' + index} className={'element element-' + el + ' index-' + index} >
Just change css properties only :)
I''ll just leave that here in case helps somebody but to me it was solved by moving from
export default function Contact(props) {...}
To:
const Contact = (props) => {...}
export default Contact
I'm looking to scroll through a story, but let the user press (space) for the next phrase. I think it could be a beautiful optional intro on a website.
Already done: I've managed to scroll through an array of strings as the user presses (space):
CodePen: https://codepen.io/anon/pen/PbRLdp
React / JS:
var strings = ["Hi","it's not easy finding a freelancer, is it?", "referrals don't always come", "you need to know it'll get done", "I get it.", "perhaps we should connect"];
var i = 0;
var hitElement = document.querySelector( '.storylines' );
document.body.onkeyup = function(e) {
if( e.keyCode == 32 ) {
addHit();
}
}
var addHit = function() {
if ( i+2 <= strings.length) {
i++
renderStories();
}
}
var renderStories = function() {
hitElement.innerHTML = strings[i];
}
HTML:
<span class="storylines">press (spacebar)</span>
My question: How can I use React to create a transition between phrases? I'm thinking of translating / fading the current span downwards, and fading the new span in (without translating it).
I tried to change your question as little as possible since I'm not that familiar with reactJS.
I would do it only by css. This will allow you to change what each transition focuses on without too much code added (opacity vs. opacity and placement).
var strings = ["Hi", "it's not easy finding a freelancer, is it?", "referrals don't always come", "you need to know it'll get done", "I get it.", "perhaps we should connect"];
var i = 0;
var hitElement = document.querySelector('.storylines');
document.body.onkeyup = function(e) {
if (e.keyCode == 32) {
addHit();
}
}
var addHit = function() {
if (i + 2 <= strings.length) {
i++
renderStories();
}
}
var renderStories = function() {
hitElement.classList.remove('enter');
hitElement.classList.add('hide');
setTimeout(function() {
hitElement.innerHTML = strings[i];
hitElement.classList.remove('hide');
hitElement.classList.add('enter');
}, 250);
}
.storylines {
display: inline-block;
transition: opacity 250ms linear 150ms;
opacity: 0;
}
.storylines.hide {
transition: all 250ms linear;
transform: translateY(15px);
opacity: 0;
}
.storylines.enter {
opacity: 1;
}
<span class="storylines enter">press (spacebar)</span>
NOTICE: click on Expand snippet to not have the spacebar scroll the stackoverflow page :-)
This is the logic flow:
The page starts with the storylines class and the enter class.
This is to have the 1st element already shown.
When the change occurs, remove the enter class, and add the hide class to make the class leave.
hide will make sure the element disappears and moves down. enter is removed so it won't override the opacity of the element.
Also, hide contains a different transition that will animate all changes. This allows the transform to also move the element on exit.
After a timeout, add the enter class and remove the hide class
Here, since we remove the hide class, the transition changes to animate only opacity. So, the element appears in place instead of moving up or down.
NOTICE that the enter animation as a delay value added, so it won't happen in the same time as the hide animation.
transition: opacity 250ms linear 150ms;
Also, you can change the animations using css only, which I think is a good separation of roles.
Try this on your last function:
var renderStories = function() {
hitElement.style.opacity = 0;
setTimeout(function(){
hitElement.innerHTML = strings[i];
hitElement.style.opacity = 1;
}, 600);
}
An this in your CSS:
.storylines {
transition: all 0.6s;
opacity: 1;
}
The JS timeout should be exactly the transition time for your CSS element.
EDIT: With translate
var renderStories = function() {
hitElement.style.opacity = 0;
hitElement.style.transform = "translateY(-100%)";
setTimeout(function(){
hitElement.innerHTML = strings[i];
hitElement.style.opacity = 1;
hitElement.style.transform = "translateY(0%)";
}, 600);
}
CSS
.storylines {
transition: all 0.6s;
opacity: 1;
display: block; /* the element needs to be seen as solid */
}
EDIT 2: Changed the location so the new line appears in the same place:
var renderStories = function() {
hitElement.style.opacity = 0;
hitElement.style.transform = "translateY(-100%)";
setTimeout(function(){
hitElement.style.trasition = "opacity 0.3s, transform 0s"
hitElement.style.transform = "translateY(0%)";
}, 300);
setTimeout(function(){
hitElement.innerHTML = strings[i];
hitElement.style.opacity = 1;
hitElement.style.trasition = "opacity 0.3s, transform 0.3s"
}, 600);
}
CSS:
.storylines {
transition: opacity 0.3s, transform 0.3s;
opacity: 1;
display: block; /* the element needs to be seen as solid */
}
If you want it slower make sure that the 600 is the double of 0.3 or 300 with whatever value you're changing.
I have a simple scope of a list of names:
$scope.friends = [
{name:'John'},
{name:'Jessie'},
{name:'Johanna'},
{name:'Joy'},
{name:'Mary'},
{name:'Peter'},
{name:'Sebastian'},
{name:'Erika'},
{name:'Patrick'},
{name:'Samantha'}
];
I perform a simple ng-repeat over these items, and display on my front end. What i want to try to achieve is that each item/name transitions in, one after each other, like a chain.
The closet to the effect i have seen can be found here: http://jsfiddle.net/57uGQ/ however this uses jQuery libraries which i want to avoid.
I am using the ng-animate library if that helps?
See plunker: http://plnkr.co/edit/aGwEe2jlGBTvjoSsiK9p?p=preview
You can use a combination of ng-if, $timeout and the .ng-enter-stagger css class to replicate the effect in the jsfiddle example.
html:
<div ng-controller="repeatController">
<ul>
<li class="animate-repeat" ng-repeat="friend in friends" ng-if="!hidden">
{{friend.name}},
</li>
</ul>
</div>
js:
angular.module('ngRepeat', ['ngAnimate']).controller('repeatController', function($scope, $timeout) {
$scope.friends = [
{name:'John'},
{name:'Jessie'},
{name:'Johanna'},
{name:'Joy'},
{name:'Mary'},
{name:'Peter'},
{name:'Sebastian'},
{name:'Erika'},
{name:'Patrick'},
{name:'Samantha'}
];
$scope.hidden = true;
$timeout(function(){
$scope.hidden = false;
}, 10);
});
css:
.animate-repeat.ng-enter {
transition: 1s linear all;
opacity: 0;
}
.animate-repeat.ng-enter-stagger {
transition-delay: 1.2s;
transition-duration: 0s;
}
.animate-repeat.ng-enter.ng-enter-active {
opacity: 1;
}
Plunker