I'm trying to animate a background image position smoothly with CSS over a longer period, let's say 60 seconds:
#movingbackground {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-image: url('https://upload.wikimedia.org/wikipedia/commons/thumb/6/68/Bigsurflowers.jpg/1280px-Bigsurflowers.jpg');
overflow: hidden;
background-position: left center;
animation: zoomin 60s ease-in infinite;
}
#-webkit-keyframes zoomin {
0% { background-position: 0% center; transform: scale(1.0); }
50% {background-position: 100% center; transform: scale(1.2); }
100% { background-position: 0% center; transform: scale(1.0); }
}
#keyframes zoomin {
0% { background-position: 0% center; transform: scale(1.0); }
50% {background-position: 100% center; transform: scale(1.2); }
100% { background-position: 0% center; transform: scale(1.0); }
}
<div id="movingbackground"></div>
The small movements in the beginning and end are "jumping" a few pixel every second instead of moving slowly (may depend on screen size).
The reason for that is probably that there is not enough movement to fill the required number of frames, especially when the animation is eased. As I think I have seen this effect working smoothly somewhere I wonder how to work around this.
Here's a Fiddle as well.
Animation of background-position makes browser to do layout, paint and composite.
Re-layout and re-paint are heavy on CPU and cause "jumping".
Instead of that, you might apply your background to pseudo-element (or use <img> in your HTML) and animate its transform property using 3d transformation.
It will make browser to use GPU for the animation and animation will run in composition phase pretty smoothly.
See the snippet below:
html,
body {
margin: 0;
padding: 0
}
#movingbackground {
position: relative;
width: 100vw;
height: 100vh;
overflow: hidden;
}
#movingbackground:before {
content: '';
position: absolute;
top: 0; left: 0; z-index: -1;
height: 100%;
width: 200%;
background: url(https://upload.wikimedia.org/wikipedia/commons/thumb/6/68/Bigsurflowers.jpg/1280px-Bigsurflowers.jpg) 0 50% / cover;
animation: zoomin 60s ease-in infinite;
}
#keyframes zoomin {
50% {
transform: translateX(-50%) scale(1.2)
}
}
<div id="movingbackground"></div>
I did some testing and came to the conclusion that it's probably impossible. (At least with transitions or animations)
The problem is the way browsers render images on a screen. The pixels of the image apparently get lined up with those of your screen.
So the picture always "jumps" exactly one pixel at a time.
That means, that the more pixels you have in your image, the more steps it will make. But when using ease-in it will always stutter in the beginning.
As I think I have seen this effect working smoothly somewhere
That was probably not realized with css.
Related
I have some problems with a CSS transition effect. I don't understand why, but it isn't working. Here is a demo that isn't working :
https://codyhouse.co/demo/ink-transition-effect/index.html
Here is an article about how this effect was done (before, when it did work) :
https://codyhouse.co/gem/ink-transition-effect
The code I'm working on to debug is this one :
https://codepen.io/1019/pen/YzxzNGX
HTML file :
<body>
CSS ANIMATIONS TEST
<div class='cd-transition-layer'>
<div class="bg-layer"></div>
</div>
</body>
CSS file :
.cd-transition-layer {
position: fixed;
top: 0;
left: 0;
z-index: 30;
height: 100%;
width: 100%;
}
.cd-transition-layer .bg-layer {
position: absolute;
left: 50%;
top: 50%;
z-index: 15;
transform: translateY(-50%) translateX(-2%);
height: 100%;
width: 2500%;
background: url('https://i.imgur.com/9uDdPAP.png') no-repeat 0 0;
background-size: 100% 100%;
animation: cd-sprite 5s steps(24);
animation-fill-mode: forwards
}
.cd-transition-layer.opening .bg-layer {
z-index: 15;
animation: cd-sprite .8s steps(24);
animation-fill-mode: forwards
}
#keyframes cd-sprite {
0% {
transform: translateY(-50%) translateX(-2%)
}
100% {
transform: translateY(-50%) translateX(-98%)
}
}
Can you please help me find what is wrong ?
Thank you !
EDIT : Okay, weird : it seems the div just completely disappears during the animation before reappering. If I keep focus on the div in the inspector, it stays there. Is it because it's too long (2500% width) ?
Moving large divs
It seems that animating a large div over the screen very fast can cause a render/flicker in webkit based browsers.
If i have to guess, it's probably due to performance reasons, where the browser cuts off things thats are not in the viewport. when moving to the next frame, it will not have the pixels ready to be rendered, resulting in a flicker.
It becomes more apparent when you remove the steps(24) from the animation.
The div will slide over the screen, and at some point just stop being visible.
Using background-position instead
When animating, instead of moving a div over the screen, we can also opt to move only the background instead.
background: url("https://i.imgur.com/9uDdPAP.png") no-repeat;
background-size: 2500% 100%; /* Size is needed to stretch 1 frame to fit the div */
background-position: 0% 0%; /* we can start from frame 0 */
animation: cd-sprite 1s steps(24);
/* the animation is the same, we only move the background instead. (in 24 steps) */
#keyframes cd-sprite {
0% {
background-position: 0% 0%;
}
100% {
background-position: 100% 0%;
}
}
* {
box-sizing: border-box;
}
.cd-transition-layer {
position: fixed;
top: 0;
left: 0;
z-index: 30;
height: 100%;
width: 100%;
}
.cd-transition-layer .bg-layer {
position: absolute;
left: 50%;
top: 50%;
z-index: 15;
height: 100%;
width: 100%;
background: url("https://i.imgur.com/9uDdPAP.png") no-repeat;
background-size: 2500% 100%;
background-position: 4.16% 0%;
transform: translateY(-50%) translateX(-50%);
animation: cd-sprite 1s steps(24) infinite;
animation-direction: alternate;
animation-delay: 1s;
animation-fill-mode: forwards;
border: 36px solid red;
}
#keyframes cd-sprite {
0% {
background-position: 0% 0%;
}
100% {
background-position: 100% 0%;
}
}
<body>
<div class='cd-transition-layer'>
<div class="bg-layer"></div>
</div>
</body>
I have this code
.wave {
position: absolute;
height: 90vh;
width: 1920px * 2;
background-image: url(...);
background-size: 1920px 100%;
background-repeat: repeat-x;
animation: wave 3s linear infinite;
#keyframes wave {
0% {
transform: translateX(0);
// left: 0;
}
100% {
transform: translateX(-1920px);
// left: -1920px;
}
}
}
which should loop seamlessly creating a continuous wave motion. Unfortunately, in safari, it flickers on every loop. I have tried all the -webkit stuff and -webkit-backface-visibility: hidden, but no luck
If I remove transform: translateX(...) and animate left instead, the flickering disappears, but I want to use transform for perfomance reasons
I have created this simple example here
You can see the flicker on every loop (3s) in safari. Works fine in chrome
Adding -webkit-transform: translateZ(0); to img or picture element will do the trick
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 have read about how using translate has better performance, but it seems they behave slightly differently: using left:100% moves the animated object all the way to the end of the screen, whereas translate(100%) only moves the animated object as far as its length. That is, it moves 100% of the screen versus 100% of the object.
Can explain why this is, and what can be done to reproduce the same behavior when using translate?
You can see a demo here: http://jsfiddle.net/32VJV/1/
.slide_1 {
top: 0px;
left:0%;
position: absolute;
overflow: hidden;
font-size: 30px;
}
.slide_1 {
-webkit-animation: slide 3s infinite;
-webkit-animation-delay: 0s;
-webkit-animation-fill-mode:forwards;
-webkit-transform-origin: 0% 0%;
}
.slide_2 {
top: 25px;
left:0%;
position: absolute;
overflow: hidden;
font-size: 30px;
}
.slide_2 {
-webkit-animation: slide2 3s infinite;
-webkit-animation-delay: 0s;
-webkit-animation-fill-mode:forwards;
-webkit-transform-origin: 0% 0%;
}
#-webkit-keyframes slide {
0% {
-webkit-transform: translate(0%);
}
50% {
-webkit-transform: translate(100%);
}
100% {
-webkit-transform: translate(0%);
}
}
#-webkit-keyframes slide2 {
0% {
left 0%;
}
50% {
left:100%;
}
100% {
left:0%;
}
}
<div style="font-size:18px;">
<div class=""> <span class="slide_1" id="dimensions">ABC</span> <span class="slide_2" id="dimensions">ABC</span>
</div>
</div>
The difference between the two is that animating a property like left will keep the element in the flow of the document whereas translate does not.
For more information on why you might use one or the other, Paul Irish has an excellent write up (with links to more information): Why Moving Elements With Translate() Is Better Than Pos:abs Top/left
There's also a lot of great information on browser performance at jankfree.org
Solution for the translate animation: make the element as wide as the window:
Example
slide_1 {
top: 0px;
left:0%;
right: 0;
position: absolute;
overflow: hidden;
font-size: 30px;
}
An interesting exercise: Open your devtools and what what happens when you activate one animation at a time.
In Chrome:
The translate animation has basically nothing going on except a periodic GC
The Left animation you will see repeatedly:
Recalculate Style
Layout
Pain Setup
Paint
Composite Layers
In this case, the overhead it pretty small, but that can change quickly depending on what is being moved around the screen.