I have two animations called slide and bounce-return which both should be playing with 0 delay and 1s duration. Due to what I suspect is some kind of race condition in animation processing logic, these animations are out of sync by 1 frame regardless of animation speed or refresh rate. How can I stop these circles from jumping in and out?
(If you don't see any problem right away, view the snippet in full page or open the developer tools.)
.items {
--var-circle-size: 16px;
--var-circle-space: 8px;
--var-circle-border: solid red;
--var-circle-border-width: 0px;
--var-circle-shadow: 3px 2px 4px -1px rgba(0, 0, 0, 0.4);
--var-circle-shadow-high: 6px 4px 6px -2px rgba(0, 0, 0, 0.2);
--var-circle-count: 5;
--var-anim-bounce-func: cubic-bezier(0.7, 0.1, 0.6, 1.6);
--var-anim-time-unit: calc(1s);
--var-anim-color-cycle-time: calc(var(--var-circle-count)*var(--var-anim-time-unit));
--var-anim-color-cycle-func: steps(1);
--var-anim-color-1: #6db;
--var-anim-color-2: #be4;
--var-anim-color-3: #ec5;
--var-anim-color-4: #d86;
--var-anim-color-5: #f8c;
--var-anim-slide-time: var(--var-anim-time-unit);
--var-anim-slide-func: linear;
--var-circle-size-space: calc(var(--var-circle-size) + var(--var-circle-space));
--var-anim-slide-amount: calc(var(--var-circle-size-space)/2);
--var-anim-slide-start: calc(-1*var(--var-anim-slide-amount));
--var-anim-slide-end: var(--var-anim-slide-amount);
}
.maximized {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
}
.container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.items {
font-size: 0;
white-space: nowrap;
user-select: none;
pointer-events: none;
margin-right: calc(-1*var(--var-circle-space) - var(--var-circle-size-space));
animation: slide var(--var-anim-slide-time) var(--var-anim-slide-func) infinite;
}
.circle {
width: var(--var-circle-size);
height: var(--var-circle-size);
border-radius: 100%;
box-shadow: var(--var-circle-shadow);
display: inline-block;
margin: 0;
margin-right: var(--var-circle-space);
box-sizing: border-box;
border: var(--var-circle-border);
border-width: min(var(--var-circle-border-width), calc(var(--var-circle-size)/2));
background-color: var(--var-anim-bgcolor);
--var-color-cycle-anim: anim-color-cycle var(--var-anim-color-cycle-time) var(--var-anim-color-cycle-func) calc(var(--var-anim-delay-fact)*var(--var-anim-time-unit)) infinite;
animation: var(--var-color-cycle-anim);
}
#keyframes anim-color-cycle {
0% {
--var-anim-bgcolor: var(--var-anim-color-1);
}
20% {
--var-anim-bgcolor: var(--var-anim-color-2);
}
40% {
--var-anim-bgcolor: var(--var-anim-color-3);
}
60% {
--var-anim-bgcolor: var(--var-anim-color-4);
}
80% {
--var-anim-bgcolor: var(--var-anim-color-5);
}
100% {
--var-anim-bgcolor: var(--var-anim-color-1);
}
}
#keyframes slide {
0% {
transform: translate(var(--var-anim-slide-start), 0);
}
100% {
transform: translate(var(--var-anim-slide-end), 0);
}
}
#keyframes bounce-inout {
40%,
60% {
transform: translate(0, var(--var-anim-bounce-inout-fact));
box-shadow: var(--var-circle-shadow-high);
}
}
#keyframes bounce-return {
10% {
left: 0;
}
90%,
100% {
left: var(--var-anim-bounce-return-amount);
}
}
#keyframes bounce-inout-fact-alternate {
0% {
--var-anim-bounce-inout-fact: calc(-1*var(--var-circle-size-space));
}
50% {
--var-anim-bounce-inout-fact: var(--var-circle-size-space);
}
}
.circle:nth-child(1) {
--var-anim-delay-fact: -4;
}
.circle:nth-child(2) {
--var-anim-delay-fact: -3;
}
.circle:nth-child(3) {
--var-anim-delay-fact: -2;
}
.circle:nth-child(4) {
--var-anim-delay-fact: -1;
}
.circle:nth-child(5) {
--var-anim-delay-fact: -0;
}
.circle:last-child {
--var-anim-bounce-inout-fact: calc(-1*var(--var-circle-size-space));
--var-anim-bounce-return-amount: calc(-1 * var(--var-circle-count) * var(--var-circle-size-space));
position: relative;
animation: var(--var-color-cycle-anim), bounce-inout var(--var-anim-time-unit) var(--var-anim-bounce-func) infinite, bounce-return var(--var-anim-time-unit) ease-in-out infinite, bounce-inout-fact-alternate calc(2*var(--var-anim-time-unit)) steps(1) infinite;
}
<div class="maximized container">
<div class="items">
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
</div>
</div>
I have a theory that combining everything into one set of keyframes for each circle, rather than try to synchronise an animation on items with the circle animations, might help remove the problem with phasing.
To test it here's a snippet that roughly reproduces the action of the given code. In it the items div is not animated. Each circle keeps its original color and is animated to the right, up, back, to the right, down, back - with little bounces at each end.
No 'flashing/shadowing' has been observed. Also the amount of processing being done is less.
On the code in the question my laptop was showing an average of around 5% cpu and 15% gpu usage. On this snippet they are around 1.5% and 11.5% respectively.
.items {
--size: 16px;
--space: 8px;
--border: solid red;
--border-width: 0px;
--shadow: 3px 2px 4px -1px rgba(0, 0, 0, 0.4);
--shadow-high: 6px 4px 6px -2px rgba(0, 0, 0, 0.2);
--count: 5;
--bounce-func: cubic-bezier(0.7, 0.1, 0.6, 1.6);
--time-unit: calc(1s);
--cycle-time: calc(2 * var(--count)*var(--time-unit));
--color-cycle-func: steps(1);
--color-1: #6db;
--color-2: #be4;
--color-3: #ec5;
--color-4: #d86;
--color-5: #f8c;
--slide-time: var(--time-unit);
--slide-func: linear;
}
.maximized {
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
}
.container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
.items {
font-size: 0;
white-space: nowrap;
user-select: none;
pointer-events: none;
width: calc((var(--size) + var(--space)) * var(--count));
height: var(--size);
position: relative;
}
.circle {
position: absolute;
width: var(--size);
height: var(--size);
border-radius: 100%;
box-shadow: var(--shadow);
display: inline-block;
margin: 0;
margin-right: var(--space);
box-sizing: border-box;
border: var(--border);
border-width: min(var(--border-width), calc(var(--size)/2));
animation: bounceround infinite var(--cycle-time) var(--slide-func);
animation-delay: var(--delay);
background-color: var(--color);
}
.circle:nth-child(1) {
--color: var(--color-1);
--delay: -8s;
}
.circle:nth-child(2) {
--color: var(--color-2);
--delay: -6s;
}
.circle:nth-child(3) {
--color: var(--color-3);
--delay: -4s;
}
.circle:nth-child(4) {
--color: var(--color-4);
--delay: -2s;
}
.circle:nth-child(5) {
--color: var(--color-5);
--delay: 0s;
}
#keyframes bounceround {
0% {
}
36.6666% {
transform: translateX(calc(var(--count) * 100%)); /* move to the right */
}
40% {
transform: translateX(calc((var(--count) - 0.5) * 100%)) translateY(calc(-100% - var(--size))); /* go up a bit */
}
41% {
transform: translateX(calc((var(--count) - 1) * 100%)) translateY(calc(-100% - (var(--size) / 2))); /* come down a fraction */
}
45% {
transform: translateX(0) translateY(-100%); /* move to the left */
}
49% {
transform: translateX(0) translateY(calc(var(--size) / 4)); /* jump down a bit*/
}
50% {
transform: translateX(0) translateY(0); /* go down to start position */
}
86.6666% {
transform: translateX(calc(var(--count) * 100%)); /* move to the right */
}
90% {
transform: translateX(calc((var(--count) - 0.5) * 100%)) translateY(calc(100% + var(--size))); /* go down a bit */
}
91% {
transform: translateX(calc((var(--count) - 1) * 100%)) translateY(calc(100% + calc(var(--size) / 2))); /* go up a fraction */
}
95% {
transform: translateX(0) translateY(100%); /* move to the left */
}
99% {
transform: translateX(0) translateY(calc(-1 * var(--size) / 4)); /* jump up a bit */
}
100% {
transform: translateX(0) translateY(0); /* back to where we started */
}
}
<div class="maximized container">
<div class="items">
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
<div class="circle"></div>
</div>
</div>
Possible workaround: Add duplicate version of circles just with color-cycle and no moving animation and have them overlap with original one, and swap visibility of it for just a few millis when the animation iteration is about to restart.
Related
This code with pixels work perfect but with % seconds change.
Can you help me to find a solution?
Thank you for all your help on this question.
I need to write something... then I write this.
/* .qt-quotes */
.qt-quotes {
width: 600px;
background: linear-gradient(to right, grey 10px, white 0);
animation: qt-qotes-height 1s normal both, qt-qotes-width 1s normal 1s both, qt-qotes-shadow 1s 2s normal both;
}
.qt-quotes p {
height: max-content;
width: max-content;
padding: 2.5px 20px;
animation: qt-qotes-text 1s 2s normal both;
}
#keyframes qt-qotes-height {
from{max-height: 0;}
to{max-height: 100%;}
}
#keyframes qt-qotes-width {
from{max-width: 10px;}
to{max-width: 100%;}
}
#keyframes qt-qotes-text {
from{visibility: hidden;; opacity: 0;}
to{visibility: visible; opacity: 1;}
}
#keyframes qt-qotes-shadow {
from {box-shadow: none;}
to {box-shadow: 0px 0px 30px 0px grey;}
}
/* .qt-not-copyable */
.qt-not-copyable {
user-select: none;
}
<div class="qt-quotes qt-not-copyable">
<p><i>"This isn't a second..."</i></p>
<p>idk</p>
</div>
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%).
I have the following working code which I am attempting to simplify:
[tt] {
position: relative;
}
[tt]::after {
bottom: 100%;
content: attr(tt);
padding: 5px;
background: #333;
color: #fff;
}
[tt]::before,
[tt]::after {
position: absolute;
/* Middle 3 */
left: 50%;
transform: translate(-50%, 50%);
}
/* First 3 */
[tt]:nth-child(-n+3)::before,
[tt]:nth-child(-n+3)::after {
transform: translate(0 , 50%);
}
/* Last 3 */
[tt]:nth-last-child(-n+3)::before,
[tt]:nth-last-child(-n+3)::after {
transform: translate(-100%, 50%);
}
/* add animation */
[tt]:hover::before,
[tt]:hover::after {
animation: tt-move1 100ms ease-out forwards;
display: block;
}
[tt]:nth-child(-n+3):hover::before,
[tt]:nth-child(-n+3):hover::after {
animation: tt-move2 100ms ease-out forwards;
}
[tt]:nth-last-child(-n+3):hover::before,
[tt]:nth-last-child(-n+3):hover::after {
animation: tt-move3 100ms ease-out forwards;
}
#keyframes tt-move1 {
to {
transform: translate(-50%, 0);
}
}
#keyframes tt-move2 {
to {
transform: translate(0, 0);
}
}
#keyframes tt-move3 {
to {
transform: translate(-100%, 0);
}
}
/*For working demo*/
div {
/*won't work unless set relative, Something that happens in [tt]*/
top:100px;
margin: 10px;
float:left;
width: 20px;
height: 20px;
border: black solid 3px;
}
<div tt="tt1"></div>
<div tt="tt2"></div>
<div tt="tt3"></div>
<div tt="tt4"></div>
<div tt="tt5"></div>
<div tt="tt6"></div>
<div tt="tt7"></div>
<div tt="tt8"></div>
<div tt="tt9"></div>
The above code has a specific animation for each different type of element, something which seems unnecessary. To my knowledge, I am simply applying the same transform to each element (moving the element up along the y-axis) so I expected that the following should also work:
[tt] {
position: relative;
}
[tt]::after {
bottom: 100%;
content: attr(tt);
padding: 5px;
background: #333;
color: #fff;
}
[tt]::before,
[tt]::after {
position: absolute;
/* Middle 3 */
left: 50%;
transform: translate(-50%, 50%);
}
/* First 3 */
[tt]:nth-child(-n+3)::before,
[tt]:nth-child(-n+3)::after {
transform: translate(0 , 50%);
}
/* Last 3 */
[tt]:nth-last-child(-n+3)::before,
[tt]:nth-last-child(-n+3)::after {
transform: translate(-100%, 50%);
}
/*****************Changed code*******************/
/* add animation */
[tt]:hover::before,
[tt]:hover::after {
animation: tt-move 100ms ease-out forwards;
display: block;
}
#keyframes tt-move {
to {
transform: translateY(0);
}
}
/*///////////////Changed code/////////////////*/
/*For working demo*/
div {
/*won't work unless set relative, Something that happens in [tt]*/
top:100px;
margin: 10px;
float:left;
width: 20px;
height: 20px;
border: black solid 3px;
}
<div tt="tt1"></div>
<div tt="tt2"></div>
<div tt="tt3"></div>
<div tt="tt4"></div>
<div tt="tt5"></div>
<div tt="tt6"></div>
<div tt="tt7"></div>
<div tt="tt8"></div>
<div tt="tt9"></div>
After some research, I now understand that transform: translateY(Δy); is the same as saying transform: translate(0,Δy); which is causing the unexpected result. Unfortunately that was the only method I have been able to find that looks like it is supposed to do what I wanted.
I am looking for a method to transform:translate that allows the x-axis of a previous transform:translate to stay the same, while only changing the y-axis.
Is there a different way to accomplish this simplification? Or am I stuck using the repetitious code from above?
When you animate transform you have to add any already set values or else they will temporary be overwritten.
In this case you could animate the bottom instead, which will give the output you want.
[tt] {
position: relative;
}
[tt]::after {
bottom: 100%;
content: attr(tt);
padding: 5px;
background: #333;
color: #fff;
}
[tt]::before,
[tt]::after {
position: absolute;
/* Middle 3 */
left: 50%;
transform: translate(-50%, 50%);
}
/* First 3 */
[tt]:nth-child(-n+3)::before,
[tt]:nth-child(-n+3)::after {
transform: translate(0 , 50%);
}
/* Last 3 */
[tt]:nth-last-child(-n+3)::before,
[tt]:nth-last-child(-n+3)::after {
transform: translate(-100%, 50%);
}
/*****************Changed code*******************/
/* add animation */
[tt]:hover::before,
[tt]:hover::after {
animation: tt-move 100ms ease-out forwards;
}
#keyframes tt-move {
to {
bottom: 170%;
}
}
/*///////////////Changed code/////////////////*/
/*For working demo*/
div {
/*won't work unless set relative, Something that happens in [tt]*/
top:100px;
margin: 10px;
float:left;
width: 20px;
height: 20px;
border: black solid 3px;
}
<div tt="tt1"></div>
<div tt="tt2"></div>
<div tt="tt3"></div>
<div tt="tt4"></div>
<div tt="tt5"></div>
<div tt="tt6"></div>
<div tt="tt7"></div>
<div tt="tt8"></div>
<div tt="tt9"></div>
I've created a line, which appears from 0 to full length on mouse hover. In this code, the line moves from left to right. I just want to make it move from (0,0) to any given angle. Is there any way I can achieve this?
.cspaceintro .intro-container .line2 {
position: relative;
left: 890px;
bottom: 25px;
width: 2%;
height: 30px;
background-color: #3ebbff;
background: -webkit-gradient(linear, 0 0, 0 100%,from(#7BC3FF), to(#7BC3FF));
-webkit-animation: aaa 2s linear 1;
-webkit-animation-fill-mode: both;
}
#keyframes aaa {
0% {
width: 0%;
}
30% {
width: 2%;
}
60% {
width: 4%;
}
100% {
width: 5%;
}
}
<div class="cspaceintro">
<div class="intro-container">
<div id="li2"></div>
</div>
</div>
You can use CSS3 properties transform and transform-origin.
.cspaceintro {
/* rotate to the respective degrees */
-webkit-transform: rotate(45deg);
transform: rotate(45deg);
/* sets the origin point for the transformed element */
-webkit-transform-origin: 0;
transform-origin: 0;
}
Please refer the link: https://developer.mozilla.org/en/docs/Web/CSS/transform?v=example#Live_example
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.