Difference in CSS rotate 359 vs rotate 360 - css

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.

Related

How to avoid sharp jumps between speeds in css animation

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>

css transition for page transitioin not triggering

I have a page transition I'm trying to work into my site. I have 2 50% height, 100% width elements, one placed before and after the body (with pseudo-selectors). I would like the 2 elements to slide to the middle of the screen, covering the background content. The transition is triggered when the "is-changing" class is added to the body, via Javascript.
document.getElementById("btn").addEventListener("click", fakeReq);
function fakeReq() {
let body = document.body;
body.classList.add("is-changing");
console.log("class added");
setTimeout(function() {
body.classList.remove("is-changing");
console.log("class removed");
}, 5000);
}
body {
height: 100vh;
width: 100%;
background-color: grey;
}
main {
display: flex;
height: 100%;
justify-content: center;
align-items: center;
}
body::after, body::before {
height: 50vh;
width: 100%;
position: fixed;
left: 0;
background-color: blue;
z-index: 10;
}
body::before {
top: 0;
transform: translateY(-100%);
}
body::after {
bottom: 0;
transform: translateY(100%);
}
body.is-changing::after, body.is-changing::before {
transform: translateY(0);
}
.loading-bar {
position: fixed;
height: 2px;
width: 90%;
}
.loading-bar::before {
position: absolute;
background-color: aqua;
left: 0;
top: 0;
height: 100%;
width: 100%;
transform: scaleX(0);
transform-origin: left center;
}
.is-changing .loading-bar::before {
transform: scaleX(1);
}
<body>
<main>
<div class="index main-content">
<h1>Home Page</h1>
<button id="btn">html request</button>
</div>
</main>
</body>
It looks to me like you're running into two issues.
The first issue is that you forgot to include the content attribute in your pseudo elements (often this will be empty, like content: ""). Without this attribute, your pseudo elements will not exist in the DOM. Running your code snippet and inspecting it confirms this, since the pseudo elements are nowhere to be found.
Second, you're creating multiple pseudo elements. body::before is it's own pseudo element, and body.is-changing::before is a separate pseudo element. If you are hoping to create a constant set of elements that act as "doors" for a loading display, you may want to consider creating two real elements that sit in position: fixed above and below the viewport, and then slide in or out when a class is added. Perhaps these could be div.upper-door and div.lower-door.
Also, it looks to me like you're in need of a transition for your transform, or else the pseudo elements will just "snap" back and forth. You can take control of the position of your elements at different points during this transition by using a css animation. Your JavaScript would largely remain the same, except for targeting the .upper-door and .lower-door divs using document.querySelector(), or simply using IDs rather than classes and targeting with getElementById(), if that makes more sense for you. Your css might look like this:
div.upper-door {
top: 0;
transform: translateY(-100%);
}
div.upper-door.is-changing {
animation-duration: 5s;
animation-name: upper-door-closeopen;
}
div.lower-door {
bottom: 0;
transform: translateY(100%);
}
div.lower-door.is-changing {
animation-duration: 5s;
animation-name: lower-door-closeopen;
}
#keyframes upper-door-closeopen {
0% {
transform: translateY(-100%);
}
25% {
transform: translateY(0);
}
75% {
transform: translateY(0);
}
100% {
transform: translateY(-100%);
}
}
#keyframes lower-door-closeopen {
0% {
transform: translateY(100%);
}
25% {
transform: translateY(0);
}
75% {
transform: translateY(0);
}
100% {
transform: translateY(100%);
}
}
The css animation will be triggered when .is-changing is added to the element. As you experiment, you may find different permutations of this solution (such as using event listeners if a button click is triggering the loading screen) to be ideal.
There is a great resource on MDN for css animations if you would like more information.
You missed to add the content property on the pseudo-elements which is mandatory to make them available on the page. You also missed to add the transition property on the pseudo-elements to achieve your animation of sliding up/down.
Here's a snippet containing a working demo, I only used the code that is related to your issue:
document.getElementById("btn").addEventListener("click", fakeReq);
function fakeReq() {
let body = document.body;
body.classList.add("is-changing");
console.log("class added");
setTimeout(function() {
body.classList.remove("is-changing");
console.log("class removed");
}, 5000);
}
body {
height: 100vh;
width: 100%;
background-color: grey;
position: relative; /* not really related to your issue but, to make sure that the body's pseudo-elements are positioned relative to the body */
}
main {
display: flex;
height: 100%;
justify-content: center;
align-items: center;
}
body::after, body::before {
content: ""; /* make the pseudo-elements available */
height: 50vh;
width: 100%;
position: fixed;
left: 0;
background-color: blue;
z-index: 10;
transition: all .8s ease-out; /* allow the animation, change this rule per your requirements */
}
body::before {
top: 0;
transform: translateY(-100%);
}
body::after {
bottom: 0;
transform: translateY(100%);
}
body.is-changing::after, body.is-changing::before {
transform: translateY(0);
}
<body>
<main>
<div class="index main-content">
<h1>Home Page</h1>
<button id="btn">html request</button>
</div>
</main>
</body>
Learn more about after pseudo-element.
Learn more about before pseudo-element.
You can use the following
document.getElementById("btn").addEventListener("click", fakeReq);
function fakeReq() {
let body = document.body;
body.classList.add("is-changing");
console.log("class added");
setTimeout(function() {
body.classList.remove("is-changing");
console.log("class removed");
}, 5000);
}
body {
height: 100vh;
width: 100%;
background-color: grey;
}
main {
display: flex;
height: 100%;
justify-content: center;
align-items: center;
}
body::after, body::before {
content:'';
height: 50vh;
width: 100%;
position: fixed;
left: 0;
background-color: blue;
z-index: 10;
}
body::before {
content:'';
top: 0;
transform: translateY(-100%);
transition: .5s all;
}
body::after {
content:'';
bottom: 0;
transform: translateY(100%);
}
body.is-changing::after, body.is-changing::before {
content:'';
transform: translateY(0);
}
.loading-bar {
position: fixed;
height: 2px;
width: 90%;
}
.loading-bar::before {
content:'';
position: absolute;
background-color: aqua;
left: 0;
top: 0;
height: 100%;
width: 100%;
transform: scaleX(0);
transform-origin: left center;
}
.is-changing,.loading-bar::before {
transform: scaleX(1);
}
<body>
<main>
<div class="index main-content">
<h1>Home Page</h1>
<button id="btn">html request</button>
</div>
</main>
</body>

Making pure css image slider responsive

The following pure css slider is working well, but I need to make it responsive. I've tried replacing the pixel-based sizes with percentages and with vw, but it doesn't line up. I'd be grateful for any ideas.
Here's the html:
<section class="slideshow">
<div class="slideshow-container slide">
<img src="images/anim/home-animation1.jpg" alt="pills">
<img src="images/anim/home-animation2.jpg" alt="scientist">
<img src="images/anim/home-animation3.jpg" alt="chemical structure">
<img src="images/anim/proudmembermassbio.jpg" alt="proud member of MassBio"> </div>
</section>
And the css:
/*general styles*/
html { box-sizing: border-box; }
*, *:before, *:after { box-sizing: inherit; }
/* SLIDESHOW STYLES */
.slideshow-container {
width: 1400px; /* the entire "stage" */
font-size: 0;
transition: 1s ease;
height: 315px;
}
.slideshow-container:hover { animation-play-state: paused; }
.slideshow { /* the visible "stage" */
width: 350px;
margin: 1rem auto -1rem;
overflow: hidden;
border: solid 1px white;
}
img, .text-container {
width: 350px;
height: auto;
display: inline-block;
font-size: 16px;
text-align: center;
}
.text-container { /* for text slides */
height: 195px;
position: relative;
}
.slide { animation: slide 10s ease infinite; }
#keyframes slide {
0% { transform: translateX(0%); }
12.5% { transform: translateX(0%); }
25% { transform: translateX(-25%); }
37.5% { transform: translateX(-25%); }
50% { transform: translateX(-50%); }
62.5% { transform: translateX(-50%); }
75% { transform: translateX(-75%); }
87.5% { transform: translateX(-75%); }
99% { transform: translateX(-75%); }
100% { transform: translateX(0); }
}
.p {
margin-top: 140px;
text-align: center;
}
Maybe this is too late for the user that posted this question, but can be helpful for someone else that want a pure responsive CSS slider.
I have created a working example in this CodePen that is working as requested using percentages for widths and in the animation, and for this reason it is responsive and works really well in each resolutions.
All the main solution to have the responsiveness is here:
slider__container {
display: flex;
position: relative;
animation: 30s slide infinite;
font-size: 0;
width: 1000%; /* because I am using 10 slides */
}
The width should be calculated accordingly to how many slides are there in the slider: slides x 100% (slides times 100%, in my example 1000%).

How can I add two transition transforms but one after one?

I want to add 2 transition transforms
But I want to start the second transform after the end of the first transform
the element should go to a point slowly and after that it should go to another point
transform: translate(0%, 300%), translate(15%, -136%);
You cannot do this with just a single element using transition because when you put more than one translate within the transform, the transform property on the whole is transitioned and not one by one.
With pure CSS transition using an extra wrapper element:
If you add an extra wrapper element around the actual element and put one of the transforms on the wrapper element you could achieve the effect that you are looking for. It would also produce the exact reverse effect on the hover out (hover the body and hover out in the below snippet).
.wrapper {
position: relative;
height: 100px;
width: 100px;
transition: all 1s 1s;
}
.content {
position: absolute;
height: 100%;
width: 100%;
border: 1px solid;
transition: all 1s;
}
body:hover .content {
transform: translate(15%, -136%);
transition: all 1s 1s;
}
body:hover > .wrapper {
transform: translate(0%, 300%);
transition: all 1s;
}
body {
min-height: 100vh;
}
<div class='wrapper'>
<div class='content'>Some text</div>
</div>
Transition with a bit of JS/jQuery without any extra elements:
If you add an extra wrapper element around the actual element and put one of the transforms on the wrapper element you could achieve the effect that you are looking for. It would also produce the exact reverse effect on the hover out (hover the body and hover out in the below snippet).
$(document).ready(function() {
var isHover; /* variable to track state */
$('body').hover(function() {
isHover = !isHover; /* invert the state */
$('.content').css('transform', 'translate(0%, 300%)');
}, function() {
isHover = !isHover; /* invert the state */
$('.content').css('transform', 'translate(0%, 300%)');
});
$('.content').on('transitionend', function() {
if (isHover) {
$('.content').css('transform', 'translate(0%, 300%) translate(15%, -136%)');
} else {
$('.content').css('transform', 'none');
}
});
});
.content {
height: 100px;
width: 100px;
border: 1px solid;
transition: all 1s;
}
body {
min-height: 100vh;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='content'>Some text</div>
With animation and no extra element:
Using animations this can be done using a single element but the reverse effect is tough to achieve. We would have to write extra code for this and even then it will be complex.
.content {
position: relative;
height: 100px;
width: 100px;
border: 1px solid;
}
body:hover > .content {
animation: move 1s forwards;
}
#keyframes move {
0% {
transform: none;
}
50% {
transform: translate(0%, 300%);
}
100% {
transform: translate(0%, 300%) translate(15%, -136%);
}
}
body {
min-height: 100vh;
}
<div class='content'>Some text</div>
Animations with reverse effect:
Below is a snippet which produces the reverse effect also using CSS animations. But as you can see it is a bit complex. We can do this using a single animation also but it would become more complex.
$(document).ready(function() {
$('body').hover(function() {
$('.content').css('transform', 'none');
$('.content').removeClass('hover-out').addClass('hover-in');
}, function() {
$('.content').css('transform', 'translate(0%, 300%) translate(15%, -136%)'); /* as soon as an animation is removed, the element would snap back to original state, to avoid that we have to add final state via inline style */
$('.content').removeClass('hover-in').addClass('hover-out');
});
});
.content {
height: 100px;
width: 100px;
border: 1px solid;
}
.hover-in {
animation: hover-in 1s forwards;
}
.hover-out {
animation: hover-out 1s forwards;
}
#keyframes hover-in {
0% {
transform: none;
}
50% {
transform: translate(0%, 300%);
}
100% {
transform: translate(0%, 300%) translate(15%, -136%);
}
}
#keyframes hover-out {
0% {
transform: translate(0%, 300%) translate(15%, -136%);
}
50% {
transform: translate(0%, 300%);
}
100% {
transform: none;
}
}
body {
min-height: 100vh;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div class='content'>Some text</div>

Rotate multiple elements at the same time

I have list of items (li) that are stacked around in circle using transformation. Items have different angle depending on its initial position in the circle:
transform: rotate(0deg) translateY(-75px) rotate(-0deg)
transform: rotate(45deg) translateY(-75px) rotate(-45deg)
... etc
last negative rotate is to rotate ccw to keep element in up position.
translate Y is radius offset.
What I would like to accomplish is to rotate all items around for various number of degrees or let's say if one item gets clicked I would like all items to rotate so that clicked item will be on the top position (0deg).
What would be best way to do this? Looking for some clever ways.. All items should ofcourse rotate at the same time - like a dialer on the old phone.
With some jQuery, you can change the class of the container and assign the rotation value to the container :
fiddle
$('.one').click(function() {
$('#container').removeClass('second third fourth').addClass('first');
});
$('.two').click(function() {
$('#container').removeClass('first third fourth').addClass('second');
});
$('.three').click(function() {
$('#container').removeClass('first second fourth').addClass('third');
});
$('.four').click(function() {
$('#container').removeClass('first second third').addClass('fourth');
});
#container {
position: relative;
width: 30%;
padding-bottom: 30%;
margin: 10% auto;
transition: transform .5s ease-out;
}
.elt {
position: absolute;
padding: 2%;
background: teal;
cursor: pointer;
transition: transform .5s ease-out;
}
.one { top: 0; left: 47.5%; }
.two { top: 47.5%; right: 0; }
.three { bottom: 0; left: 47.5%; }
.four { top: 47.5%; left: 0; }
#container.first { transform: rotate(0deg); }
#container.first div { transform: rotate(0deg); }
#container.second { transform: rotate(-90deg); }
#container.second div { transform: rotate(90deg); }
#container.third { transform: rotate(180deg); }
#container.third div { transform: rotate(-180deg); }
#container.fourth { transform: rotate(90deg); }
#container.fourth div { transform: rotate(-90deg); }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="container">
<div class="elt one">1</div>
<div class="elt two">2</div>
<div class="elt three">3</div>
<div class="elt four">4</div>
</div>

Resources