I'm working on a simple CSS animation and came across a curious problem.
When animating several small divs, if I zoom in or out on Chrome/Firefox the heights of the divs becomes inconsistent - despite them all sharing the same size styles.
Is there any way to address this using CSS? I want the bars to maintain a consistent height without regard to the zoom level. I realize this is something of an edge case, but want to cover as many bases as possible!
Example is here.
HTML
<div class='animation-box animate'>
<div class="animation-bar"></div>
<div class="animation-bar"></div>
<div class="animation-bar"></div>
<div class="animation-bar"></div>
</div>
CSS
.animation-box {
width: 100px;
}
.animation-bar {
animation-duration: 3s;
animation-iteration-count: infinite;
animation-name: bargraph;
animation-timing-function: linear;
background-color: #0d97c1;
height: 3px;
margin: 2px;
}
#keyframes bargraph {
0% {
width: 100%;
}
50% {
width: 10%;
}
100% {
width: 100%;
}
}
What about a simplification with only one element and less of code:
.animation-bar {
animation: bargraph 1.5s infinite linear alternate;
width: 100px;
height: 25px;
background-image: linear-gradient(#0d97c1 50%, transparent 50%);
background-position:0 0;
background-size: 100% 5px;
background-repeat: repeat-y;
}
#keyframes bargraph {
0% {
background-size: 100% 5px;
}
100% {
background-size: 10% 5px;
}
}
<div class="animation-bar"></div>
This is a problem of subpixel rendering. The issue here is that when you are zooming, the space between two bars will have to snap to a given pixel on your screen.
If you have a 3px margin # 150% zoom, the computed space is 4,5px. Meaning that the zoomed space on screen will be inconsistently rendered at 4 or 5px.
On a regular CPU computed dom, your bar will be placed at the closest value, producing those weird gaps.
What you could do to minimize the effect is applying some GPU rendered CSS (opposed to the regular CPU rendering) which is way better at rendering subpixel graphics.
One way of doing that is applying transforms.
.animation-box {
width: 55px;
margin: 0 15px 0 -5px;
}
.animation-bar {
animation: bargraph 1s infinite linear;
transform-origin: top left;
height: 3px;
background-color: #0d97c1;
margin-bottom: 3px;
}
#keyframes bargraph {
0% {
transform: scaleX(1);
}
25% {
transform: scaleX(.8);
}
50% {
transform: scaleX(.6);
}
75% {
transform: scaleX(.8);
}
100% {
transform: scaleX(1);
}
}
<div class='animation-box animate'>
<div class="animation-bar"></div>
<div class="animation-bar"></div>
<div class="animation-bar"></div>
<div class="animation-bar"></div>
</div>
I recommend an excellent article on Smashing Magazine on that topic : CSS GPU Animation: Doing It Right
Related
I was wondering why my background image is not repeating when I have background repeat set to repeat. I am using bootstrap, that is why there is a col-lg-6 there. I am a new to coding and I wanted to test my skills by making an exact copy of another website. This is the website. If you go to that, you can see exactly want I want.
.image-div {
overflow: hidden;
}
#float {
height: 100%;
width: 100%;
background:url(https://assets.maccarianagency.com/the-front/web-screens/home/home-hero-bg-dark.png);
background-repeat: repeat;
-webkit-animation: moving-img 7s infinite linear;
animation: moving-img 7s infinite linear;
}
#keyframes moving-img {
0% {
transform: translate(0px, 0px) rotate(-15deg) scale(1);
}
100% {
transform: translate(-400px, -600px) rotate(-15deg) scale(1);
}
}
<div class="col-lg-6 image-div" style="height: 80vh; width: 50vw;">
<div id="image"></div>
</div>
One way of thinking of this is that there is a continuous scroll upwards on the image div, and then we rotate that div 15%.
First therefore we need to get a continuous scroll. This can be achieved by putting both a before and after pseudo element on the image div which have the required background. These then both get animated upwards, one starting at top: 0 one starting at top: 100%. That way the scroll is continuous - the after pseudo element follows up immediately after the before one.
So far so good, but when we rotate the image div, there are gaps where the parent div shows through. So we make the before and after pseudo elements twice the size in both directions, get their background images repeated and adjust their positions so they always cover the parent div. The parent is also given overflow: hidden so we don't see the extra bits.
Here is a working snippet. Note that the choice of having the background images 30% of width is arbitrary - change it to what you want.
Note also that the website that is to be copied has a bug, though minor. Every so often you see a slight jerk in the scrolling. We have overcome that problem here by having the two sets of background animate independently so as the second one gets to the top, the first takes on opacity 0 for a split second as it repositions itself back to the top. This fools us into thinking it's all continuous. That website has also put a 'sloping' white over part of the div but that was not part of the question asked here.
.container {
}
.image-div {
position: relative;
overflow: hidden;
}
#image {
position: absolute;
transform: rotate(-15deg) translateX(-25%) translateY(-25%);
height: 200%;
width: 200%;
overflow: hidden;
}
#image::before, #image::after {
content: '';
position: absolute;
height: 100%;
width: 100%;
background-size: 30% auto;
background-image:url(https://assets.maccarianagency.com/the-front/web-screens/home/home-hero-bg-dark.png);
background-repeat: repeat repeat;
-webkit-animation: moving-img 7s infinite linear;
animation: moving-img 7s infinite linear;
z-index: 1;
}
#image::after {
top:100%;
}
#keyframes moving-img {
0% {
transform: translate(0px, 0px);
opacity: 1;
}
99.95% {
transform: translateY(-100%);
opacity: 1;
}
100% {
transform: translate(0, 0);
opacity: 0;
}
}
<div class="col-lg-6 image-div" style="height: 80vh; width: 50vw;">
<div id="image"></div>
</div>
Try below code. Let me know if you succeed.
<style>
.image-div {
overflow: hidden;
}
#float {
height: 100%;
width: 100%;
background:url(https://assets.maccarianagency.com/the-front/web-screens/home/home-hero-bg-dark.png);
background-repeat: repeat;
-webkit-animation: moving-img 7s infinite linear;
animation: moving-img 7s infinite linear;
}
#keyframes moving-img {
0% {
transform: translate(0px, 0px) rotate(-15deg) scale(1);
}
100% {
transform: translate(-400px, -600px) rotate(-15deg) scale(1);
}
}
</style>
<div class="col-lg-6 image-div" style="height: 80vh; width: 50vw;">
<div id="float"></div>
</div>
The animation is moving linearly from left to right but starts abruptly once the animation time is over. Tried with reducing to 1s and increasing the time to 15s. And even increased the keyframes but still doesn't work. Please help:
.grid-item-2 {
grid-column: 1 / span 2;
grid-row: 1;
background-image: url(Building_1.png);
margin-left: 100px;
margin-top: 53px;
width: 90%;
background-repeat: repeat-x;
animation: Buildings 2s linear infinite;
}
#keyframes Buildings {
0% { background-position: 0% 100%; }
25% { background-position: 25% 75%; }
50% { background-position: 50% 50%; }
75% { background-position: 75% 25%; }
100% { background-position: 100% 0; }
}
<div class="grid-item grid-item-2"></div>
The problem with the jumping is that you need to adapt the background image to the size of the div element, which can be hard if you want to have a responsive site. The code below is the best I can come up with, where I loop between -100% and 200%, making the image start outside the element and then end outside the other side of the element, creating the illusion that it continues. I would honestly replace repeat-x with just repeat.
I added a background color just to make the element more visible.
.grid-item-2 {
grid-column: 1 / span 2;
grid-row: 1;
background-image: url("https://picsum.photos/id/737/300/200.jpg");
/* margin-left: 100px;
margin-top: 53px; */
width: 90%;
background-repeat: repeat-x;
background-size: 50% 50%;
animation: Buildings 3s linear infinite;
background-color: #000; /* just to show case the element better */
height: 90vh;
}
#keyframes Buildings {
0% { background-position: -100% 200%; }
100% { background-position: 200% -100%; }
}
<div class="grid-item-2">
</div>
Getting rid of the intermediary keyframes would make it smoother. You only need the frames at 0% and 100%. When you set the animation timing function to linear, it will automatically make sure that all the keyframes that you defined are met. The animation should look something like this...
#keyframes Buildings {
0% {
background-position: 0% 100%;
}
100% {
background-position: 100% 0;
}
}
My goal is to create a little slideshow, and I already made it here : https://jsfiddle.net/cas1g2ch/2/
Here's the code :
HTML
<div id="first-block">
<div id="background-header">
<div class="bg-wrapper">
<div class="background-img-1"></div>
</div><!--
--><div class="bg-wrapper">
<div class="background-img-2"></div>
</div>
</div>
</div>
CSS
#first-block{
position: relative;
width: 100%;
height: 100vh;
}
#keyframes zoomPicture {
0%{
transform: scale(1.0);
}
50%{
transform: scale(1.1);
}
100%{
transform: scale(1.0);
}
}
#background-header{
position: absolute;
top : 0;
left : 0;
width: 200vw;
height: 100vh;
box-shadow: 0px 1px 1px #333;
}
#background-header > .bg-wrapper
{
width: 100vw;
height: 100vh;
display: inline-block;
float: left;
overflow: hidden;
}
#background-header > .bg-wrapper > div{
width: 100vw;
height: 100vh;
background-attachment: fixed;
animation-duration: 20s;
background-size: cover;
animation-timing-function: ease-in-out;
animation-name: zoomPicture;
animation-iteration-count: infinite;
}
#background-header .background-img-1 {
background-image: url("http://www.hd-wallpaper.images-fonds.com/modules/mg3/albums/Paysages_(landscapes)_Wallpaper_HD/Paysages/Paysage_(landscape)_wallpaper_HD_0025.jpg");
}
#background-header .background-img-2 {
background-image: url("http://unreveunvoyage.fr/wp-content/uploads/2015/04/parc-national-de-banff-paysage-canada.jpg");
}
Right now the slideshow has two pictures, and each picture covers the entire screen size (width : 100vw ; height : 100vh ; background-size : cover).
To display the pictures I used background-image, and the backgrounds are fixed.
Because I cannot animate a background with background-size : cover, I used transform : scale to add a little zoom effect.
The problems :
On Chrome there's a weird Glitch (Chrome 56 on Windows), just use the scrollbar
in the demo to see the problem, here's a video I made that shows the
problem : https://www.youtube.com/watch?v=NHCLBTpxCAs (In this video I chose a high transform: scale value to better recognize the bug)
On Firefox the backgrounds are not fixed, but the pictures are
displayed correctly
Surprisingly it works great on Internet Explorer 11
It also seems to work on mobile
How can I fix this? What is the best workaround?
I am using an img tag wrapped by a div to display a svg spinner. The div has a rotate animation on it. Do you know why its not spinning on the origin and why are the inline-spinners overlapping?
I cannot use SVG animationTransform because IE does not support it. Since its a simple spinner, I was hoping to rotate just the div and get the loading effect.
Here's the plunkr demo:
https://plnkr.co/edit/jiuI4rlETsNHN1yLbG2k?p=preview
<div class="spinner">
<img src="spinner.svg" alt="">
</div>
<div class="spinner">
<img src="spinner.svg" alt="">
</div>
<h4>Inline Spinners</h4>
<div class="spinner-inline">
<img src="spinner.svg" alt="">
</div>
<div class="spinner-inline">
<img src="spinner.svg" alt="">
</div>
/* Styles go here */
body{
background: #444;
color: white;
}
.spinner{
display: block;
height: 50px;
width: 50px;
animation: spin 2s linear infinite;
transform-origin: 50% 50%;
}
.spinner-inline{
display: inline-block;
height: 10px;
width: 10px;
animation: spin 2s linear infinite;
transform-origin: 50% 50%;
}
#keyframes spin{
0% {transform: rotate(0deg);}
100% {transform: rotate(360deg);}
}
Update:
.spinner *{
width: 100%;
height: 100%;
}
.spinner-inline *{
width: 100%;
height: 100%;
}
Adding above CSS for child elements helped a little but the inline spinners still dont spin on origin.
The reason your inline-spinners have an offset in the transform origin is because the default line-height (28px) takes over your height, so resetting that you solve the origin problem:
.spinner-inline{
display: inline-block;
height: 10px;
line-height:0;/*****/
width: 10px;
animation: spin 2s linear infinite;
transform-origin: 50% 50%;
}
https://plnkr.co/edit/mW7BamvPST89ufqNyE8N?p=preview
I looked at your plunkr, although I don't believe I can answer the origin question, the inline spinners is an easy fix.
You have the width and height set to 20px (which is only half of their actual size).
Set your spinner-inline css to:
.spinner-inline{
display: inline-block;
height: 40px;
width: 40px;
animation: spin 2s linear infinite;
transform-origin: 50% 50%;
}
And they will no longer overlap.
*Alternatively, it looks like setting this does make the spinners spin on origin as well.
I wanted to ask, what is wrong with this CSS code? It is used to animate background image - zoom effect.
#media (min-width: 1000px) {
.anim-on {
background-size: 110% 110%;
background-position: center center;
animation: shrink 12s infinite alternate;
}
.anim-out {
background-size: 120% 120%;
background-position: center center;
animation: small 6s infinite alternate;
}
#keyframes shrink {
0% {
background-size: 110% 110%;
}
100% {
background-size: 100% 100%;
}
}
#keyframes small {
0% {
background-size: 100% 100%;
}
100% {
background-size: 110% 110%;
}
}
}
This code generates nice effect, but i saw, that on slower machines, affect is bad.
What is wrong? Or maybe someone has better idea, how create this effect, in better technique?
Background size is a visual property and so any change to its value would cause repainting to occur. Painting is a very expensive operation and is bound to have an impact on the performance in low end machines. One way to overcome this would be to use CSS transform (scale to be precise) instead of background-size change to produce the animation.
Snippet which will cause performance impact:
The below snippet uses the same animation as in the question. When you run this snippet and inspect it using Chrome Dev tools (by enabling "Show Paint Rects" option), you'd see that both images have a paint rect associated with them (green or red colored box) and that as the animation is happening the box keeps blinking (or stays as-is). This indicates that a repaint is happening often and thus it impacts performance.
.anim-on,
.anim-out {
height: 200px;
width: 200px;
background: url(http://lorempixel.com/200/200/nature/1);
}
.anim-on {
background-size: 110% 110%;
background-position: center center;
animation: shrink 12s infinite alternate;
}
.anim-out {
background-size: 120% 120%;
background-position: center center;
animation: small 6s infinite alternate;
}
#keyframes shrink {
0% {
background-size: 110% 110%;
}
100% {
background-size: 100% 100%;
}
}
#keyframes small {
0% {
background-size: 100% 100%;
}
100% {
background-size: 110% 110%;
}
}
/* Just for demo */
div {
float: left;
margin-right: 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div class='anim-on'></div>
<div class='anim-out'></div>
Snippet which will cause lesser performance impact:
In the below snippet, I have added the background-image to a pseudo-element and then used scale transform on it to produce the zoom-in/out effect. The parent's overflow: hidden setting prevents the animation from affecting its size. If you inspect this with Chrome Dev tools you'd see that the green or red colored box appears only once when the page is loaded and goes away. This indicates that there is no further repaint is happening during the animation itself and hence it is better from a performance point of view. You'd also notice that this animation is more smoother than the earlier one.
.anim-on,
.anim-out {
position: relative;
height: 200px;
width: 200px;
overflow: hidden;
}
.anim-on:after,
.anim-out:after {
position: absolute;
content: '';
height: 100%;
width: 100%;
top: 0px;
left: 0px;
background: url(http://lorempixel.com/200/200/nature/1);
}
.anim-on:after {
animation: shrink 12s infinite alternate;
}
.anim-out:after {
animation: small 6s infinite alternate;
}
#keyframes shrink {
0% {
transform: scale(1.1);
}
100% {
transform: scale(1);
}
}
#keyframes small {
0% {
transform: scale(1);
}
100% {
transform: scale(1.1);
}
}
/* Just for demo */
div {
float: left;
margin-right: 20px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js"></script>
<div class='anim-on'></div>
<div class='anim-out'></div>
You can find more information about the various CSS properties and how a change to their value will impact the rendering process in the CSS Triggers website.
You can find more information about the rendering process and how using transform (as opposed to few other properties) results in a performance improvement in the below articles/sites:
HTML5 Rocks - Accelerated Rendering in Chrome
GPU Accelerated Compositing in Chrome.
Google Developers - Rendering Performance.
I edited Henry code, now css is reusable, so user can add background image to element via CMS, and css code will do the rest:
.anim {
position: relative;
height: 100vh;
width: 100%;
overflow: hidden;
**background-size: 0px!important;**
}
.anim:after {
position: absolute;
content: '';
height: 100%;
width: 100%;
top: 0px;
left: 0px;
background-size: cover !important;
**background: inherit;**
z-index: -1;
}
.anim:after {
animation: shrink 12s infinite alternate;
}
#keyframes shrink {
0% {
transform: scale(1.1);
}
100% {
transform: scale(1);
}
}
#keyframes small {
0% {
transform: scale(1);
}
100% {
transform: scale(1.1);
}
}
<section class="anim" style="background: url('images/1.png');"></section>
Thanks * :)