I want to achieve a smooth resorting of elements in an ng-repeat.
I have this code but it results in 2 odd behaviours:
The elements are only animating vertically downwards
The elements are only animating if I change the model.countryData within the interval of the CSS transition (i.e. after 2 consecutive and quick changes)
ng-repeat
<div ng-repeat="country in model.countryData" class="repeat-animation" ng-style="{ 'top' : yPos($index) }" style="position: absolute; left: 0px;">
{{ country.name }}
</div>
CSS
.repeat-animation.ng-enter,
.repeat-animation.ng-leave,
.repeat-animation.ng-move {
-webkit-transition: all linear 2s;
-moz-transition: all linear 2s;
-o-transition: all linear 2s;
transition: all linear 2s;
}
I suspect this could be related to how I sort the underlying data and/or version issues with angular animate and/or faulty CSS syntax?
All help appreciated!
Related
I have an array of items in ng-repeat.
This parent element also has a background-transition animation with 1 second duration.
When ever I delete the element using splice, it takes 1 second to remove from the UI. (Based on what time I give for Transition duration)
I don't want to add another class to remove the transition first, and then delete. Or is that the only way?
<div class="MyTransitionClass" ng-repeat="d in myArray">
{{d.Value}}
<button type="button" class="CloseIcon" ng-click="DeleteItem($index)">DELETE</button>
</div>
.MyTransitionClass {
transition: background-color ease-in 1s;
-webkit-transition: background-color ease-in 1s;
}
Try it like this https://docs.angularjs.org/api/ngAnimate
.MyTransitionClass.ng-enter {
transition: background-color ease-in 1s;
-webkit-transition: background-color ease-in 1s;
}
Working jsfiddle http://jsfiddle.net/irhabi/3fgtqwgq/
I have made the animation of a showing/hiding a list of messages. See this plunk. But how can I adapt it to also make an animation when a message is removed from the list?
My css:
.messages-active.messages {
max-height: 50px;
}
.messages {
-webkit-transition: max-height 1s;
-moz-transition: max-height 1s;
-ms-transition: max-height 1s;
-o-transition: max-height 1s;
transition: max-height 1s;
background-color: AntiqueWhite;
overflow: hidden;
max-height: 0;
}
My index file (using Angular):
<body ng-app="app" ng-controller="TestCtrl as test">
<button ng-click="test.toggle = !test.toggle">Show messages</button>
(current: {{test.toggle}})
<div class="messages" ng-class="{ 'messages-active': test.toggle }" ng-repeat="message in test.messages">
{{message}} <a href ng-click="test.remove($index)">remove</a>
</div>
</body>
The idea is to set the height of container and add transition to the height.
$scope.styles.height = $scope.messages.length * 20 + 'px';
http://plnkr.co/edit/3dnGeVoQ1DbX55WQtJjk?p=preview
You can try following if it may help you.
On clicking remove instead removing element just add class messages-remove on it's parent div messages.
For e.g: It should become <div class="messages" to <div class="messages messages-remove".
Also add the following CSS in your style sheet.
.messages-active.messages-remove.messages,
.messages-remove.messages { max-height: 0px; }
Let me know if you have any question.
I have simple css animation effect:
.sample-show-hide {
-webkit-transition:all linear 1.5s;
transition:all linear 1.5s;
}
.sample-show-hide.ng-hide {
opacity: 0;
}
.sample-show-hide.ng-show {
opacity: 1;
}
I tried to animate md-button with it. Animation is fine for showing, but on hidding there is no effect. When I changed to simple <button> tag, animation works for both show/hide. Is there way to fix it?
ng-hide have property transition: none, use the following css
.sample-show-hide {
-webkit-transition:all linear 1.5s !important;
transition:all linear 1.5s !important;
}
check on jsfiddle
I would like to see a functional example of using angular-animate (1.2x) to sort a list. (I have only come across broken fiddles etc on the interwebs):
An ng-repeat given an array [A,B,C] and later [C, B, A] should:
Move A to the bottom
Move C to the top
Keep B's position
(Using CSS absolute top positioning or similar.)
An example using staggering (transition-delay) is a bonus.
The problem
Achieving what you want can be a bit tricky.
A common attempt is to use ng-style to calculate the element´s position based on its index in the list:
<div ng-repeat="c in countries | orderBy:q" ng-style="{ 'top': $index * 20 + 'px' }">
Demo: http://plnkr.co/edit/anv4fIrMxVDWuov6K3sw?p=preview
The problem is that only some elements are animated, and only towards the bottom.
Why is that?
Consider the following list sorted by name (similar to the one from the demo above):
2 - Denmark
3 - Norway
1 - Sweden
When you sort this list by id instead only one element will move - Sweden from bottom to top. What actually happens is that the Sweden element is removed from the DOM and inserted again at its new position. However, when an element is inserted into the DOM the associated CSS transtions will normally not occur (I say normally as it ultimately depends on how the browser in question is implemented).
The other two elements remain in the DOM, get new top positions and their transitions are animated.
So with this strategy the transitions are only animated for the elements that didn't actually move in the DOM.
Another strategy is to include the ngAnimate module and use that CSS class ng-move. Almost all examples of animated ng-repeats use this.
However, this will not work because of two reasons:
The ng-move class would only be applied to the elements that move (so only to the Sweden element in the example above)
The ng-move class is applied to the element after it has been inserted into its new position in the DOM. You can have CSS that says "animate from opacity 0 to 1", but you can't have "animate from old position to new" since the old position is not known and each element would have to move a different distance.
A solution
A solution I've used myself in the past is to use ng-repeat to render the list but never actually resorting the underlying data. This way all the DOM elements will remain in the DOM and can be animated. To render the elements correctly use ng-style and a custom property, for example:
ng-style="{ 'top': country.position * 20 + 'px' }"
To update the position property do the following:
Create a copy of the underlying data
You could use angular.copy to copy the entire array, but with large arrays this wouldn't be good for performance. It would also be unnecessary since each object in the copied array would only need a property that is unique and the property to sort by:
var tempArray = countries.map(function(country) {
var obj = {
id: country.id
};
obj[property] = country[property];
return obj;
});
In the example above id is the unique property and property is a variable containing the name of the property to sort by, for example name.
Sort the copy
To sort the array use Array.prototype.sort() with a compare function:
tempArray.sort(function(a, b) {
if (a[property] > b[property])
return 1;
if (a[property] < b[property])
return -1;
return 0;
});
Set position to the element's index in the sorted copy
countries.forEach(function(country) {
country.position = getNewPosition(country.id);
});
function getNewPosition(countryId) {
for (var i = 0, length = tempArray.length; i < length; i++) {
if (tempArray[i].id === countryId) return i;
}
}
There is room for improvement, but that is the basics of it.
Demo: http://plnkr.co/edit/2Ramkg3sMW9pds9ZF1oc?p=preview
I implemented a version that used staggering, but it looked a bit weird since elements would overlap each other momentarily.
have a look on this, its very easy:
CSS:
.animate-enter,
.animate-leave
{
-webkit-transition: 400ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all;
-moz-transition: 400ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all;
-ms-transition: 400ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all;
-o-transition: 400ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all;
transition: 400ms cubic-bezier(0.250, 0.250, 0.750, 0.750) all;
position: relative;
display: block;
}
.animate-enter.animate-enter-active,
.animate-leave {
opacity: 1;
top: 0;
height: 30px;
}
.animate-leave.animate-leave-active,
.animate-enter {
opacity: 0;
top: -50px;
height: 0px;
}
HTML:
<!doctype html>
<html ng-app>
<head>
<meta charset="utf-8">
<title>Top Animation</title>
<script>document.write('<base href="' + document.location + '" />');</script>
<link rel="stylesheet" href="style.css">
<link href="//netdna.bootstrapcdn.com/twitter-bootstrap/2.3.1/css/bootstrap-combined.min.css" rel="stylesheet">
<script src="http://code.angularjs.org/1.1.5/angular.js"></script>
</head>
<body ng-init="names=['Igor Minar', 'Brad Green', 'Dave Geddes', 'Naomi Black', 'Greg Weber', 'Dean Sofer', 'Wes Alvaro', 'John Scott', 'Daniel Nadasi'];">
<div class="well" style="margin-top: 30px; width: 200px; overflow: hidden;">
<ul class="nav nav-pills nav-stacked">
<li ng-animate="'animate'" ng-repeat="name in names">
{{name}}
</li>
</ul>
</form>
</div>
</body>
</html>
I have looked at the official show/hide transition example at the bottom of this page... http://docs.angularjs.org/api/ng.directive:ngShow
I have tried to modify it to get a seemless fade transition (transition: opacity 0.5s ease-in-out) from one div to the other, where both divs occupy the exact same position on the page, so that one div completely fades out before the other div begins to fade in.
In jquery, it would be as simple as:
$("#divA").fadeOut(function() { $("divB").fadeIn(); });
Does anyone have any advice on the best way to achieve this with angular, with respect to the linked example, which uses a single model "checked" to trigger the transition?
I used the example in ngShow to make the following jsfiddle based on angular1.2.0-rc.3.
The html code:
<div ng-app="App">
Click me: <input type="checkbox" ng-model="checked"><br/>
<div class="check-element animate-show" ng-show="checked">
<span class="icon-thumbs-up"></span> I show up when your checkbox is checked.
</div>
<div class="check-element animate-show" ng-hide="checked">
<span class="icon-thumbs-down"></span> I hide when your checkbox is checked.
</div>
</div>
The CSS styles
.animate-show.ng-hide-add,
.animate-show.ng-hide-remove {
-webkit-transition:all linear 0.5s;
-moz-transition:all linear 0.5s;
-o-transition:all linear 0.5s;
transition:all linear 0.5s;
display:block!important;
}
.animate-show.ng-hide-add.ng-hide-add-active,
.animate-show.ng-hide-remove {
line-height:0;
opacity:0;
padding:0 10px;
}
.animate-show.ng-hide-add,
.animate-show.ng-hide-remove.ng-hide-remove-active {
line-height:20px;
opacity:1;
padding:10px;
border:1px solid black;
background:white;
}
.check-element {
padding:10px;
border:1px solid black;
background:white;
}
And finally the JavaScript code, don't forget to include the libraries angular.js and angular-animate.js
angular.module('App', ['ngAnimate']);
I hope it helps you ;)
Using the ngAnimate module, you can do this in pure CSS with the -transition-delay directive:
Plunker
HTML
<body ng-app="ngAnimate">
Click me: <input type="checkbox" ng-model="checked">
<br/>
<img ng-show="checked" src="img1.jpg">
<img ng-hide="checked" src="img2.jpg">
</body>
CSS
img {
position: absolute;
}
.ng-hide-add-active {
display: block!important;
-webkit-transition: 0.5s linear all;
transition: 0.5s linear all;
}
.ng-hide-remove-active {
display: block!important;
-webkit-transition: 0.5s linear all;
transition: 0.5s linear all;
-webkit-transition-delay: 0.5s;
transition-delay: 0.5s;
}
.ng-hide {
opacity: 0;
}
You can use ng-animate in conjuction with ng-show (http://docs.angularjs.org/api/ngAnimate), available from Angular 1.1.4. Or alternatively simply apply a show class when the model is ticked and apply your show and animation to the class.
<label><input type="checkbox" ng-model="showElement" />Show div</label>
<div ng-class="{show: showElement}"></div>