CSS keyframes animation gracefully finished - css

I have animation of icon that is rotating.
It may be finished any unexpected time.
So my question is. Is there any way using pure CSS to finish it to the end keyframe before stop rotating?
I have this code right now:
I did tried transition without any good result:
.loop {
transition: 400ms;
transform: rotate(180deg);
&:hover {
animation: rotation 400ms infinite linear;
}
}
#keyframes rotation {
from {
transform: rotate(180deg);
}
to {
transform: rotate(0deg);
}
}

I wrote this directive, that works as I expect.
import { Directive, ElementRef, Input } from '#angular/core';
#Directive({
selector: '[appRotate]'
})
export class RotateDirective {
speedQueue: Array<number>= [];
_speed = 0;
#Input('appRotate') set speed(value: number = 0) {
const steps = 5;
const step = (value - this._speed) / steps;
this.speedQueue = new Array(steps).fill(0).map((_, i) => (i+1) * step + this._speed);
}
constructor(private el: ElementRef) {
let rot = 0;
const loop = () => {
if (this.speedQueue.length) {
this._speed = this.speedQueue.shift();
}
rot += this._speed * 360;
this.el.nativeElement.style.transform = `rotate(${rot % 360}deg) scaleX(-1)`;
requestAnimationFrame(loop);
};
requestAnimationFrame(loop);
}
}
<span class="material-icons loop" [appRotate]="speed">
loop
</span>
<div>
<input type="radio" name="speed" (click)="speed=0">0
<input type="radio" name="speed" (click)="speed=0.02">2%
<input type="radio" name="speed" (click)="speed=0.08">8%
</div>
It works like that:
It is not CSS solution.

Related

There's some way to add fadein or slide effect on "react-dates-range" when i change between months?

I'm using the library https://github.com/hypeserver/react-date-range , react 17.0.2 and next 12.0.0
I would like that when I change from one month to another, instead of making a sudden change, to be able to configure a Fade-In, or a Slide, just as it happens in the AirBnb calendar.
Does anyone know how I can do this? Thank you very much and sorry for the inconvenience.
This is my actual behavior
This is something like what I want, but it could also be a fade in or something similar.
Exactly like Airbnb i believe its not possible because the react-date-range works a little different from what airbnb does.
But you can get similar behavior using SwitchTransition and CSSTransition from react-transition-group library. You can check their docs here.
First, here's the code sample with DateRangerPicker using CSSTransition between months.
And below the same code, with comments on what its doing:
CSS file:
/*
Overflow the Calendar container
.rdrDateRangeWrapper is a class that react-date-range creates internally
*/
.rdrDateRangeWrapper {
overflow-x: hidden;
}
/*
.rdrMonths is the class that react-date-range creates internally for the month container calendar
.fadeRightToLeft classes is related to react-transition-group
Created for transition from right to left
*/
.fadeRightToLeft-enter .rdrMonths {
opacity: 0;
transform: translateX(100%);
}
.fadeRightToLeft-enter-active .rdrMonths {
opacity: 1;
transform: translateX(0%);
}
.fadeRightToLeft-exit .rdrMonths {
opacity: 1;
transform: translateX(0%);
}
.fadeRightToLeft-exit-active .rdrMonths {
opacity: 0;
transform: translateX(-100%);
}
.fadeRightToLeft-enter-active .rdrMonths,
.fadeRightToLeft-exit-active .rdrMonths {
transition: opacity 100ms, transform 100ms;
}
/*
Same as fadeRightToLeft:
.fadeLeftToRight classes is related to react-transition-group
Created for transition from left to right
*/
.fadeLeftToRight-enter .rdrMonths {
opacity: 0;
transform: translateX(-100%);
}
.fadeLeftToRight-enter-active .rdrMonths {
opacity: 1;
transform: translateX(0%);
}
.fadeLeftToRight-exit .rdrMonths {
opacity: 1;
transform: translateX(0%);
}
.fadeLeftToRight-exit-active .rdrMonths {
opacity: 0;
transform: translateX(100%);
}
.fadeLeftToRight-enter-active .rdrMonths,
.fadeLeftToRight-exit-active .rdrMonths {
transition: opacity 100ms, transform 100ms;
}
Component file:
import { useState } from "react";
import { addDays, isAfter } from "date-fns";
import { DateRangePicker } from "react-date-range";
import { SwitchTransition, CSSTransition } from "react-transition-group";
import "react-date-range/dist/styles.css";
import "react-date-range/dist/theme/default.css";
import "./styles.css";
export default function App() {
const [state, setState] = useState([
{
startDate: new Date(),
endDate: addDays(new Date(), 7),
key: "selection"
}
]);
// state created to hold the first month that calendar is showing
const [shownDateChangeValue, setShownDateChangeValue] = useState(new Date());
// state created to check if use created next Month ou previous month
const [isNextMonth, setIsNextMonth] = useState(true);
return (
<SwitchTransition mode="out-in">
<CSSTransition
/*** call the transition when month changes ***/
key={shownDateChangeValue}
/*** code related to SwitchTransition ***/
addEndListener={(node, done) =>
node.addEventListener("transitionend", done, false)
}
/*** Set the transition class related to the user action ***/
classNames={isNextMonth ? "fadeRightToLeft" : "fadeLeftToRight"}
>
<DateRangePicker
onChange={(item) => {
setState([item.selection]);
}}
showSelectionPreview={true}
moveRangeOnFirstSelection={false}
months={2}
ranges={state}
direction="horizontal"
/*** set the current month ***/
shownDate={shownDateChangeValue}
/*** Change shownDateChangeValue and isNextMonth states, dispatching the transition ***/
onShownDateChange={(month) => {
/* check if user click next or previous month */
const isNext = isAfter(month, shownDateChangeValue);
setIsNextMonth(isNext ? true : false);
setShownDateChangeValue(month);
}}
/>
</CSSTransition>
</SwitchTransition>
);
}

How to generate a random number each time a component re-renders?

I want to use Less variables to generate a random number for conditionally class that gets added and removed each second. Here is the sample code of reactjs file
function App() {
const [date , setDate] = useState('')
const [circleBoolean, setCircleBoolean] = useState(false);
const [random_number, setRandom_number] = useState(0);
useEffect(() => {
const interval = setInterval(() => {
doThings()
}, 1000);
return () => clearInterval(interval);
}, []);
function doThings() {
setCircleBoolean((prev)=>{
return !prev});
setDate(moment().format('MMMM Do YYYY, h:mm:ss a')) ;
setRandom_number(Math.random() * 100);
console.log("cirlce boolean :" + circleBoolean)
}
return (
<div className="App">
<div className="bg">
<div style={{ top:`${random_number}%`, left:`${random_number}%` }} className={circleBoolean ? "circle" : ""} />
<div className="card">
<p className="card-info">{date}</p>
</div>
</div>
</div>
);
}
and here is the sample code from css file
#color: blue;
#random: (round(`Math.random()`)) ;
.circle {
position: absolute;
border: solid 4px #color;
border-radius: 50%;
height: #random;
width: 3em;
background-color: #color;
animation: circleSize 1s ease-in-out;
}
#keyframes circleSize {
0% {
transform: scale(0, 0);
}
50% {
transform: scale(1,1);
}
100%{
transform: scale(0,0);
}
}
The random number is generated only during the first render and is not changed during subsequent re-renders. How do I go about it ?
My advice is use a 'css-in-js' approach.
React only re-renders components that changed and your css is static right now. If you define your styles in a js file and pass your random number to this file every time your App's interval runs - you'll get your re-renders.

Is it possible to create scroll in generic transition Stylus mixin?

JavaScript:
const show = entries => entries[0].isIntersecting ? entries[0].classList.remove('is-hidden') : null;
const observer = new IntersectionObserver(show, {threshold:0});
Array.from(document.querySelectorAll('.js-hidden')).forEach(element => observer.observe(element));
I want to play fade in animation when '.js-hidden' class are removed like below... but this code are not working:
HTML:
<div class="my-component js-hidden is-hidden">
<p class="text text-1">Hello</p>
<p class="text text-2">World</p>
</div>
Stylus:
fadeIn(duration=1s, delay=0s) {
opacity: 1;
transition: opacity duration ease delay;
&.is-hidden { // <- yeah, this is wrong... but, any ideas? I want to apply transition both element and pseudo element.
opacity: 0;
}
}
.my-component {
.text,
&::before,
&::after {
fadeIn();
}
.text-2 {
transition-delay: .3s;
}
&::before {
content: 'foo';
transition-delay: .5s;
}
&::after {
content: 'bar';
transition-delay: .7s;
}
}
And, if the fade in elements are more nested?
<div class="my-component js-hidden is-hidden">
<div class="wrapper-1">
<div class="wrapper-2">
<p class="text text-1">Hello</p>
<p class="text text-2">World</p>
</div>
</div>
</div>
I want to apply transition both element and pseudo element. but I don’t know what should I do...
Thanks.
Finally...
Thank you Andy.
Finally I arrived the code below. XD
JavaScript:
const show = entries => entries[0].isIntersecting ? entries[0].target.classList.remove('is-hidden') : null;
const observer = new IntersectionObserver(show, {threshold:0});
Array.from(document.querySelectorAll('.js-hidden')).forEach(element => observer.observe(element));
HTML:
<div class="my-component js-hidden is-hidden">
<p class="text text-1">Hello</p>
<p class="text text-2">World</p>
<p class="text text-3">Hello</p>
<p class="text text-4">World</p>
<p class="text text-5">Hello</p>
<p class="text text-6">World</p>
<p class="text text-7">Hello</p>
<p class="text text-8">World</p>
<p class="text text-9">Hello</p>
</div>
Stylus:
fadeIn(target, duration=1s, delay=0s, property=all, easing=ease) {
{target} {
opacity: 1;
transition: duration property easing delay;
}
&.is-hidden {
{target} {
opacity: 0;
}
}
}
.my-component {
duration = .3s;
delay = 1s;
fadeIn('.text', duration:duration, delay:delay);
fadeIn('&::before', duration:duration, delay:delay);
fadeIn('&::after', duration:duration, delay:delay);
interval = duration;
amount = 9;
for i in 2..amount {
.text-{i} {
transition-delay: (interval * (i - 2) + duration + delay)s;
}
}
&::before {
content: 'FOOOOOOOOOOOOOOO';
transition-delay: (interval * ((amount + 1) - 2) + duration + delay)s;
}
&::after {
content: 'BARRRRRRRRRRRRRR';
transition-delay: (interval * ((amount + 2) - 2) + duration + delay)s;
}
}
First, it seems you're looking for a transition, not an animation. I'm not a Stylus expert, but know IntersectionObserver and CSS pretty well. I have the basic demo working now.
Some notes on the adjusted fadeIn function.
is-hidden is a class that exists in the DOM from the beginning, so cue the transition when it's not there
use a delegate pattern from transitions—that is, have the change in the parent affect the children (don't listen for a class for each child/pseudo element)
fadeIn(duration=1s, delay=0s) {
.text,
&::before,
&::after {
opacity: 0;
transition: 0.5s opacity ease;
}
&:not(.is-hidden) {
.text,
&::before,
&::after {
opacity: 1;
}
}
}
Also, I couldn't get your JavaScript to work due to some errors and rewrote it to suit the demo. Here's the rewritten JavaScript:
const components = document.querySelectorAll(".my-component");
const observer = new IntersectionObserver(components => {
components.forEach(component => {
if (component.intersectionRatio > 0) {
component.target.classList.remove("is-hidden")
} else {
component.target.classList.add("is-hidden")
}
})
});
Array.from(document.querySelectorAll(".js-hidden")).forEach(element =>
observer.observe(element)
);
CodePen Demo

animate react component with transform and transition

I use this inline transform to iterate dynamically over slides
<div className="slides" style={{transform: `translate(${currentPosition}%, 0px)`, left: 0}}> {slider} </div>
</div>
which is working fine. However I would like to add some fadein to this element on position changing and the following css does not working
on element slides via css
opacity: 0;
animation: fadein 2s ease-in 1 forwards;
here is my fadein animation
#keyframes fadein {
from {
opacity:0;
}
to {
opacity:1;
}
}
What I'm missing in this case?
You can try something like this. That will give you idea how can you apply animations.
class Test extends React.Component {
constructor(props) {
super(props);
this.state = {
currentPosition: 0,
opacity: 0
}
}
componentDidMount() {
setTimeout(() => this.setState({
opacity: 1
}), 500);
}
handleClick() {
let self = this;
this.setState({
opacity: 0
}, () => setTimeout(() => self.setState({
opacity: 1
}), 2000))
}
render() {
return ( <
div >
<
div className = "slides"
style = {
{
transform: `translate(${this.state.currentPosition}%, 0px)`,
left: 0,
opacity: this.state.opacity
}
} > Some title to test animation < /div> <
button onClick = {
this.handleClick.bind(this)
} > Click < /button> <
/div>
)
}
}
React.render( < Test / > , document.getElementById('container'));
.slides {
transition: all 2s ease-in;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/0.14.8/react-dom.min.js"></script>
<div id="container"></div>
Here is the fiddle.
Hope it helps.

Simple forward and backward CSS animation with Angular ngAnimateSwap directive

I have made a simple swap animation with ng-animate-swap directive, nothing fancy. It works well in forward direction, but fails animating backwards correctly.
The problem is, that the entering slide will not be visible before animation ends.
Check an example in Plunker.
Here is the controller code:
var elem = document.querySelector('.wrap');
var $elem = angular.element(elem);
var slides = [
{ color: "#f00" },
{ color: "#0f0" },
{ color: "#00f" },
];
var current = 0;
$scope.slide = slides[ current ];
// switch to next slide
$scope.nextSlide = function(){
$elem.removeClass('animate-back');
if(slides.length <= ++current){
current = 0;
}
$scope.slide = slides[ current ];
};
// switch to prev slide
$scope.prevSlide = function(){
$elem.addClass('animate-back');
if(--current<0){
current = slides.length-1;
}
$scope.slide = slides[ current ];
};
the HTML:
<div class="wrap" ng-controller="AppCtrl">
<div class="container">
<div ng-animate-swap="slide" class="slide" ng-style="{background:slide.color}"></div>
</div>
<button ng-click="prevSlide()">Previous Slide</button>
<button ng-click="nextSlide()">Next Slide</button>
</div>
the CSS:
.slide.ng-enter-active,
.slide.ng-leave {
transform: translate(0,0);
}
// forward
.slide.ng-enter {
transform: translate(0,-100%);
}
.slide.ng-leave-active {
transform: translate(0,100%);
}
// backward
.animate-back .slide.ng-enter {
transform: translate(0,100%);
}
.animate-back .slide.ng-leave-active {
transform: translate(0,-100%);
}
I think it's simple CSS issue, but can not wrap my head around it.
What am I missing here?
You're right! The problem was the css. The direction was missing for the incoming slide.
".animate-back .slide.ng-enter-active"
This should work.
.animate-back .slide.ng-enter {
transform: translate(0,100%);
}
.animate-back .slide.ng-enter-active {
transform: translate(0,0);
}
.animate-back .slide.ng-leave-active {
transform: translate(0,-100%);
}
Updated Plunker: http://plnkr.co/edit/Uze8e8DmmjlaSqEeBELe?p=preview

Resources