Rotate image from random start location in React using Keyframes - css

Using React, I have a component that I want to start rotated from a random location and rotate 360 degrees infinitely. What I have is:
HTML:
return <img className={`${props.className}`} style={{
animation: `spin 5s linear infinite`,
transform: `rotate(${Math.floor(Math.random() * (361));}deg)`
}} src={image} alt="img"/>
CSS:
#keyframes spin {
from {transform:rotate(0deg);}
to {transform:rotate(360deg);}
}
By default, what this does is ignore the transform, load the image so that it is upright (i.e. 0 degrees), and rotate to 360 degrees (vertical again) over the course of 5 seconds. What I want is a way for from to be transform:rotate(deg);} and to to be {transform:rotate(<SOME RANDOM NUMBER + 360>deg);}. Is this possible?
As a side note, is it possible to randomize the direction it travels?

You can't get the required effect using transform rotate - as you have noted the animation will immediately alter this to rotate(0).
But what you can do is leverage the animation-delay CSS property.
Instead of calculating a random rotation, calculate a random time between 0 and 5 seconds and use that, negative, to set animation-delay.The animation will start part way through its cycle, at a random point and so at a random angle.
Here's a pure CSS/HTML/JS snippet with the rotation slowed down so it's easier to see that the rotation does start at a different angle each time:
const spinner = document.querySelector('.spin');
spinner.style.setProperty('--delay', Math.floor(Math.random() * 50));
.spin {
width: 100px;
height: 100px;
background: red;
animation: spin 50s linear infinite;
animation-delay: calc(var(--delay) * -1s);
}
#keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
<div class="spin"></div>

Related

Safari CSS Bug: Animation Rotation Direction Incorrect?

I'm writing a custom animation for a project I'm working on. The idea is that the animation is meant to resemble an exclamation mark becoming unhinged from the baseline, swinging for a moment, and then falling off the baseline entirely.
For some reason, only Safari (OSX and iOS) refuses to render the first animation keyframe as a clockwise rotation in a CSS animation I've made. Instead, it renders the keyframe as a counter-clockwise animation, but then works just fine for the following animation keyframes.
Working CodePen example: http://codepen.io/michaelmarcialis/pen/obPYPO
#keyframes unhinged {
0% {
transform: rotate(0deg);
}
15% {
transform: rotate(240deg);
}
30% {
transform: rotate(125deg);
}
45% {
transform: rotate(220deg);
}
60% {
transform: rotate(145deg);
}
75% {
opacity: 1;
transform: rotate(200deg);
}
90% {
opacity: 0;
transform: translate(-0.5rem, 8.57142857142857rem) rotate(215deg);
}
95% {
opacity: 0;
transform: translate(0) rotate(0deg);
}
100% {
opacity: 1;
transform: rotate(0deg);
}
}
All other browsers render the animation as intended, with the first animation keyframe rotating clockwise. Safari is the only one that applies a counter-clockwise rotation in the initial keyframe. I'm assuming Safari is doing this because the distance to travel the rotation is shorter when going counter-clockwise, but it's not honoring the CSS spec properly if that's the case.
Does anyone know a remedy for this?
The problem is that if you try to animate a rotate in safari greater than 180 degrees, it will instead rotate the other way. So if you try to rotate +270 degrees, Safari will animate a rotation of -90 degrees.
The workaround for Safari is to never rotate more than 179 in either direction, then complete the rest of the rotation in another segment.

Direction of rotation in CSS Animation\Transformation

I'm trying to understand CSS animations and I'm running into the following issue:
I've got a simple div with animation attached to it in CSS, like below:
<div id="learn">LEARN</div>
#learn {
width: 100px;
height: 100px;
background: blue;
position: relative;
animation: test1 5s ease-in 2s infinite;
}
Case 1:
#keyframes test1 {
0% {
transform: rotate(179deg);
}
100% {
transform: rotate(357deg);
}
}
Case 2 (the 0% transform is split into 90+89 deg instead of 179):
#keyframes test1 {
0% {
transform: rotate(90deg) rotate(89deg);
}
100% {
transform: rotate(357deg);
}
}
Why is the first case rotating clockwise and the second case rotating counter clockwise?
This might just be a bug with rotation transforms, which I'll address after some background.
Background
First off, the way rotations are supposed to work is with relation to a circle. If you specify a number of degrees, that will place the element in the position dictated by a how much rotation the degrees specify. When not animating a rotation, there are many ways to represent the element's placement. Code Uniquely is half-right in their comment in that the placement of 357 degrees and -3 degrees is the same, but when animating, they're very different. Going from 0 degrees to -3 degrees is a small counter-clockwise rotation, whereas going from 0 degrees to 357 degrees is a large clockwise rotation.
Findings
What you've found seems to ignore this calculation (in both Firefox and Chrome from what I've tested out). From what I'm seeing, combining rotate transforms effectively reverses the direction that the rotation should be going in, even if you're combining with a 0deg rotation:
transform: rotate(90deg); //rotates clockwise
transform: rotate(0deg) rotate(90deg); //rotates counter-clockwise
It seems that you can fix this by "combining" the rotation transforms in both animation steps:
// this performs a clockwise rotation
#keyframes test3 {
0% {
transform: rotate(0deg) rotate(90deg);
}
100% {
transform: rotate(0deg) rotate(357deg);
}
}
Finally, it seems that the number of "combinations" matters. If you combine 2 rotations in one step, but 3 in another, the unexpected behavior occurs:
// rotates counter-clockwise since there are unequal rotation transforms
#keyframes test4 {
0% {
transform: rotate(0deg) rotate(0deg) rotate(90deg);
}
100% {
transform: rotate(0deg) rotate(357deg);
}
}
Conclusion
This likely isn't the greatest answer, but combining rotations like this just doesn't appear to be documented anywhere. I would suggest not combining the same rotate transforms (you can still combine rotateX and rotateY without this weirdness) and stick with the sum of the degree values.
In case anyone finding this explanation wants to perhaps find out more about this behavior than I have, here's a fiddle with the examples above as a starting point.

Rotate element to 360deg using CSS3 Animation

I am trying to create a loader animation using CSS3. Here is the code:
http://codepen.io/raaj-obuli/pen/RPeLer
If you look at the code, I've entered the css, in #keyframe defn, for rotating the squares from 0deg to 360deg ( as like below ). But the dices are not rotating. Please help on this and also let me know if you need more details.
#keyframes tilt{
0%{
transform: scale($scaleMin) rotate($rotateStart);
}
50%{
transform: scale($scaleMax);
background: #BC11FF;
box-shadow: 0 0 2px #D467FF;
}
95%,100%{
transform: scale($scaleMin) rotate($rotateEnd);
background: #11A8FF;
box-shadow: none;
}
}
PS. CSS is written using SCSS in the code sample.
It's missing the rotate() in 50% section.
$rotateMid: 225deg;/*added, adjust the value as needed*/
span {
animation: tilt #{$animDuration}s linear infinite; /*changed to linear*/
}
50%{
transform: scale($scaleMax) rotate($rotateMid); /*changed/added*/
}
Updated: http://codepen.io/anon/pen/QbJmbO?editors=110
Differences between the transition timing functions:
ease-in will start the animation slowly, and finish at full speed.
ease-out will start the animation at full speed, then finish slowly.
ease-in-out will start slowly, be fastest at the middle of the animation, then finish slowly.
ease is like ease-in-out, except it starts slightly faster than it ends.
linear uses no easing.
Source: https://stackoverflow.com/a/9636239/483779

css3 animate pngs with transparency

I have images that I want to swap in and out (no slide effect). The first cycle, the images appear stacked on top of each other (since they have holes). All subsequent cycles, it works correctly (only one visible at a time).
Html
<div class="small xfade">
<span></span>
<span></span>
<span></span>
<span></span>
</div>
css
#keyframes xfade {
0% { opacity:1; }
17% { opacity:1; }
25% { opacity:0; }
92% { opacity:0; }
100% { opacity:1; }
}
.xfade span {
animation-name: xfade;
animation-timing-function: ease-in-out;
animation-iteration-count: infinite;
animation-duration: 8s;
height: 100%;
left: 0;
/* opacity: 0; */
position: absolute;
top: 0;
width: 100%;
}
All visible at once during first loop
Only one visible during first loop, but flash
An usual scene when doing animations is where you have the same animation for several elements, but you want them to be delayed, making a sort of full cycle.
Then you set a animation-duration of say 8 seconds, and if you have 4 elements, you set a delay of 0 for the first, a delay of 2s for the second, and 4s and 6s for the others.
The problem with this is that the second element only starts animating after 2 seconds. In the meantime, it has the initial properties, that can match, or not, (usually not) the properties of the animation.
The best way to solve that is to realize that, if the animation-duration is 8s, then setting the delay to 2s is the same than setting it to -6s. because if you count 8s starting in -6s, you will end at 2s. But, then the animation is running from the first moment, with the properties that should have.
So, the delays in your case should be 0s, -6s, -4s, -2s. Just subtract the transition-duration from the transition delay.
Whenever you set an animation, and discover than the first animation is wrong, but after that they are ok, the likely problem (and solution) is this.
If you want to make everything go faster, but everything else being the same, then you have to reduce both the animation duration and all the animation delays
Example setting
.xfade span {
animation-duration: 4s;
}
.xfade span:nth-of-type(1) {
animation-delay: -1s;
}
.xfade span:nth-of-type(2) {
animation-delay: -2s;
}
.xfade span:nth-of-type(3) {
animation-delay: -3s;
}
fiddle
Also, let's analyze how the tween time is computed. First of all, you are setting the animation for an element that shares the full time with another 3 elements. That means that every element has 1/4 of the time, that is 25%.
This time has to be divided between time of full display and time of tween. Inthe original example, tween time is 8%. If you want that reduced, let's say that tween time will be 6%. Then, full display time will be 25 - 6 = 19.
That gives us the first part of the keyframes:
#keyframes xfade {
0% { opacity:1; }
19% { opacity:1; }
25% { opacity:0; }
Now, for the final keyframes, we have to remember that the tween time is 6, and set that at the end. The keyframe mus be at 100 - 6 = 94. Remainig keyframes:
94% { opacity:0; }
100% { opacity:1; }
}

Have CSS3 spin start slow then end slow?

This is not a question that can be solved by using ease-in.
If I have an element that I want to spin in CSS3 for a certain amount of time, but that starts off slow and ends slow, how can I do this?
CSS
#-webkit-keyframes spin {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(360deg); }
}
div{
background-image:-webkit-radial-gradient(center, ellipse cover, rgba(0,0,0,1) 0%,rgba(51,51,51,1) 20%,rgba(0,0,0,1) 20%,rgba(51,51,51,1) 40%,rgba(0,0,0,1) 40%,rgba(51,51,51,1) 60%,rgba(0,0,0,1) 60%,rgba(51,51,51,1) 80%,rgba(0,0,0,1) 80%,rgba(51,51,51,1) 100%);
width: 200px;
height: 200px;
-webkit-animation-name: spin;
-webkit-animation-duration: 2s;
-webkit-animation-iteration-count: 60.5;
-webkit-animation-timing-function: ease-in;
}
HTML
<div></div>
I can't seem to figure out how to do this. My animation runs for a total of 121 seconds, since it takes 2 seconds for one spin to complete, so 60.5 spins will take a total of 121 seconds (if my math is incorrect, please tell me). This works fine, except that I want the div to start spinning off slow, then completed all 59 rotations, then end slow for the last one.
I'd like to use pure CSS for this, if possible.
Sorry that I don't have a JSFiddle...
Edit: I used a relative solution in my experiment: CSS3 Clock, could that count as a half fiddle? :D
Edit #2: JSFiddle provided by #Charlie: http://jsfiddle.net/7DPnc
If it really has to be pure CSS, I would suggest wrapping 3 divs together and spin them separately:
CSS
div.first_round
{
-webkit-animation-duration:3s;
-webkit-animation-iteration-count:1;
}
div.last_round
{
-webkit-animation-duration:3s;
-webkit-animation-iteration-count:1.5;
-webkit-animation-delay:100s; /* you'll have to do the math */
}
div.main_round
{
-webkit-animation-duration:2s;
-webkit-animation-delay:3s;
-webkit-animation-iteration-count:59;
-webkit-animation-timing-function:linear;
}
HTML
<div class="first_round">
<div class="last_round">
<div class="main_round">
</div>
</div>
</div>
Or if you don't mind using a little JS, listen to animationend event...
You need 60 spins in 120 seconds right?
Lets first change the iteration count to 1.
-webkit-animation-iteration-count:1;
and the duration to 120 seconds
-webkit-animation-duration: 120s;
Now set the amount of spins. (360deg x 60spins)
#-webkit-keyframes spin {
0% { -webkit-transform: rotate(0deg); }
100% { -webkit-transform: rotate(21600deg); }
}
And now we'll modify that to set the timing. (shave a rotation off each side, add to new section)
#-webkit-keyframes spin {
10% { -webkit-transform: rotate(360deg); }
90% { -webkit-transform: rotate(20880deg); }
100% { -webkit-transform: rotate(21600deg); }
}
Lastly, we set the easing function to linear in order to avoid the stop that will occur between keyframe sections if you use a curve. (replace with ease, ease-out, etc to see what I mean)
-webkit-animation-timing-function: linear;
You can easily tweak the timing by changing duration, and the keyframe percentages.
DEMO

Resources