I'm using the ngAnimate module, but all my ng-if, ng-show, etc, are affected by that, I want to leverage ngAnimate for some selected elements.
For performance and some bugs in elements that shows and hide very speedy.
thanks.
If you want to enable animations for specific elements (as opposed to disabling them for specific elements) you can use the $animateProvider to configure elements with a particular class name (or regex) to animate.
The code below will enable animations for elements that have the angular-animate class:
var myApp = angular.module("MyApp", ["ngAnimate"]);
myApp.config(function($animateProvider) {
$animateProvider.classNameFilter(/angular-animate/);
})
Here is example markup that includes the angular-animate class to enable animations:
<div ng-init="items=[1,2,3,4,5,6,7,8,9]">
<input placeholder="Filter with animations." ng-model="f" />
<div class="my-repeat-animation angular-animate" ng-repeat="item in items | filter:f track by item" >
{{item}}
</div>
</div>
Plunker example borrowed and modified from this blog where only the first filter has animations (due to having the angular-animate class).
Please note that I'm using angular-animate as an example and it is completely configurable using the .classNameFilter function.
There are two ways you can disbale animations in AngularJS if you have the module ngAnimate as a dependency on your module:
Disable or enable the animation globally on the $animate service:
$animate.enabled(false);
Disable the animations for a specific element - this must be the element for that angular will add the animationstate css classes (e.g. ng-enter, ...)!
$animate.enabled(false, theElement);
As of Angular 1.4 version you should reverse the arguments:
$animate.enabled(theElement, false);
Documentation for $animate.
To disable ng-animate for certain elements, using a CSS class, which follows Angular animate paradigm, you can configure ng-animate to test the class using regex.
Config
var myApp = angular.module("MyApp", ["ngAnimate"]);
myApp.config(function($animateProvider) {
$animateProvider.classNameFilter(/^(?:(?!ng-animate-disabled).)*$/);
})
Usage
Simply add the ng-animate-disabled class to any elements you want to be ignored by ng-animate.
Credit
http://davidchin.me/blog/disable-nganimate-for-selected-elements/
Just add this to your CSS. It is best if it is the last rule:
.no-animate {
-webkit-transition: none !important;
transition: none !important;
}
then add no-animate to the class of element you want to disable. Example:
<div class="no-animate"></div>
thanks, i wrote a directive which you can place on the element
CoffeeScript:
myApp.directive "disableAnimate", ($animate) ->
(scope, element) ->
$animate.enabled(false, element)
JavaScript:
myApp.directive("disableAnimate", function ($animate) {
return function (scope, element) {
$animate.enabled(false, element);
};
});
I've found that $animate.enabled(false, $element); will work for elements that use ng-show or ng-hide but it will not work for elements that use ng-if for some reason! The solution I ended up using was to just do it all in CSS, which I learned from this thread on GitHub.
CSS
/* Use this for transitions */
.disable-animations.ng-enter,
.disable-animations.ng-leave,
.disable-animations.ng-animate {
-webkit-transition: none !important;
transition: none !important;
}
/* Use this for keyframe animations */
.disable-animations.ng-animate {
-webkit-animation: none 0s;
animation: none 0s;
}
SCSS
.disable-animations {
// Use this for transitions
&.ng-enter,
&.ng-leave,
&.ng-animate {
-webkit-transition: none !important;
transition: none !important;
}
// Use this for keyframe animations
&.ng-animate {
-webkit-animation: none 0s;
animation: none 0s;
}
}
I do NOT want to use ngAnimate on my ng-if's, so this would be my solution:
[ng-if] {
.ng-enter, .ng-leave, .ng-animate {
-webkit-transition: none !important;
transition: none !important;
}
}
Just posting this as another suggestion!
I have a list from which the first li is hidden using ng-hide="$first". Using ng-enter results in the li being shown for half a second before disappearing.
Based on Chris Barr's solution, my code now looks like this:
HTML
<ol>
<li ng-repeat="item in items"
ng-hide="$first"
ng-class="{'no-animate' : $first}">
</li>
</ol>
CSS
.no-animate.ng-enter,
.no-animate.ng-leave,
.no-animate.ng-animate {
transition: none !important; /* disable transitions */
animation: none 0s !important; /* disable keyframe animations */
}
li.ng-enter {
opacity: 0;
transition: opacity 0.3s ease-out;
}
li.ng-enter-active {
opacity: 1;
}
/* I use Autoprefixer. If you don't, please include vendor prefixes. */
I know that it is a delayed reply, but here we use in MainController:
// disable all animations
$animate.enabled(false);
But the problem is that when we disable all animations, the ui-select are configured to opacity: 0.
So, its necessary to set opacity to 1 using CSS:
.ui-select-choices {
opacity: 1 !important;
}
This will properly set opacity and the ui-select will work.
Related
I'm playing with transitions and directives. I've created a Card directive that should show a clone of it self in fullscreen when clicked. The transition doesn't happen if I don't apply the altering css class in a timeout. Is that how it should be done?
<div ng-app='trans'>
<div data-card class='card'>timeout</div>
<div data-card='notimeout' class='card'>not timeout</div>
</div>
Between to original position and the fullscreen mode it should transition with a spin. The goto class is just so that i can add/remove transitions so that the card doesn't transition widht/height when the window is resized. I think it reads nice too =)
.card {
width:10vh;
height:14vh;
background-color:pink;
margin: 10px;
}
.card.goto.fullscreen {
transition: all 0.6s linear;
}
.card.fullscreen {
height:95vh;
width: 68vh;
position:absolut;
position: absolute;
top: 50% !important;
left: 50% !important;
margin: 0;
transform: translate(-50%, -50%) rotateY(360deg);
}
This is a simplified version of my directive.
var app = angular.module('trans', []);
app.directive('card', ['$document', '$timeout', function ($document, $timeout) {
return {
restrict: 'A',
link: link,
scope: {}
};
function link(scope, element, attrs) {
var clone;
element.on('click', function () {
if (clone) {
clone.off().remove();
}
clone = element.clone();
var spec = getCardSpecifications();
clone.css({
'margin': '0',
'top': spec.top + 'px',
'left': spec.left + 'px',
'position': 'absolute'
});
$document.find('body').append(clone);
clone.addClass('goto');
if (attrs.card == 'notimeout') {
clone.addClass('fullscreen');
} else {
$timeout(function () {
clone.addClass('fullscreen');
}, 0);
}
});
function getCardSpecifications() {
var spec = {};
spec.top = element.prop('offsetTop');
spec.left = element.prop('offsetLeft');
spec.height = element[0].offsetHeight;
spec.width = element[0].offsetWidth;
return spec;
}
}
}]);
I've created this jsfiddle that demonstrates the problem.
The problem doesn't have anything to do with Angular itself, but with creating a new DOM node and setting a class on it right after. Such a problem is described e.g. here, and it uses the same solution as yours in the first example.
DISCLAIMER: The real Angular way of doing this would be ngAnimate. What follows is a solution that is almost the same as the OP's, and one you'd only want to use if you don't want to depend on that module – but it's only ~11kb uncompressed, and 4kb gzipped. Choose wisely!
What also worked for me is waiting for the DOM node to be ready:
clone.ready(function() {
clone.addClass('fullscreen');
});
This amounts to almost the same thing as using a 0ms timeout, but is a. more descriptive and b. works in all cases, while the timeout solution apparently sometimes fails in Firefox (see linked article).
The second solution given in the article also reads a little more hackish (matter of opinion, really), and you'll have to retrieve the actual DOM element instead of the jqLite wrapper around it to use it.
Why exactly this happens, even though you are adding the class "after appending", I wasn't able to quickly find out. Perhaps appendChild, which append most likely uses internall, is asynchronous (i.e. pushes the DOM manipulation task onto the event queue)? Some more googling might be useful if you're really interested in the cause of this problem.
You should probably use animate to do an animation
$animate.addClass(clone, 'fullscreen'
I had issues trying to get the dependency for animate in fiddle so
I made a Plunker
When changing the DOM via the directive with methods like css, you have to inform the digest loop of these changes.
In order to accomplish that you should add scope.$apply() after adding the css class inside your if statement. The reason why $timeout works for you is that because it calls $apply after the timeout executes.
I use Angular 1.3.1, ngAnimate with CSS transitions. And I want to know when the CSS transition starts and completes.
I was expecting to be able to use module.animation but it does not work as I expect it (and as the docs suggest). I'm not able to get a notification when the transition completes.
I have created a Plunker: http://plnkr.co/edit/UGCmbBoiLT1xvhx7JGst.
This is my HTML code:
<body ng-controller="Controller as ctrl">
<h1>Angular Animation issue!</h1>
<div class="animation" id="resizeme" ng-class="ctrl.className"></div>
<label>
<input type="checkbox" ng-model="ctrl.className"
ng-true-value="'big'" ng-false-value="''">
Bigger!
</label>
This is the CSS code:
#resizeme {
width: 100px;
height: 100px;
background-color: red;
-webkit-transition: width linear 1s;
transition: width linear 1s;
}
#resizeme.big {
width: 200px;
}
And this is the JavaScript code:
var module = angular.module('app', ['ngAnimate']);
module.controller('Controller', function() {
this.className = '';
});
module.animation('.animation', function() {
return {
addClass: function(elemennt, className) {
console.log('addClass animation starts');
return function(cancel) {
console.log('addClass animation completes')
}
},
removeClass: function(elemennt, className) {
console.log('removeClass animation starts');
return function(cancel) {
console.log('removeClass animation completes')
}
},
}
});
Checking the checkbox runs the "addClass" animation. I do get the "addClass animation starts" message in the console, but I don't get the "addClass animation completes" message when the transition is complete.
The Javascript-defined Animations section of ngAnimate docs says the following for the function returned by enter, addClass and friends:
//this (optional) function will be called when the animation
//completes or when the animation is cancelled (the cancelled
//flag will be set to true if cancelled).
So I'd expect the function that outputs "addClass animation completes" to be called when the transition completes. But it's not called.
Does anyone know how I can get a notification when the transition completes?
PS: I know I could use the $animate service and the promise returned by $animate.addClass. But I'm trying to use ng-class (and other animation-compatible Angular directives), so I don't have access to the promise provided by the $animate service.
I am wiring up a status bar on my angular app, the purpose is when a request is made to the server the bar will show the response message, will have a background colour to denote success or error, and if it was successful to hide after a few seconds.
What I am seeing is that the first time this logic is run through after loading the page the animations are not run (both the fade in and timed fadeout fail to run), but only if the status bar element is initially hidden, if I set the ng-show variable to true on page load all the animations work as expected.
I did inspect the source via chrome's developer tools and during this first run the div has these classes alert-bar ng-hide-remove ng-hide-remove-active alert-bar-success ng-animate ng-hide after the animation should have been finished. When the animation does work the only classes present are alert-bar alert-bar-success ng-animate ng-hide.
The HTML:
<div class="alert-bar" ng-show="response.show" ng-class="(response.result == true) ? 'alert-bar-success' : 'alert-bar-danger'">
<div class="container">
<label>Message: {{response.message}}</label>
</div>
</div>
The CSS:
.alert-bar {
width: 100%;
margin-top: -20px;
margin-bottom: 20px;
}
.alert-bar-success {
background-color: #5cb85c;
border-color: #4cae4c;
color: #ffffff;
}
.alert-bar-danger {
color: #ffffff;
background-color: #d9534f;
border-color: #d43f3a;
}
.alert-bar.ng-hide-add, .alert-bar.ng-hide-remove {
-webkit-transition:all linear 0.3s;
-moz-transition:all linear 0.3s;
-o-transition:all linear 0.3s;
transition:all linear 0.3s;
display:block!important;
}
.alert-bar.ng-hide-add.ng-hide-add-active,
.alert-bar.ng-hide-remove {
opacity:0;
}
.alert-bar.ng-hide-add,
.alert-bar.ng-hide-remove.ng-hide-remove-active {
opacity:1;
}
The Controller:
controllers.controller("AppController", ['$scope', '$timeout', function($scope, $timeout) {
$scope.response = {};
$scope.response.received = function(message, result) {
$scope.response.message = message;
$scope.response.result = result;
$scope.response.show = true;
if (result == true) {
$timeout(function () {
$scope.response.show = false;
}, 4000);
}
};
}]);
This happens due to interplay between the animation code applied for both the ng-class and ng-hide directives. The only way I have made things like this work is to use separate <div> elements which are shown/hidden conditionally but that have static classes.
Here is a plunkr that demonstrates splitting the above into two <div> elements to create a working example for the problem in the question:
http://plnkr.co/edit/PFNGkOLKvs8Gx9h1T6Yk?p=preview
Alternatively, you could forego the use of ng-hide/ng-show entirely and use ng-class exclusively.
Edit: see http://plnkr.co/edit/JiLPc6cqiLHR21c64cCy?p=preview for a version that uses ng-class exclusively.
You don't need to use two divs. I guess the simplest solution is just putting animate css into class "ng-hide-add-active" only, not into "ng-hide-add". Like below:
.page-ready-animate.ng-hide-add-active {
-webkit-animation: 0.5s fadeOutDown;
animation: 0.5s fadeOutDown;
}
I would like to fade out a UL on ng-show=false. I add this css:
ul {
-webkit-transition: all 2s;
transition: all 2s;
}
.ng-show-remove,
.ng-show-add,
.ng-hide-remove,
.ng-hide-add {
display:block!important;
}
ul.ng-hide {
opacity: .2;
}
But when ng-show is set to false it just instantly disappears. How do I get it to fade out instead of instantly disappearing?
Click one of the li in this fiddle for a demonstration.
I've attached a fiddle solution.
A few things to note:
The angular version you included in your fiddle doesn't support the css animations. You'll need to update to the 1.2 version (which also requires you to include the angular-animate.js file and the ngAnimate module into your app)
var app = angular.module('foo', ['ngAnimate']);
Fiddle.
Is it possible to use ngAnimate without any CSS3 or JavaScript? Let's say if you simply need to toggle the opacity, can you just do it in the markup?
<div ng-show='foo == 'yes'' ng-animate="show: 'opacity:1', hide: 'opacity:0'" >
</div>
Animation in the browser either needs to happen by (1) letting the browser's rendering engine handle it via CSS, or (2) controlling it yourself with JavaScript. So, somewhere, one of these two things needs to happen.
That said, you could build your own directive that builds the correct CSS and/or JavaScript on the fly and attaches/applies it to the given element, but I believe that using ngAnimate as provided is probably easier.
An example for those coming into ngAnimate for the first time:
HTML:
<div ng-show="foo == 'yes'" ng-animate="{show: 'fade'}"></div>
CSS:
.enter-fade {
-webkit-transition: 1s linear opacity;
-moz-transition: 1s linear opacity;
-o-transition: 1s linear opacity;
transition: 1s linear opacity;
opacity: 0;
}
.enter-fade.enter-fade-active {
opacity: 1;
}
Or, if you're supporting browsers that don't support CSS3 transitions, you can do the transition with JavaScript instead:
myModule.animation('fade', function() {
return {
setup : function(element) {
element.css({'opacity': 0});
},
start : function(element, done, memo) {
element.animate({'opacity': 1}, function() {
done();
});
}
};
});
You can find more information at these great Yearofmoo articles:
Animation in AngularJS
Enhanced Animation in AngularJS