Angular: Hightlight animation on table rows - css

Situation
I have a table with devices and their statuses. When I click on a specific button the rows that have the offline status need to have a highlight for a couple of seconds and then return back to normal.
What I have so far
<tr id="deviceRow" class="user-item" *ngFor="let device of group.devices" (click)="$event.stopPropagation()" [class.highlightOn]="this.offlineHighlight == true && device.onlineState == 'Offline'">
When I click on the button the offlineHighlight boolean becomes true and it adds the highlightOn class which is this.
.highlightOn {
background-color: rgb(255, 68, 65);
-webkit-animation: fade-out 3s ease-out both;
animation: fade-out 3s ease-out both;
}
#-webkit-keyframes fade-out {
0% {
background-color: rgba(255,51,47,1);
}
100% {
background-color: transparent;
}
}
#keyframes fade-out {
0% {
background-color: rgba(255,51,47,1);
}
100% {
background-color: transparent;
}
}
This adds the 'highlight' animation.
After the animation is completed I set the offlineHighlught boolean to false again in the button code.
showOfflineDevices() {
this.offlineHighlight = true;
this.tabIndex = 1;
setTimeout(function(){
this.offlineHighlight = false;
}, 3000);
}
It all works fine until the animation has completed. Standard the table rows have different background colors for each odd even row. When the animation is complete all the rows that had the highlightOn class have a white background color as you can see here.
TL:DR The background color of the table rows need to go back to normal after the animation is completed. The even rows are also white now, which need to be grey.

It's because you set background-color to transparent on fade-out, you can simply use transitions like this (just add and remove class with additional styles, don't override existing styles on fade-out):
setInterval(() => {
$(".color").addClass("selected");
setTimeout(() => {
$(".color").removeClass("selected")
}, 2500);
}, 5000);
div {
transition: background-color .5s ease;
}
div:nth-child(odd) {
background-color: lightgray;
}
div:nth-child(even) {
background-color: gray;
}
.selected {
background-color: green !important;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>1</div>
<div class="color">2</div>
<div class="color">3</div>
<div>4</div>
<div>5</div>

Related

How to skip to a specific time in css-animations by seconds?

is there any way to skip to specific time in css animation?
for example: if we have a div moving from left to right in 10 seconds, user change time to 5 and animation jumps to second 5 and continues playing.
Changing animation-delay will do exactly that.
When you change it to negative values, it will jump forward.
const selectElement = document.querySelector('.number');
selectElement.addEventListener('change', (event) => {
const result = document.querySelector('.some-div');
result.style.animationDelay = `${event.target.value}s`;
});
.some-div {
width: 100px;
height: 100px;
background-color: red;
animation-name: example;
animation-duration: 20s;
animation-delay: 0;
}
#keyframes example {
from {
transform: translateX(0px);
}
to {
transform: translateX(400px);
}
}
<div class="some-div"></div>
<input type="number" class="number" />
<br/>
change the value in the input

CSS force animation to be fully completed on click, active or focus?

Given:
.button_focus, .button_active {
width: 8rem;
height: 5rem;
background-color: #e4e7eb;
}
.button_focus:focus,
.button_active:active {
animation-name: clickAnimation;
animation-duration: 250ms;
animation-timing-function: ease-in-out;
}
#KeyFrames clickAnimation {
0.00% { background-color: #d5d7da; }
14.29% { background-color: #ced0d3; }
28.57% { background-color: #bbbbbb; }
42.86% { background-color: #b1b2b3; }
57.14% { background-color: #b1b2b3; }
71.43% { background-color: #bbbbbb; }
85.71% { background-color: #ced0d3; }
100.00% { background-color: #d5d7da; }
}
<div class="contianer">
<button class="button_focus">
focus
</button>
<button class="button_active">
active
</button>
</div>
I want to find a way to be able to spam-click the button and the animation to fully be processed every time. Currently, with the :focus pseudo-class, I need to click on the button then click away, for the animation to be reinitialized when I click again on the button.
Conversely, if I use the :active pseudo-class, the animation is played on every successive click but it is not fully completed. I need to press the button for 250ms for the animation to be fully completed.
There are a few posts on this matter on SO, and the solutions seem to add an animation class using JS and removing it afterwards, but in most posts, the questions involve hovering. In my case, it's just a click so I don't understand how I can add an animation class and removing it as well at some point. I guess I am just confused.
Does anyone have any ideas or tips?
The problem is that the focus and active pseudo classes are sort of too lingering or too ephemeral' - so to get rid of fous the user has to move away and to keep the active they have to keep pressing.
Using JS we can listen for the click on the button, add a class which sets the animation.
We keep a permanent listen out for the animationend event on the button, and when that is triggered we remove the class. That way the animation is not 'disturbed' until it has finished, but we do need to remove it so that on the next click it can be set again (otherwise if there is no change to the setting CSS thinks it's done it).
const button = document.querySelector('.button_click');
button.addEventListener('click', function() {
button.classList.add('clicked');
});
button.addEventListener('animationend', function() {
button.classList.remove('clicked');
});
.button_click {
width: 8rem;
height: 5rem;
background-color: #e4e7eb;
animation-name: none;
}
.button_click.clicked {
animation-name: clickAnimation;
animation-duration: 250ms;
animation-timing-function: ease-in-out;
}
#KeyFrames clickAnimation {
0.00% {
background-color: #d5d7da;
}
14.29% {
background-color: #ced0d3;
}
28.57% {
background-color: #bbbbbb;
}
42.86% {
background-color: #b1b2b3;
}
57.14% {
background-color: #b1b2b3;
}
71.43% {
background-color: #bbbbbb;
}
85.71% {
background-color: #ced0d3;
}
100.00% {
background-color: #d5d7da;
}
}
<div class="contianer">
<button class="button_click">
click me
</button>
</div>

CSS animation-direction not working as I would have expected

N.B. With the (more complex) setup I'm actually working with I can't use CSS Transitions. I recognise that CSS Transitions would be a
perfectly good solution in the example below.
I'm having a little trouble with
animation-direction: reverse
which I've never used before but doesn't seem to be running the way I might have expected it to.
The easiest solution to my problem would be to write two CSS #keyframes animations and use one or the other.
But for the sake of economy and elegance I would like to use a single animation and play it forwards or backwards.
This example below shows the effect I'm trying to achieve.
When the page loads, pressing either button will fire the intended animation.
However, after one button is pushed, the animation no longer runs and only the end-frame of the forwards or reverse animation is displayed.
What am I doing wrong here?
Working Example:
const square = document.querySelector('.square');
const buttonOutbound = document.querySelector('button.outboundButton');
const buttonReturn = document.querySelector('button.returnButton');
buttonOutbound.addEventListener('click', () => {
square.className = 'square';
square.classList.add('outbound');
}, false);
buttonReturn.addEventListener('click', () => {
square.className = 'square';
square.classList.add('return');
}, false);
.square {
width: 100px;
height: 100px;
margin: 12px;
background-color: red;
transform: translateX(0) scale(1);
}
.square.outbound {
animation: animateSquare 1s linear normal forwards;
}
.square.return {
animation: animateSquare 1s linear reverse forwards;
}
#keyframes animateSquare {
100% {
background-color: blue;
transform: translateX(200px) scale(0.5);
}
}
<div class="square"></div>
<button type="button" class="outboundButton">Outbound animation</button>
<button type="button" class="returnButton">Return animation</button>
The browser considers that animation as complete therefore it does not restart it in order to restart the animation you need to first remove the class and then re-add it however for the browser to recognize this change you need add a slight delay. Adding a setTimeout does the trick even if the timeout is 0 because js is single threaded.
const square = document.querySelector('.square');
const buttonOutbound = document.querySelector('button.outboundButton');
const buttonReturn = document.querySelector('button.returnButton');
buttonOutbound.addEventListener('click', () => {
square.classList.remove('return');
square.classList.remove('outbound');
setTimeout(() => {
square.classList.add('outbound');
}, 0)
}, false);
buttonReturn.addEventListener('click', () => {
square.classList.remove('return');
square.classList.remove('outbound');
setTimeout(() => {
square.classList.add('return');
}, 0)
}, false);
.square {
width: 100px;
height: 100px;
margin: 12px;
background-color: red;
transform: translateX(0) scale(1);
}
.square.outbound {
animation: animateSquare 1s linear normal forwards;
}
.square.return {
animation: animateSquare 1s linear reverse forwards;
}
#keyframes animateSquare {
100% {
background-color: blue;
transform: translateX(200px) scale(0.5);
}
}
<div class="square"></div>
<button type="button" class="outboundButton">Outbound animation</button>
<button type="button" class="returnButton">Return animation</button>
I had a think about this away from my laptop screen and realised that... what was missing from my original set up was one additional static class, describing the presentational state of .square after the .outbound animation has run:
.square.outbounded {
background-color: blue;
transform: translateX(200px) scale(0.5);
}
(N.B. There's no need for a corresponding static class, describing the state of .square after the .return animation has run, since the presentational state that class would describe is already described in the initial styles of .square)
Working Example (with .outbounded class added)
const square = document.querySelector('.square');
const buttonOutbound = document.querySelector('button.outboundButton');
const buttonReturn = document.querySelector('button.returnButton');
buttonOutbound.addEventListener('click', () => {
square.className = 'square outbound';
setTimeout(() => {
square.classList.add('outbounded');
square.classList.remove('outbound');
}, 1000);
}, false);
buttonReturn.addEventListener('click', () => {
square.className = 'square return';
setTimeout(() => {
square.classList.remove('return');
}, 1000);
}, false);
.square {
width: 100px;
height: 100px;
margin: 12px;
background-color: red;
transform: translateX(0) scale(1);
}
.square.outbounded {
background-color: blue;
transform: translateX(200px) scale(0.5);
}
.square.outbound {
animation: animateSquare 1s linear normal forwards;
}
.square.return {
animation: animateSquare 1s linear reverse forwards;
}
#keyframes animateSquare {
100% {
background-color: blue;
transform: translateX(200px) scale(0.5);
}
}
<div class="square"></div>
<button type="button" class="outboundButton">Outbound animation</button>
<button type="button" class="returnButton">Return animation</button>

How to set animation on first element on loading?

I am using Angular2 with SystemJs. I am wondering how can I set some animation (like fade-out effect) on the Loading label in this example. We can see the label before the application loads. Is there a way to add fade out effect when the content of the following changes (index.html):
<body>
<my-app>loading...</my-app>
</body>
CSS leaves some room for improvement
#Component({
selector: 'my-app',
template: `
<router-outlet></router-outlet>
`,
directives: [ROUTER_DIRECTIVES],
host: {
'[class.loaded]': 'isLoaded',
},
})
#RouteConfig([
{path: '/', name: 'Main View', component: MainViewComponent}
])
export class AppComponent {
isLoaded:boolean = false;
constructor(){}
ngAfterViewInit() {
setTimeout(() => this.isLoaded = true, 0);
}
}
<style>
my-app {
visibility: hidden;
opacity: 0;
}
my-app.loaded {
visibility: visible;
opacity: 1;
transition: visibility 0s 2s, opacity 2s linear;
}
div.spinner {
position: absolute;
top: 150px;
left: 150px;
}
my-app.loaded + div.spinner {
visibility: hidden;
opacity: 0;
transition: visibility 0s 2s, opacity 2s linear;
background-color: red;
}
</style>
<body>
<my-app></my-app>
<div class="spinner">loading...</div>
</body>
Plunker
The best way is add a class hidden to the label, and after load app remove the class.
To achieve the fade animation with css add this properties to the label:
transition: all 0.5s
opacity: 1
And to the class hidden
.hidden {
opacity: 0 !important
}
When you remove the hidden class it will animate opacity to default value that is 1 (100%).
If you have JQuery in your app can use $('label').fadeOut(). But nowadays is better use pure CSS

Queued CSS animations using delay or keyframes that can be interrupted smoothly

First, a fiddle: http://jsfiddle.net/AATLz/
The essence here is that there's a set of animations queued using -webkit-transition-delay. First element 0.4s, second 0.8s, third 1.4s, etc. They're queued last to first by default, and first to last when the parent has the 'expanded' class (toggled with that button).
This means that the animation when '.expanded' is added brings the boxes out one by one, and in reverse when the class is removed.
That's dandy. The problems start to arise when the class is toggled mid-animation. If you toggle, say, after the second box has animated, there's a delay before they start animating back, because a couple delay timers are being waited out.
Delays are obviously a bit clunky here.
The two alternatives I have in mind are 1) CSS keyframe animations, which I'm not entirely sure of how to activate on multiple elements in succession, and 2), JS controlled timing - using something like jQuery Transit. I'm not sure which would be more capable/graceful or if I'm missing another option.
Any input would be awesome!
jsfiddle: http://jsfiddle.net/Bushwazi/fZwTT/
Changed it up a bit. Control the timing with js. Animations with css.
CSS:
* {
margin:0;
padding:0;
}
#container {
background: orange;
height: 100px;
position: relative;
width: 100px;
}
.box {
height: 100px;
left: 0;
position: absolute;
top: 0;
width: 100px;
-webkit-transition:all 0.5s ease-in-out 0s;
-moz-transition:all 0.5s ease-in-out 0s;
-o-transition:all 0.5s ease-in-out 0s;
transition:all 0.5s ease-in-out 0s;
-webkit-transform: translate3d(0,0,0);
}
.box-1 {
background: red;
}
.box-2 {
background: green;
}
.box-3 {
background: yellow;
}
.box-4 {
background: blue;
}
.box-1 .box-1 {
left:100px;
}
.box-2 .box-2 {
left:200px;
}
.box-3 .box-3 {
left:300px;
}
.box-4 .box-4 {
left:400px;
}
HTML:
<div id="container" class="box-0" data-status="default" data-box="0">
<div class="box box-1"></div>
<div class="box box-2"></div>
<div class="box box-3"></div>
<div class="box box-4"></div>
</div>
<button id="ToggleAnim">Toggle</button>
JS:
(function(){
var testies = {
to: 0,
init: function(){
$button = $('#ToggleAnim');
$anim_elm = $('#container');
$(function(){
testies.el();
});
},
el: function(){ // el ==> event listeners
$button.on('click', testies.toggleBoxes);
},
toggleBoxes: function(evt){
var status = $anim_elm.attr('data-status'),
pos = $anim_elm.attr('data-box');
window.clearTimeout(testies.to);
// if default ==> build
// if killing ==> build
// if building ==> kill
// if done ==> kill
if(status == 'build' || status == 'done'){
testies.kill();
} else {
testies.build();
}
evt.preventDefault();
},
build: function(){
bpos = $anim_elm.attr('data-box');
if(bpos < 4){
bpos++;
$anim_elm.attr('data-status', "build").attr('data-box', bpos).addClass('box-' + bpos);
testies.to = window.setTimeout(testies.build, 500);
}
if(bpos == 4)$anim_elm.attr('data-status', "done");
console.log('BUILD: ' + bpos);
},
kill: function(){
kpos = $anim_elm.attr('data-box');
if(kpos > 0){
db = kpos - 1;
$anim_elm.attr('data-status', "kill").attr('data-box', db).removeClass('box-' + kpos);
testies.to = window.setTimeout(testies.kill, 500);
}
console.log('KILL: ' + kpos);
if(kpos == 0)$anim_elm.attr('data-status', "default")
}
}
testies.init();
})();

Resources