AngularJS animation when model changes - javascript

I have been searching on Google and Stackoverflow but haven't found what I am looking for.
Here's what I have. I promise I am making a sincere effort at figuring this out.
The problem is as follows: I have animations working with the list. When I add items to the list using the timeout, they correctly animate in. However, the "title" variable is a string. I want to apply an animation when this value changes. I am still clueless right now honestly on how to get that to work. I understand that I can add css classes for animations for ng-hide, but I still don't quite understand how to fit that here. Any help is appreciated in advance. Please enlighten me. You don't have to give me code. Even a high level description of how to accomplish this will suffice.
// app.js
(function() {
var app = angular.module("MyApp", ["ngAnimate"]);
// route configuration
}());
// homecontroller.js
(function() {
var app = angular.module("MyApp");
app.controller("HomeController", ["$scope","$timeout", homeController];
function homeController($scope, $timeout) {
$scope.items = ["Frodo", "Bilbo", "Merry", "Pippin", "Sam"];
$scope.title = "The Hobbits";
$timeout(function() {
$scope.title = "The Hobbits and the Wizard";
$scope.items.unshift("Aragorn","Faramir","Boromir");
}, 5000);
}
}());
Some HTML
<!-- view for HomeController -->
<h1>{{ title }}</h1>
<div ng-controller="HeaderWebpart.HeaderController">
<div class="testClass" style="display:block;" ng-repeat="item in items">{{ item }}</div>
</div>
And CSS
div.testClass.ng-enter {
-webkit-animation: enter 1000ms cubic-bezier(0.250, 0.100, 0.250, 1.000);
animation: enter 1000ms cubic-bezier(0.250, 0.100, 0.250, 1.000);
display: block;
position: relative;
}
#-webkit-keyframes enter {
from {
opacity: 0;
height: 0px;
left: -70px;
}
75% {
left: 15px;
}
to {
opacity: 1;
height: 20px;
left: 0px;
}
}
div.testClass.ng-enter-active {
opacity: 1;
}

You currently have nothing that applies any animation logic to the <h1> element, simply assigning a value to title within a controller is not enough.
If you have a look at the documentation for angular animations
https://docs.angularjs.org/api/ngAnimate - you'll see that only a specific set of directives have animation hooks. Each of these directives usually have a pairing of enter/leave or add/remove animations. This means that angular adds and removes specific CSS classes to these elements, which you can use to perform animations with, similar to what you have already done with the ng-repeat directive and testClass animations above:
.yourAnimationCSSClass.ng-enter { }
=> what your element should look like before the animation starts
what the change should be and the duration
.yourAnimationCSSClass.ng-enter.ng-enter-active { }
=> ending(stable) state for your animation, ie. what the
element should look like when you're done
... ng-leave and ng-leave-active work similarly.
So, to solve this for your <h1> element, one way to apply an animation is to optionally set a CSS class using ngClass. This ends up being fairly close to the Class and ngClass animation hooks example in the Angular developer guide for animations, so have a look at that example.

Related

Vue.js [v-cloak] does not use CSS transition

My Intention:
Using the Vue.js (v2) attribute [v-cloak], I want to have the "app" hidden until ready. When [v-cloak] is removed, I want the "app" to fade in. Using CSS opacity and transitions to achieve the fade.
The Problem:
When [v-cloak] is removed from my "app" there is no transition as I would expect. It just goes from hidden to visible immediately. Seems to ignore the CSS.
Example:
I have made an exaggerated example with Vue.js and a JavaScript simulated version to show how they both behave.
https://codepen.io/freemagee/pen/wXqrRM
When viewing this example, you will see the "Plain old JavaScript" red box fade into view over 5 seconds. But the Vue controlled version just appears without a fade. They share the same CSS for the transition, so in theory should work the same way.
Anyone used [v-cloak] effectively to achieve smooth transitions?
Codepen Code
HTML
<div id="app" v-cloak>
<div class="red-box"></div>
<p>Vue.js {{ message }}</p>
</div>
<div id="app2" v-cloak>
<div class="red-box"></div>
<p>Plain old JavaScript</p>
</div>
CSS
html,
body {
margin: 0;
height: 100%;
min-height: 100%;
}
[v-cloak] .red-box {
opacity: 0;
visibility: hidden;
transition: visibility 0s 5s, opacity 5s linear;
}
#app,
#app2{
padding-top: 50px;
}
.red-box {
margin: 0 auto;
width: 100px;
height: 100px;
background: red;
opacity: 1;
visibility: visible;
transition: opacity 5s linear;
}
p {
text-align: center;
}
JS
new Vue({
el: "#app",
data: {
message: "Hello world"
}
});
window.setTimeout(function() {
document.getElementById("app2").removeAttribute("v-cloak");
}, 500);
This won't work because after the Vue app instance initializes, the #app div is actually removed, re-rendered and becomes a different div, even though it looks the same. This is probably due to Vue's virtual DOM mechanism.
The #app2 elements is still the same DOM after document.getElementById("app2").removeAttribute("v-cloak");: https://codepen.io/jacobgoh101/pen/PaKQwV
The #app element is a different DOM after new Vue(...): https://codepen.io/jacobgoh101/pen/ERvojx?editors=0010
For the Vue app, the element with v-cloak is removed, another element without v-cloak is added back. There is no element that transition from "with v-cloak" to "without v-cloak". That's why the CSS transition won't work. Hope that this is clear enough.
(If you don't already know this, )You can use Transition Component
As explained, there is no transition possible with [v-cloak], as the DOM element without the v-cloak is a new one.
I've figured a simple workaround, using the mounted methods and its hook vm.$nextTick(), in which case, the use of v-cloak isn't necessary anymore.
Mounted is called when the original app element has been replaced with the new one generated by Vue, but it doesn't necessarily mean that every child has been rendered yet. nextTick is called when every child element has been rendered inside the app view.
First, I have setup my HTML app element like this :
<div id="main-view" :class="{ready: isPageReady}">...</div>
In my vue app :
new Vue({
el: "#main-view",
data: {
isPageReady: false,
[...]
mounted: function() {
this.$nextTick(function () {
this.isPageReady = true;
});
}
});
Finally in the CSS, I tried a simple fadein using opacity:
#main-view {
opacity: 0;
}
#main-view.ready {
opacity: 1;
transition: opacity 300ms ease-in-out;
}
Beware: the transition doesn't always show if you have the browser's inspector/debugger open.

Angular $animate and directive not behaving properly

So either, I'm just not getting it or the documentation is incomplete, or something else, but I can't seem to figure out my code, and what is happening.
I'm working on a directive that dynamically creates an overlay an generates a sliding menu. I've followed the solutions over several different questions and guides online. I've even read the angular documentation and nothing is working.
I can't get passed the initial creation of the background overlay. This is frustrating.
You start by clicking the icon button
<i class='fa fa-bars' slider-menu></i>
Then the slider-menu directive should activate and the css should take over.
sliderMenu.js
angular.module('dt').directive('sliderMenu', SliderMenuDirective);
SliderMenuDirective.$inject = ['$document', '$rootScope', '$animate'];
function SliderMenuDirective($document, $rootScope, $animate){
return {
link : function(scope, element, attr){
var body = $document.find('body').eq(0);
element.on('mousedown', function(event){
$animate.addClass(body, 'menu-overlay');
});
}
}
}
css
#keyframes menuOverlay{
0% {
background-color: rgba(0, 0, 0, 0);
}
100% {
background-color: $navy-blue-alpha;
}
}
.menu-overlay:after{
content : "";
display: block;
position: fixed;
top: 0;
left: 0;
height: 100vh;
width: 100vw;
z-index: 10;
background-color: rgba(0,0,0,0);
animation-name: menuOverlay;
}
Upon inspection on the initial click the only thing that is happening is the body tag gets the attribute of data-ng-animate="1". Then nothing happens. I click again and then the body tag has a new class of menu-overlay-add. I have no idea what is going and am very lost. Can someone help point me in the right direction and maybe clarify animations just a little more, or show me what I'm doing wrong?
on is a jQuery/jqLite method and does not automatically trigger the digest loop, which is why Angular's animation cycle isn't working correctly in your example.
You can use $apply:
$apply() is used to execute an expression in angular from outside of
the angular framework. (For example from browser DOM events,
setTimeout, XHR or third party libraries). Because we are calling into
the angular framework we need to perform proper scope life cycle of
exception handling, executing watches.
For example:
element.on('mousedown', function(event) {
scope.$apply(function() {
$animate.addClass(body, 'menu-overlay');
});
});
Your animation also needs a duration specified:
animation-duration: 2s;
If you want the background-color from the last keyframe to remain you can use:
animation-fill-mode: forwards;
Or the shorthand:
animation: menuOverlay 2s forwards;
Demo: http://plnkr.co/edit/6zTMlsNkg1xvKJpLs8dN?p=preview

Transition not working without querying width property

I want to animate a translateX with transition on a click event by adding a class to the div in the js. The transform and transition properties are added in the css file.
var widget = document.getElementById('widget');
widget.style.display = 'block';
document.getElementById('widget2').clientWidth; //comment this line out and it wont work
widget.className = 'visible';
It only works if I query the width property of any element in the dom before adding the class.
here is a jsfiddle:
https://jsfiddle.net/5z9fLsr5/2/
Can anyone explain why this is not working?
That's because you begin your transition and modified the display property "at the same time". Altering display will ruin any transition (citation needed, admittedly), so it would be a good idea to isolate the display changing and actual transiting:
https://jsfiddle.net/5z9fLsr5/3/
document.getElementById('showWidget').addEventListener('click', function(e) {
e.preventDefault();
var widget = document.getElementById('widget');
widget.style.display = 'block';
//document.getElementById('widget2').clientWidth;
window.setTimeout(function(){
widget.className = 'visible';
},0);
});
#widget {
width: 200px;
height: 80px;
background: black;
position: absolute;
transition: transform 500ms;
transform: translateX(-200px);
display: none;
}
#widget.visible {
transform: translateX(200px);
}
#widget2 {
position: absolute;
right: 0
}
show
<div id="widget"></div>
<div id="widget2">xxx</div>
Querying clientWidth seems to "pause" the execution for some time, so it works too.
The issue here is the initial setting of display: none. To the browser's layout manager, this indicates that the layout should be done as if the element in question wasn't even in the DOM (it still is, mind you). This means that the CSS style transform: translateX(-200px); will not be applied.
Doing this:
widget.style.display = 'block';
widget.className = 'visible';
triggers both modifications essentially at the same time - the layout is only re-done after both statements have been executed. Inserting document.getElementById('widget2').clientWidth; (clientHeight works as well) triggers the layout manager to repaint, thus registering transform: translateX(-200px).
As others have mentioned before me, the solution is to either use opacity instead of display (this would be my choice), or to use setTimeout with a delay of 0 (see Why is setTimeout(fn, 0) sometimes useful?).

animate div using angularjs animate

I am using angularJs 1.2+ and angular ui router.
I have a route that contains div. When entering to that route, a div showed up. I would like to add an animation to that div for enter and leave but I don't know how to do it.
I don't know if I should create a directive that uses $animate or use a built in directive of angular. Any suggestion?
There is a FAQ:
How to: Animate ui-view with ng-animate
And also the related example plunker.
As explained in Developer Guide / Animations,
How they work:
Animations in AngularJS are completely based on CSS classes. As long as you have a CSS class attached to a HTML element within your website, you can apply animations to it...
So, this way we can create some CSS definitions related to our ui-view element (class or attribute as used in the plunker) and ngAnimation injected classes: .ng-enter, .ng-leave..
A snippet from the FAQ related plunker
[ui-view].ng-enter, [ui-view].ng-leave {
position: absolute;
left: 0;
right: 0;
...
transition:all .5s ease-in-out;
...
}
[ui-view].ng-enter-active {
opacity: 1;
...
transform:scale3d(1, 1, 1);
...
}
[ui-view].ng-leave {
opacity: 1;
...
transform:translate3d(0, 0, 0);
...
}
You need to create the animation in CSS and have ngAnimate apply and remove the appropriate classes.
Or here is a nice Angular Lib that I use with predefined animations: https://github.com/Hendrixer/ng-Fx
Read the directions carefully, you need the TweenMax.js library.

Animations in AngularJS 1.2 - How To

I tried to use animations within my app but unfortunately to no avail. I checked lots of examples, blog, downloaded animate.css etc etc.
I injected animation module, tried basic examples, tried following tutorials for instance, but it seems I miss something every time.
Can someone please provide exact instructions for AngularJS v1.2 animations to work, with injections, inclusions and everything you need to do to get them working? Maybe a step-by-step instructions on how you usually do your animations.
A basic fadein/fadeout example on ng-show/hide would suffice.
Thank you very much
Reference angular-animate.js
Add ngAnimate as a dependent module:
myApp = angular.module('myApp', ['ngAnimate']);
Add a div to your view with the ng-show directive and for example the two classes 'fadein' and 'fadeout':
<div class="fadein fadeout" ng-show="show">I am the div.</div>
Add the classes to your css. In 1.2 ngAnimate is class-based and you need to add certain suffixes to your classes based on a certain naming convention. A good source of information regarding this can be found here.
Example:
/* Fade in ngShow */
.fadein.ng-hide-remove {
-webkit-transition: 1s;
transition: 1s;
display: block !important;
opacity: 0;
}
.fadein.ng-hide-remove.ng-hide-remove-active {
opacity: 1;
}
/* Fade out ngShow */
.fadeout.ng-hide-add {
-webkit-transition: 1s;
transition: 1s;
display: block !important;
opacity: 1;
}
.fadeout.ng-hide-add.ng-hide-add-active {
opacity: 0;
}
Add logic to change the expression that is provided to the ng-show directive and the div should fade in and out.
A working example: http://plnkr.co/edit/lq4LmUq5mrbJBGMMEyh9?p=preview

Categories

Resources