Animating new ng-grid rows - javascript

How can I animate a newly created row in ng-grid?
What I would like is that when new data comes in from the server, it is added to the beginning of the grid, and that row then glows for a few seconds.
I've tried adding an ng-animate class to the rowTemplate for the grid, but that was unsuccessful:
$scope.gridOptions = {
data: 'myData',
rowTemplate: '<div style="height: 100%" class="reveal-animation"><div ng-style="{ \'cursor\': row.cursor }" ng-repeat="col in renderedColumns" ng-class="col.colIndex()" class="ngCell {{col.cellClass}}"><div class="ngVerticalBar" ng-style="{height: rowHeight}" ng-class="{ ngVerticalBarVisible: !$last }"> </div><div ng-cell></div></div></div>'
};
Where my reveal-animation (lifted straight from the ng-animate docs) is:
.reveal-animation.ng-enter {
-webkit-transition: 1s linear all;
transition: 1s linear all;
opacity: 0;
}
.reveal-animation.ng-enter.ng-enter-active {
opacity: 1;
}
But this does not appear to work for the grid.
Is there some way to accomplish this?
Here is a Plunker with my attempt
http://plnkr.co/edit/iR9voQaFRREi0pjNLQgc?p=preview
I added a <ul> at the bottom to show the behavior that I want (and to prove that ng-animate is working).

The proper way to animate this is by binding an animation to .ngRow i.e.:
/*
The animate class is apart of the element and the ng-enter class
is attached to the element once the enter animation event is triggered
*/
.ngRow.ng-enter {
-webkit-transition: 1s linear all; /* Safari/Chrome */
transition: 1s linear all; /* All other modern browsers and IE10+ */
/* The animation preparation code */
opacity: 0;
}
/*
Keep in mind that you want to combine both CSS
classes together to avoid any CSS-specificity
conflicts
*/
.ngRow.ng-enter.ng-enter-active {
/* The animation code itself */
opacity: 1;
}
The problem is that you are using unshift to push the values into the beginning of the array, but ng-grid still functions by adding a row at the bottom of the grid and rebinding all the rows such that, unintentionally, the first item in the grid is the one that gets the animation.
Here's the plunker for my fork which has the above css working - perhaps you can take it a step further: http://plnkr.co/edit/gNSM4FRMFcTtQtT6EU7b?p=preview
As a thought: maybe you can have the elements go into the data set as a normal push and re-order the grid by something that keeps them in reverse order. That might trick the grid into animating the newest thing in but keep the newest thing at the top of the grid.
To be honest, I have found that simply building my own grid and binding an ng-repeat to the tr was much easier than trying to fuss with other grid systems, especially ones like ng-grid where you don't get to control the behavior.

Related

Angular: how to animate the background color of a ul list item when text changes [duplicate]

I have created a table in which user can increase and decrease the value.
See the Fiddle
//sample code as its not allowing me to push the link to JSFiddle with out pasting code
<tr ng-repeat="d in dataSource" ng-animate="'animate'">
// css - as from angular page
.animate-enter {
-webkit-transition: 1s linear all; /* Chrome */
transition: 1s linear all;
background-color:Yellow;
}
.animate-enter.animate-enter-active {
background-color:Red;
}
I want to do animation when the model updates i.e the table column changes in background color From Red to white in case user changes the value.
So when you click up arrow or down arrow in any perticular column, the background color of that table column changes from Red to white.
I am not able to get my head around it. Any pointers on how to achieve this ?
There are couple of issues in your code:
NEVER do DOM manipulations in the code of controller: $(elem).animate(.. is something you should avoid. Only in directives you can manipulate with DOM element.
In 1.2+ versions of AngularJS you need to reference ngAnimate module.
It is better to do CSS3 animations with fallback to js-based animations.
I propose to write a directive that will track changes and add a class that will trigger the animation and then remove it:
app.directive('animateOnChange', function($animate,$timeout) {
return function(scope, elem, attr) {
scope.$watch(attr.animateOnChange, function(nv,ov) {
if (nv!=ov) {
var c = nv > ov?'change-up':'change';
$animate.addClass(elem,c).then(function() {
$timeout(function() {$animate.removeClass(elem,c);});
});
}
});
};
});
Working example:
http://plnkr.co/edit/zs495osfSnWSvWBIn3rh?p=preview
This can be solved with a simple directive and CSS3 animations.
HTML
<span animate-on-change="someValue">{{someValue}}</span>
Directive
myModule.directive('animateOnChange', function($timeout) {
return function(scope, element, attr) {
scope.$watch(attr.animateOnChange, function(nv,ov) {
if (nv!=ov) {
element.addClass('changed');
$timeout(function() {
element.removeClass('changed');
}, 1000); // Could be enhanced to take duration as a parameter
}
});
};
});
CSS
[animate-on-change] {
transition: all 1s;
-webkit-transition: all 1s;
}
[animate-on-change].changed {
background-color: red;
transition: none;
-webkit-transition: none;
}
Fiddle
in Angular 1.5 u can use ngAnimateSwap built in directive.
From the docs:
ngAnimateSwap is a animation-oriented directive that allows for the container to be removed and entered in whenever the associated expression changes. A common usecase for this directive is a rotating banner or slider component which contains one image being present at a time. When the active image changes then the old image will perform a leave animation and the new element will be inserted via an enter animation.

How can I trigger ng-move after a splice?

I am using ng-animate and I have a list of entries iterated through using ng-repeat. When you make a selection on a particular entry, it disappears. I have defined .ng-move, .ng-move-active, .ng-leave, .ng-leave-active appropriately so that the .leave animation occurs when I perform a .splice() operation on my data, and the .move operation occurs when I reorder entries.
However, what I want is for when one of the entries is removed, the .ng-leave occurs on that entry while .ng-move slides them all up. I've found that .splice() doesn't trigger an .ng-move though, so I'm curious if there is a way to force that animation to happen after a .splice()?
Here is the html:
<div data-ng-repeat="entry in data" class="container card-drop card-shift">
<card data="entry"></card>
</div>
Here are the css classes:
.card-drop.ng-leave {
transition: 0.5s ease-in-out;
opacity: 1;
}
.card-drop.ng-leave.ng-leave-active {
transform: scale(0.5);
opacity: 0;
}
.card-shift.ng-move {
transition: 0.5s ease-in-out;
}
.card-shift.ng-move.ng-move-active {
transform: translateY(-284px);
}
And in the javascript the event I am concerned about is simply $scope.data.splice(index, 1);
EDIT: http://plnkr.co/edit/nz38XxPbV4Ycqdn3QYVP?p=preview
Above is the plunk for the issue I am referring to. Notice that when you click on an entry and it is spliced, the .ng-leave animation occurs but none of the ng-move animations do.
The solution was that there was no easy way to do this. Ultimately I ended up manipulating DOM elements' CSS from within the directive to provide the sliding animation, then instantaneously shifted them to their original spot at the same time as the splice, resulting in the animation I was after, albeit in a hacky way.

Remove an element whilst enter animation is still running

I have an animation which runs on any new item to a grid. Lets say this animation takes 5 seconds to run. Currently, if I try removing that element within the 5 seconds (so whilst the enter animation is still running), the item remains in the list until the enter animation finishes.
Looking at the docs, it says that this is by design:
You'll notice that when you try to remove an item
ReactCSSTransitionGroup keeps it in the DOM. If you're using an
unminified build of React with add-ons you'll see a warning that React
was expecting an animation or transition to occur. That's because
ReactCSSTransitionGroup keeps your DOM elements on the page until the
animation completes.
It ways that you need to add the following (updated to the relevant class names obviously) and it should work for the case described above:
.example-leave {
opacity: 1;
transition: opacity .5s ease-in;
}
.example-leave.example-leave-active {
opacity: 0.01;
}
I'm not finding this to be the case, even though I have the described leave classes, I'm finding that it is still waiting for the original enter animation to finish, is this behavior correct, how do I fix this?
Here is a video showing the quirk in question - https://www.youtube.com/watch?v=3oKerWlLZIE
If it makes a difference here is my classes:
.request-summary-item-holder-enter {
background-color: #F8F5EC;
transition: background-color 5s ease-in;
}
.request-summary-item-holder-enter.request-summary-item-holder-enter-active {
background-color: transparent;
}
.request-summary-item-holder-leave {
opacity: 1;
transition: opacity 0.05s ease-in;
}
.request-summary-item-holder-leave.request-summary-item-holder-leave-active {
opacity: 0.01;
}
Update:
Source code references:
Setting the state - https://github.com/avanderhoorn/Glimpse.Client.Prototype/blob/master/src/request/components/request-summary-view.jsx#L33
Usage of transition group and setting keys - https://github.com/avanderhoorn/Glimpse.Client.Prototype/blob/master/src/request/components/request-summary-list-view.jsx

Animation transitions behave differently for ng-hide-remove and ng-hide-add

I am trying to animate a <div> to slide-in/out from the left on a button click. I am using the angular framework and ng-showto control the <div> display/visibility, and adding transitions to the ng-hide set of styles.
I have successfully managed to have the div slide in from the left, however I can not get it to slide out (it simply dissappears after the specified delay). I have tried modifying several examples online to get the behavior I am after to no avail.
JSFiddle for anyone that wants to have a look
https://jsfiddle.net/mquinlan/0wcrcwxe/5/
You got that almost right except for removing the left:0 in the selectors for .animate-show.ng-hide-add.ng-hide-add-active, .animate-show.ng-hide-remove.ng-hide-remove-active.
.animate-show.ng-hide-add.ng-hide-add-active,
.animate-show.ng-hide-remove.ng-hide-remove-active {
-moz-transition: all ease 0.5s;
transition: all ease 0.5s;
}
Updated Fiddle: https://jsfiddle.net/vsj62g5r/

What is the cleanest way to disable CSS transition effects temporarily?

I have a DOM element with this effect applied:
#elem {
transition: height 0.4s ease;
}
I am writing a jQuery plugin that is resizing this element, I need to disable these effects temporarily so I can resize it smoothly.
What is the most elegant way of disabling these effects temporarily (and then re-enabling them), given they may be applied from parents or may not be applied at all.
Short Answer
Use this CSS:
.notransition {
-webkit-transition: none !important;
-moz-transition: none !important;
-o-transition: none !important;
transition: none !important;
}
Plus either this JS (without jQuery)...
someElement.classList.add('notransition'); // Disable transitions
doWhateverCssChangesYouWant(someElement);
someElement.offsetHeight; // Trigger a reflow, flushing the CSS changes
someElement.classList.remove('notransition'); // Re-enable transitions
Or this JS with jQuery...
$someElement.addClass('notransition'); // Disable transitions
doWhateverCssChangesYouWant($someElement);
$someElement[0].offsetHeight; // Trigger a reflow, flushing the CSS changes
$someElement.removeClass('notransition'); // Re-enable transitions
... or equivalent code using whatever other library or framework you're working with.
Explanation
This is actually a fairly subtle problem.
First up, you probably want to create a 'notransition' class that you can apply to elements to set their *-transition CSS attributes to none. For instance:
.notransition {
-webkit-transition: none !important;
-moz-transition: none !important;
-o-transition: none !important;
transition: none !important;
}
Some minor remarks on the CSS before moving on:
These days you may not want to bother with the vendor-prefixed properties like -webkit-transition, or may have a CSS preprocessor that will add them for you. Specifying them manually was the right thing to do for most webapps when I first posted this answer in 2013, but as of 2023, per https://caniuse.com/mdn-css_properties_transition, only about 0.4% of users in the world are still using a browser that supports only a vendor-prefixed version of transition.
There's no such thing as -ms-transition. The first version of Internet Explorer to support transitions at all was IE 10, which supported them unprefixed.
This answer assumes that !important is enough to let this rule override your existing styles. But if you're already using !important on some of your transition rules, that might not work. In that case, you might need to instead do someElement.style.setProperty("transition", "none", "important") to disable the transitions (and figure out yourself how to revert that change).
Anyway, when you come to try and use this class, you'll run into a trap. The trap is that code like this won't work the way you might naively expect:
// Don't do things this way! It doesn't work!
someElement.classList.add('notransition')
someElement.style.height = '50px' // just an example; could be any CSS change
someElement.classList.remove('notransition')
Naively, you might think that the change in height won't be animated, because it happens while the 'notransition' class is applied. In reality, though, it will be animated, at least in all modern browsers I've tried. The problem is that the browser is buffering the styling changes that it needs to make until the JavaScript has finished executing, and then making all the changes in a single "reflow". As a result, it does a reflow where there is no net change to whether or not transitions are enabled, but there is a net change to the height. Consequently, it animates the height change.
You might think a reasonable and clean way to get around this would be to wrap the removal of the 'notransition' class in a 1ms timeout, like this:
// Don't do things this way! It STILL doesn't work!
someElement.classList.add('notransition')
someElement.style.height = '50px' // just an example; could be any CSS change
setTimeout(function () {someElement.classList.remove('notransition')}, 1);
but this doesn't reliably work either. I wasn't able to make the above code break in WebKit browsers, but on Firefox (on both slow and fast machines) you'll sometimes (seemingly at random) get the same behaviour as using the naive approach. I guess the reason for this is that it's possible for the JavaScript execution to be slow enough that the timeout function is waiting to execute by the time the browser is idle and would otherwise be thinking about doing an opportunistic reflow, and if that scenario happens, Firefox executes the queued function before the reflow.
The only solution I've found to the problem is to force a reflow of the element, flushing the CSS changes made to it, before removing the 'notransition' class. There are various ways to do this - see here for some. The closest thing there is to a 'standard' way of doing this is to read the offsetHeight property of the element.
One solution that actually works, then, is
someElement.classList.add('notransition'); // Disable transitions
doWhateverCssChangesYouWant(someElement);
someElement.offsetHeight; // Trigger a reflow, flushing the CSS changes
someElement.classList.remove('notransition'); // Re-enable transitions
Here's a JS fiddle that illustrates the three possible approaches I've described here (both the one successful approach and the two unsuccessful ones):
http://jsfiddle.net/2uVAA/131/
Add an additional CSS class that blocks the transition, and then remove it to return to the previous state. This make both CSS and JQuery code short, simple and well understandable.
CSS:
.notransition {
transition: none !important;
}
Note: !important was added to be sure that this rule will have higher preference, because using an ID is more specific than class.
JQuery:
$('#elem').addClass('notransition'); // to remove transition
$('#elem').removeClass('notransition'); // to return to previouse transition
I would advocate disabling animation as suggested by DaneSoul, but making the switch global:
/*kill the transitions on any descendant elements of .notransition*/
.notransition * {
transition: none !important;
}
.notransition can be then applied to the body element, effectively overriding any transition animation on the page:
$('body').toggleClass('notransition');
For a pure JS solution (no CSS classes), just set the transition to 'none'. To restore the transition as specified in the CSS, set the transition to an empty string.
// Remove the transition
elem.style.transition = 'none';
// Restore the transition
elem.style.transition = '';
If you're using vendor prefixes, you'll need to set those too.
elem.style.webkitTransition = 'none'
You can disable animation, transition, transforms for all of element in page with this CSS code:
var style = document.createElement('style');
style.type = 'text/css';
style.innerHTML = '* {' +
' transition-property: none !important;' +
' transform: none !important;' +
' animation: none !important;}';
document.getElementsByTagName('head')[0].appendChild(style);
I think you could create a separate CSS class that you can use in these cases:
.disable-transition {
transition: none;
}
Then in jQuery you would toggle the class like so:
$('#<your-element>').addClass('disable-transition');
If you want a simple no-jquery solution to prevent all transitions:
Add this CSS:
body.no-transition * {
transition: none !important;
}
And then in your js:
document.body.classList.add("no-transition");
// do your work, and then either immediately remove the class:
document.body.classList.remove("no-transition");
// or, if browser rendering takes longer and you need to wait until a paint or two:
setTimeout(() => document.body.classList.remove("no-transition"), 1);
// (try changing 1 to a larger value if the transition is still applying)
This is the workaround that worked easily for me. It isn't direct answer to the question but still may help someone.
Rather than creating notransition class which was supposed to cancel the transition
.notransition {
-webkit-transition: none !important;
-moz-transition: none !important;
-o-transition: none !important;
transition: none !important;
}
I created moveTransition class
.moveTransition {
-webkit-transition: left 3s, top 3s;
-moz-transition: left 3s, top 3s;
-o-transition: left 3s, top 3s;
transition: left 3s, top 3s;
}
Then I added this class to element with js
element.classList.add("moveTransition")
And later in setTimeout, I removed it
element.classList.remove("moveTransition")
I wasn't able to test it in different browsers but in chrome it works perfectly
If you want to remove CSS transitions, transformations and animations from the current webpage you can just execute this little script I wrote (inside your browsers console):
let filePath = "https://dl.dropboxusercontent.com/s/ep1nzckmvgjq7jr/remove_transitions_from_page.css";
let html = `<link rel="stylesheet" type="text/css" href="${filePath}">`;
document.querySelector("html > head").insertAdjacentHTML("beforeend", html);
It uses vanillaJS to load this css-file. Heres also a github repo in case you want to use this in the context of a scraper (Ruby-Selenium): remove-CSS-animations-repo
does
$('#elem').css('-webkit-transition','none !important');
in your js kill it?
obviously repeat for each.
I'd have a class in your CSS like this:
.no-transition {
-webkit-transition: none;
-moz-transition: none;
-o-transition: none;
-ms-transition: none;
transition: none;
}
and then in your jQuery:
$('#elem').addClass('no-transition'); //will disable it
$('#elem').removeClass('no-transition'); //will enable it

Categories