Using Angular and Ionic, I have created an app that uses Firebase subscribe methods to retrieve data. I have created an animation that loops through divs repeated in a *ngFor to create a animation delay and use keyframes to change each divs opacity from 0 to .5 to 1.
#neededfoodnames {
animation: fadeIn 0.5s linear;
animation-fill-mode: both;
}
// Set delay per List Item
#for $i from 1 through 100 {
#neededfoodnames:nth-child(#{$i}) {
animation-delay: .25s * $i;
}
}
// Keyframe animation
#-webkit-keyframes fadeIn {
0% {
opacity: 0;
}
75% {
opacity: 0.5;
}
100% {
opacity: 1;
}
}
However, because I am using Firebase's subscribe methods to listen for changes, each time an item is updated, the animation is triggered again for that item.
I'd like this animation to only happen on load and not when a change is made to items.
I have tried using an ngClass based on a boolean. On load I have set the boolean to true to fire the animation but I have also created a setTimeout() method to change the boolean to false after a designated time.
HTML:
<div id="neededfoodnames" *ngFor="let list of lists; let i = index" [ngClass]="{'neededfoodnames':animate == true}">
...
</div>
TypeScript:
export class HomePage {
lists: FirebaseListObservable<any[]>;
animate: boolean;
constructor() {
this.animate = true;
setTimeout(() => {
this.animate = false
}, 4000);
}
}
The CSS was also updated to use neededfoodnames as a class instead of a id.
Using this method, I currently have the setTimeout()'s milliseconds hardcoded. If I continue with this method, I will need to find a way to dynamically set the milliseconds.
I have created an example on Stackblitz of what I'm wanting to achieve, however it is only working because it is not retrieving the data from Firebase.
Is there a better way to trigger this animation only on load and not when data changes? Or is finding and setting the setTimeout()'s milliseconds my best option?
Related
I am using transition: opacity 5s; property. I want to show different alert or console message when my opacity value is 0.4 or 0.6 or .2 . on button click I am doing transition but I want to know opacity progress so that i will show those message ?
is there any way to do this
var btn = document.querySelector("button");
var par = document.querySelector("#parId");
btn.addEventListener("click", (e) => {
par.classList.add("removed");
});
par.addEventListener("transitionend", () => {
par.remove();
});
#parId {
transition: opacity 5s;
}
.removed {
opacity: 0;
}
we are getting transitionend callback if there any progress callback where I will check opacity value ?
There is no event that can be listened to to give what you want - unless you are going to use a linear transition. In that case you can carve your changes of opacity up into 0.2s slots, changing opacity on transitionend to the next value down - 0.8, 0.6 etc.
Your code however takes the default for the transition-timing-function property which is ease - not linear - so transitionend is of no use to you.
This snippet polls the opacity changes every tenth of a second and writes the current opacity to the console so you can see what is happening.
A couple of points: you will have to check for when the opacity goes just less than one of your break points, you are unlikely every to hit it just at exactly 0.6s or whatever; also notice that the console carries on being written to after the element has totally disappeared. The timing will not be exact, things are happening asynchronously.
<style>
#parId {
transition: opacity 5s;
width: 50vw;
height: 50vh;
background: blue;
opacity: 1;
display: inline-block;
}
.removed {
opacity: 0;
}
</style>
<div id="parId"></div>
<button>Click me</div>
<script>
var btn = document.querySelector("button");
var par = document.querySelector("#parId");
btn.addEventListener("click", (e) => {
let interval = setInterval(function () {
const opacity = window.getComputedStyle(par).opacity
console.log(opacity);
if (opacity == 0) {clearInterval(interval);}
}, 100);
par.style.opacity = 0;
});
</script>
You could potentially check periodically like this, although your interval will need to be at least the speed of the opacity animation or be quicker than it to catch the values.
var par = document.querySelector("#parId");
setInterval(function() {
console.log(window.getComputedStyle(par).opacity);
}, 100)
#parId{
opacity: 0.2;
transition: opacity 3s ease-in-out;
}
#parId:hover {
opacity: 1;
}
<div id="parId">
test
</div>
Take a look in this example
https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/animationend_event
You could define your animation stages as diferent ranimations on css then call them in chain via javascript. Before, you must set an event listener for the animationend event, and every time the event is fired you check the #parId opacity.
You could do it.with jQuery to, totaly in javascript
I want to fadein a component after ajax call completes and jquery has rebuilt DOM.
I have this setup:
index.html:
<head>
<style>
body {
opacity: 0;
transition: opacity 2s;
}
</style>
</head>
<body onload="document.body.style.opacity='1'">
<div class="content">
<!-- Markup for content -->
</div>
</body>
main.css
.content {
opacity: 0;
transition: opacity 6s;
transition: opacity 2s;
-webkit-transition: opacity 6s;
-moz-transition: opacity 6s;
}
main.js
$(document).ready(function () {
const contentEl = document.querySelector(".content");
$(".submit").on("click", async function (e) {
e.preventDefault();
console.log(contentEl.style.opacity);
if (contentEl.style.opacity == 1) {
contentEl.style.opacity = 0;
console.log("Style opacity is in if and = %s", contentEl.style.opacity);
}
// Do Ajax and update DOM via jQuery
contentEl.style.opacity = 1;
}
The first time thru .content fades in as expected as well as fade in of whole page on initial render. However subsequent times thru there is no transition effect. Logging shows that I am changing style.opacity from 1 -> 0 and back to 1 after initial iteration. Any CSS guru's versed in CSS's dark secrets input advice appreciated.
$(document).ready(function () {
const contentEl = document.querySelector(".content");
contentEl.style.opacity = 1; // Define initial opacity (starting val)
$(".submit").on("click", async function (e) {
e.preventDefault();
contentEl.style.opacity = 0; // on click set opacity to 0?
FadeIn(); // Let's Fade from 0 to 1!
});
let FadeIn = () => { // Function
if (contentEl.style.opacity < 1) { // If opacity doesn't equal 1
contentEl.style.opacity += 0.2; // Let's add 0.2!
setTimeout(FadeIn(), 300); // Hell let's repeat that more 300ms
}
}; // Once we equal 1 we should be done
});
I don't play with the JQuery like that, I much rather too use CSS entirely for the animation process of these types of things, it's cleaner (less jitter). I'm assuming this is what you're sort of after though, a simple set value and slowly loop till finished. Button will start out 1 opacity, when clicked jump to 0 and slowly climb its way back up to 1.
I'm rendering an scoped Array of Objects(payments in this case), and passing each one to a payment Directive like this:
<div id="payable" ng-controller="PaymentsController">
<payment ng-repeat="payment in payments" data="payment" class="payment"></payment>
</div>
This works really well! So when I delete an element from the scoped Array from the controller like this:
app.controller('PaymentsController', function($scope) {
//The Payments Array(each object passed to a Directive)
$scope.payments = [ { id: 1, amount: 10 }, { id: 2, amount: 15 } ];
$scope.deletePayment = function(index) {
//This deletes the Array Element and removes associated
//Directive template from the DOM
$scope.payments.splice(index, 1);
}
});
The CSS (uses compass mixins for simplicity)
.payment.ng-enter {
#include transition(all 2s ease-out);
opacity: 0;
}
.payment.ng-enter-active {
opacity: 1;
}
.payment.ng-leave {
#include transition(all 2s ease-out);
}
.payment.ng-leave-active {
opacity: 0;
}
Again, the above works as expected, I delete an element from the payments Array and the directive-template/view corresponding to the deleted Array element is removed from the DOM, This is PERFECT, except for the fact it's removed instantly!
EDIT:
The reason the animations like fadeOut don't work and the result is that the ( < payment > ) is removed instantly(after a specified time in the CSS) is that the animation is acting over the ( < payment >) custom tag, which is just a wrapper for the actual element.
Directive JS definition:
(function() {
var app = angular.module('paymentDirectives', []);
app.directive('payment', function() {
return {
restrict: 'E',
scope: {
payment: '=data'
},
templateUrl: 'partials/payment.html'
};
});
})();
The animation should act on the template referenced/wrapped by the directive custom tag( < payment > )
partials/payment.html
<div class="a-payment">
<div class="content">
<p>
<label>{{payment.amount}}</label>
</p>
</div>
</div>
In this case it would be the div with class="a-payment" of course and when the animation is complete it should then remove the payment tag element
What is the Angular way(for the latest version) to do an animation for this case(ie. Element Directive is removed from the DOM)?
Thank you very much in advance, and let me know if you need more from the code I'm using.
This is likely to do with the fact that most custom tags, such as your <payment>, are display: inline; by default.
You should set their style to be display: block in the CSS/SASS.
You can do this in many ways, for example, you can create a class that will trigger the CSS animation, and before deleting the object, you first assign it that class. Here's how:
var deleteAnimDuration = 1000; // let's use one second for our example
$scope.deletePayment = function(index) {
//This deletes the Array Element and removes associated
//Directive template from the DOM
$scope.payments[index].deleteAnim = true; // or whatever property makes sense to you
$timeout(function(){
$scope.payments.splice(index, 1);
}, deleteAnimDuration);
}
Then on the directive, you can use ng-class:
<payment
ng-repeat="payment in payments"
data="payment"
ng-class="{deleting: payment.deleteAnim}">
</payment>
Then in the CSS:
payment.deleting {
transition: opacity 1s linear; // again, one second
opacity: 0;
}
Since this sample animation (opacity fade) will run for one second, you need to set deleteAnimDuration for the $timeout to one second (1000 in milliseconds).
So, what happens:
you click delete on a payment
it sets payment.deleteAnim to true, which assigns the deleting class to the element
the timeout for the animation duration is set
animation starts
animation ends
element removed from the DOM
This is the concept from the DOM standpoint:
var divs = document.getElementsByTagName('div');
for (var i = 0; i < divs.length; i++) {
divs[i].addEventListener('click', function(e) {
e.target.className = 'deleting';
deleteEl(this)
})
}
function deleteEl(el) {
setTimeout(function() {
el.parentElement.removeChild(el);
}, 1000);
}
.deleting {
transition: opacity 1s linear;
opacity: 0;
}
div {
width: 50px;
height: 50px;
display: inline-block;
background: #eee;
}
<div>Click me</div>
<div>Click me</div>
<div>Click me</div>
<div>Click me</div>
Of course, this can work with JS animations as well.
Load angular-animate.min.js in your HTML.
Add ngAnimate into your module dependencies.
Add a class to your payment directive element: e.g. <payment class="my-animation" ...></payment>.
Add the following CSS (referencing your class in step 3):
.my-animation.ng-leave { opacity: 1; transition: opacity 300ms linear; }
.my-animation.ng-leave.ng-leave-active { opacity: 0; transition: opacity 300ms linear; }
Celebrate
I'm animating a div. It has the following definition:
<div ng-show="showTranslations" ng-swipe-right="showTranslationsBlock=false">...</div>
I have the following css defined:
div.ng-hide {
transition: 0.5s linear opacity;
opacity: 0;
}
div.ng-hide-add,
div.ng-hide-remove {
/* this needs to be here to make it visible during the animation
since the .ng-hide class is already on the element rendering
it as hidden. */
display:block!important;
}
This is taken from this tutorial. The animation works. But:
Why do I need these classes .ng-hide-add and .ng-hide-remove?
Why I don't see them added to div's classes?
Why there are also classes ng-hide-add-active and ng-hide-remove-active?
Why there is no transition when the div becomes visible although I've added the following css rule:
div.ng-hide-remove {
opacity: 1;
}
UPDATE
As I can see from the table provided by google's tutorial these classes are added to trigger animation frame (this performs a reflow). Is my understanding correct? Why is animation frame is mentioned there?
I tried to increase the transition period but it didn't add the classes. I didn't see the classes ng-hide-add-active and ng-hide-remove-active added either.
As I understand from the table these are the classes that trigger transition?
UPDATE1
I've explored the Angular's source code and found the following for the ng-hide directive:
var ngHideDirective = ['$animate', function($animate) {
return function(scope, element, attr) {
scope.$watch(attr.ngHide, function ngHideWatchAction(value){
$animate[toBoolean(value) ? 'addClass' : 'removeClass'](element, 'ng-hide');
});
};
}];
As I understand the ng-hide class is added through animation service. But what happens if I don't use animations and $animate service is not available? How Angular is going to handle this situation given the code above and how it is going to add ng-hide class? Or is this $animate.addClass() simply adds a callback to addClass event?
Put your CSS transition on ng-hide-remove, ng-hide-remove-active:
div.ng-hide-remove {
transition: 0.5s linear opacity;
opacity: 0;
}
div.ng-hide-remove-active {
opacity: 1;
}
Similarly, for ng-hide-add and ng-hide-add-active:
div.ng-hide-add {
transition: 0.5s linear opacity;
opacity: 1;
}
div.ng-hide-add-active {
opacity: 0;
}
I'm using CSS3 Animations, and I want to be able to move to a specific spot in the animation. For instance, if the CSS looks like this (and pretend that I used all the proper prefixes):
#keyframes fade_in_out_anim {
0% { opacity: 0; }
25% { opacity: 1; }
75% { opacity: 1; }
100% { opacity: 0; }
}
#fade_in_out {
animation: fade_in_out_anim 5s;
}
then I would like to be able to stop the animation, and move it to the 50% mark. I guess that the ideal JavaScript would look something like this:
var style = document.getElementById('fade_in_out').style;
style.animationPlayState = 'paused';
// Here comes the made up part...
style.animation.moveTo('50%'); // Or alternately...
style.animationPlayPosition = '50%';
Does anyone know of a way to make this happen (hopefully in Webkit)?
We can use the animation-delay property. Usually it delays animation for some time, and, if you set animation-delay: 2s;, animation will start two seconds after you applied the animation to the element. But, you also can use it to force it to start playing animation with a specific time-shift by using a negative value:
.element-animation{
animation: animationFrames ease 4s;
animation-delay: -2s;
}
http://default-value.com/blog/2012/10/start-css3-animation-from-specified-time-frame/