I need to create infinite animation that will start with fast rotation ( e.g. 1 second) then gradually slow down (within another e.g. 1 second) and then continue on the very slow speed (for the remaining e.g. 8 seconds). The problem is - rotation speed changes with very sharp jumps - on 10% and 20%.
Can I control transition between animation speeds? I tried to override speed jump by adding more percentages but it just gives second jump after 20% when speed changes.
html {
height: 100%;
}
body {
height: 100%;
background: #333;
display: flex;
align-items: center;
justify-content: center;
}
.bar {
background: cyan;
width: 100px;
height: 10px;
}
.bar {
animation: rotation 10s linear infinite;
}
#keyframes rotation {
0% {
transform: rotate(0deg);
}
10% {
transform: rotate(1600deg);
}
11% {
transform: rotate(1620deg);
}
12% {
transform: rotate(1640deg);
}
13% {
transform: rotate(1660deg);
}
14% {
transform: rotate(1680deg);
}
15% {
transform: rotate(1700deg);
}
16% {
transform: rotate(1720deg);
}
17% {
transform: rotate(1740deg);
}
18% {
transform: rotate(1760deg);
}
19% {
transform: rotate(1800deg);
}
20% {
transform: rotate(1820deg);
}
100% {
transform: rotate(2160deg);
}
}
<div class="bar"></div>
You can use multiple animations: one for the initial spin with deceleration (take a look at the easing functions. In this case I'm using ease-out which mimics basic deceleration) and a second (delayed to run after the first finishes) to be linear. You'll have to play around with the values of degrees and duration to match the speed of rotation from the first animation with the linear speed of the second, otherwise you'll see the speed jump quickly (your problem in the first place). Here's an example:
html {
height: 100%;
}
body {
height: 100%;
background: #333;
display: flex;
align-items: center;
justify-content: center;
}
.bar {
background: cyan;
width: 100px;
height: 10px;
}
.bar {
animation: rotationDecelerate 2s ease-out, rotationLinear 2s linear 2s infinite;
}
#keyframes rotationDecelerate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(2160deg);
}
}
#keyframes rotationLinear {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
<div class="bar"></div>
It's just a matter of fiddling with the numbers.
I removed all the intermediate transformations between 10 and 20%. The animation calculates the position of the element based on timing functions and the sort between the two points.
The reason why you were getting a big jump is that you were trying to control every intermediate step between 10 and 20 but the animation had to be at a certain point at 20%. Let the browser control everything between 10 and 20% since you want a smooth slowdown. The timing function takes into account where you started and where you want to end, so it tries to smooth everything out for you. The more defined every percentage point is, the more stilted the animation will be.
I also played around with the values a little bit. You can put them back how you want them, but I just wanted to see how it would affect the animation if the first sec was 5 rotations, then the next second was 1 rotation, and then last 80% was one rotation. It just seemed proportional to me, and the animation looked smoother. But, I recommend playing with the degrees until you get what you want.
html {
height: 100%;
}
body {
height: 100%;
background: #333;
display: flex;
align-items: center;
justify-content: center;
}
.bar {
background: cyan;
width: 100px;
height: 10px;
}
.bar {
animation: rotation 10s linear infinite;
}
#keyframes rotation {
0% {
transform: rotate(0deg);
}
10% {
transform: rotate(1800deg);
}
20% {
transform: rotate(2160deg);
}
100% {
transform: rotate(2520deg);
}
}
<div class="bar"></div>
Related
I am trying to animate a line that expands both ways from the centre using transform:scale but for some reason the line kind of "rewinds" slightly when it reaches the end, but only on the right side of the line. This only seems to happen on firefox, (both on mobile and desktop) but seems fine on chrome.
<div class="line"></div>
<style>
.line {
height: 4px;
width: 5px;
background-color: #5d496a;
margin: 0 50%;
animation: line_animation 1s forwards ;
}
#keyframes line_animation {
0% {
transform: scale(1,1);
}
100%{
transform: scale(22,1);
}
}
</style>
I am still learning animations so I am not sure what I am doing wrong. Any help would be very appreciated.
https://www.w3schools.com/code/tryit.asp?filename=GRA6EYT2GLSX
Looks like it was an issue with scale being greater than 1.
Fixed by changing width: 5px; to width: 15%; and changed
#keyframes line_animation {
0% {
transform: scale(1,1);
}
100%{
transform: scale(22,1);
}
}
to
#keyframes line_animation {
from {
transform: scale(0.01,1);
}
to{
transform: scale(1,1);
}
}
This is my first time asking a question on here and I've found questions that are somewhat similar, but haven't worked for my issue.
I am trying to spin a word across the screen from off-screen left to off-screen right. The center of the word should be it's rotation point (ie word spins in place from left side of screen to right). I have tried using variations of translateX and rotate, but it either rotates in place or moves left to right. When it does move from the left to right off the screen, it keeps extending the bounds of my screen and stretching it before it loops back to the left side. Any ideas how I can solve this? Seems simple, but I'm terrible with animations.
.move {
position: absolute;
animation: moveword 10s infinite linear;
}
.spin {
position: absolute;
animation: spin 7s infinite linear;
}
#keyframes spin {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
#keyframes moveword {
from {
left: -10%;
}
to {
left: 95%;
}
}
Based on code that you provide, I assume you could make something like this.
overflow: hidden needs to be applied to separate element, not the <body> because it restricts scrolling.
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
.page {
position: relative;
width: 100vw;
height: 100vh;
overflow: hidden;
}
.word {
position: absolute;
top: 5px;
left: 5px;
animation: word-anim 10s infinite linear;
}
#keyframes word-anim {
0% {
transform: translateX(0px) rotateZ(0deg);
}
70% {
transform: translateX(70vw) rotateZ(360deg);
}
100% {
transform: translateX(100vw) rotateZ(360deg);
}
}
<div class="page">
<span class="word">A word</span>
</div>
The animation I created gradually slides in the element with the class of .overlay from right to left in .4 seconds by adding the .overlay-appear class to it on click. It works perfectly in Mozilla. In Chrome the slide effect doesn't take place, the element simply appears when I click that specific button. I added the vendor prefixes to keyframes, animation and transform, but the problem still persists. Maybe I'm missing something?
Here's the CSS that is relevant to the problem:
.overlay {
position: fixed;
background-color: rgb(49, 49, 49);
z-index: 100;
top: 0;
right: 0;
width: 0%;
height: calc(100vh - 25px);
display: none;
flex-direction: column;
justify-content: flex-start;
align-items: flex-end;
padding: 25px 50px 0 0;
}
.overlay-appear {
animation: appear .4s forwards;
-webkit-animation: appear .4s forwards;
width: 50%;
display: flex;
transform-origin: right;
-webkit-transform-origin: right;
}
#keyframes appear {
0% {
transform: scaleX(0%);
}
100% {
transform: scaleX(100%);
}
}
#-webkit-keyframes appear {
0% {
-webkit-transform: scaleX(0%);
}
100% {
-webkit-transform: scaleX(100%)
}
}
You missed the fact that transfrom:scale(); only takes simple integers (not measurements of any kind.). I don't know why Firefox CSS engine accepts that. But to make the code legal change the values.
#keyframes appear {
0% {
transform: scaleX(0);
}
100% {
transform: scaleX(1);
}
}
The scaleX(1) means the actual size of the div as specified by its code. Any other measure there means the relative scale (and not any absolute measure).
I'm working with some basic CSS animation, and I can't seem to figure out the rendering logic for various transforms. If I do the following animation, the target rotates from 0 to 350deg clockwise, as expected.
#keyframes rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(350deg);
}
}
However, when I add in other transforms, the rotation starts taking shortcuts (going from 0 to 350deg counter-clockwise). This even results in no visible rotation if I'm going from 0 to 360deg. Here's an example:
#keyframes rotate {
0% {
transform: rotate(0deg);
}
100% {
transform: translate(500px) rotate(350deg);
}
}
An intermediate keyframe solves some of the rotation issues, but messes up even more when used with multiple transforms.
How can I fix this - that is, in this example, how can I add a translation to the rotating square without having it change rotation behavior (taking the short CCW rotation to 350deg instead of the full CW rotation like it does normally without a translation)?
Here's a CodePen with some examples:
http://codepen.io/jbjw/pen/gmyajY
If you don't specify the starting point of rotate(0), it animates as expected.
div {
width: 50px;
height: 50px;
background-color: green;
animation: rotate 3s linear infinite;
}
#keyframes rotate {
100% {
transform: translate(500px) rotate(350deg);
}
}
body {
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
<div></div>
Alternatively you can specify your translate(0) starting point and it will work as expected.
div {
width: 50px;
height: 50px;
background-color: green;
animation: rotate 3s linear infinite;
}
#keyframes rotate {
0% {
transform: translate(0) rotate(0deg);
}
100% {
transform: translate(500px) rotate(350deg);
}
}
body {
height: 100vh;
display: flex;
justify-content: center;
align-items: center;
}
<div></div>
My best guess is that it sees that at 0%, you are at 0 degrees. At 100%, you are at 350degrees. This is actually just -10degrees from where you started, so it will from the start it knows it only has to rotate (-)10 degrees. You can fix this by adding in 50% and taking both the transforms half values:
http://codepen.io/anon/pen/PpgNje
#keyframes rotate {
0% {
transform: rotate(0deg);
}
50% {
transform: translate(250px) rotate(175deg)
}
100% {
/* transform: rotate(350deg); */
transform: translate(500px) rotate(350deg);
}
}
I suppose this is more academic than anything but I have an element I'm setting up to infinite spin and in my CSS file I have to add this (yes, I have my moz and webkit declarations in there too but that's not relevant here)
#keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(359deg); }
}
It seems odd not to set 360 here but it seems to work with 359. Is 360 simply equivalent to 0 or what? Trying to understand how this works and if there's a practical difference.
This is used, because on an infinite animation if the end and start are the same then there would be a delay when starting the next cycle..
0 and 360 degrees having the same representation would each consume a step of the animation.
tldr:
/********* CORRECT *********/
#keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
/********* NOT CORRECT *********/
#keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(359deg); }
}
full explanation:
The following syntaxes are equivalent and all produce a perfectly smooth and continuous infinite circular animation:
#keyframes spin-turn {
from { transform: rotate(0turn); }
to { transform: rotate(1turn); }
}
#keyframes spin-grad {
from { transform: rotate(0grad); }
to { transform: rotate(400grad); }
}
#keyframes spin-deg {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
#keyframes spin-rad {
from { transform: rotate(0rad); }
to { transform: rotate(6.28318530718rad); } /* 2 × π */
}
Most importantly, there is no animation step in between 100% and 0%.
When transitioning between animation iterations, the frame corresponding to 100% is not rendered. The browser uses its value to calculate all the intermediary frames up to it (their number depends on device refresh rate and browser rendering engine, not on the CSS unit used). When the next animation iteration exists, the frame corresponding to 100% is replaced by the first frame of the next iteration (the 0% one).
The 100% frame is only rendered when animation-iteration-count is a positive finite number, after all iteration counts have been played. In other words, when the animation has stopped.
Thus, using to: { transform: rotate(359deg); } produces a jump forward in the animation of exactly 1deg. It's a jump equal to 0.2(7)% of a full turn and is therefore hardly noticeable when the animated element is small or when the speed of animation is relatively fast.
Here's a tool to compare the different syntaxes. The jump forward can be observed at the top position (when the radar line reaches the north point of the circle), with deg-359 animation syntax selected, with an animation duration above 10 seconds.
new Vue({
el: '#app',
data: () => ({
units: ['turn', 'grad', 'rad', 'deg', 'deg-359'],
animation: 'spin-deg-359',
duration: 18
}),
computed: {
rotatorStyle() {
return {
animation: `${this.animation} ${this.duration}s linear infinite`
}
}
}
})
body {
margin: 0;
}
* { box-sizing: border-box; }
.controls > div {
display: flex;
padding: 3px 0;
}
.controls {
padding: 1rem;
}
.controls label {
width: 80px;
}
input[type="number"] {
width: 73px;
}
#app {
height: 100vh;
padding: 1rem;
display: flex;
align-items: flex-start;
justify-content: space-evenly;
overflow: hidden;
}
.ratio {
display: grid;
width: calc(100vh - 2rem);
}
#media(max-width: 767.98px) {
#app {
flex-direction: column;
height: auto;
}
.ratio {
width: 100%;
flex: 0 0 auto;
}
}
.ratio > * {
grid-area: 1/1;
}
.rotator {
display: flex;
border: 1px solid red;
border-radius: 50%;
align-items: center;
justify-content: center;
position: relative;
}
.rotator:before {
content: '';
position: absolute;
left: 50%;
width: 50%;
height: 0;
top: 50%;
transform: rotate(-90deg);
transform-origin: 0 0;
border-bottom: 1px solid red;
}
#keyframes spin-deg {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
#keyframes spin-deg-359 {
from { transform: rotate(0deg); }
to { transform: rotate(359deg); }
}
#keyframes spin-rad {
from { transform: rotate(0rad); }
to { transform: rotate(6.28318530718rad); }
}
#keyframes spin-turn {
from { transform: rotate(0turn); }
to { transform: rotate(1turn); }
}
#keyframes spin-grad {
from { transform: rotate(0grad); }
to { transform: rotate(400grad); }
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>
<div id="app">
<div class="controls">
<div>
<label for="duration">duration</label>
<input id="duration" v-model="duration" type="number">s
</div>
<div>
<label for="animation">animation</label>
<select id="animation" v-model="animation">
<option v-for="unit in units" :key="unit" :value="'spin-' + unit">{{unit}}</option>
</select>
</div>
</div>
<div class="ratio">
<svg viewBox="0 0 1 1"></svg>
<div class="rotator"
:style="rotatorStyle"></div>
</div>
</div>
The jump becomes more noticeable as you increase the screen size (full-page mode) or as you increase the animation-duration (i.e: 30s).
360 is 0, there are 360 degrees in a circle. So 360 would take you full circle, back to 0.