I made this animation and it's working like a charm in every browser but IE and Edge. You can see the page here https://jsfiddle.net/03ddygdx/
.progress-container {
position: relative;
}
.background-progress-bar, .progress-bar {
width: 100%;
height: 10px;
top: 0px;
left: 0px;
position: absolute;
}
.background-progress-bar {
background-color: pink;
z-index: 8;
}
.progress-bar {
background-color: red;
z-index: 9;
}
.indeterminate {
animation: indeterminate 2.5s infinite linear;
}
#keyframes indeterminate {
0% {
width: 30%;
left: 0%;
}
25% {
width: 50%;
left: 50%;
}
50% {
width: 10%;
left: 0px;
}
75% {
width: 30%;
left: 0%;
}
100% {
width: 0%;
left: calc(100% - 5px);
}
}
<div class="progress-container">
<span class="background-progress-bar">
<span class="progress-bar indeterminate"></span>
</span>
</div>
In IE and Edge doesn't apply the left property, leaving the span always to the left. I've tried the -ms-animation property but that also doesn't work.
In case it matters I got this meta tag in my index.html
<meta http-equiv="X-UA-Compatible" content="IE=edge">
Edit: Ok, the problem was adding a calc() to calculate the size of the left attribute, so the bug is in there.
Edit 2: I created a bug report about this specific case, so if there is there any information about it I guess you could check it out here https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/12872907/
After hitting my head on the wall for a couple hours turns out it has to be a ie-edge bug. Changing the keyframes to this solved my problem.
#keyframes indeterminate {
0% {
width: 30%;
margin-left: 0%;
}
25% {
width: 50%;
margin-left: 50%;
}
50% {
width: 10%;
margin-left: 0px;
}
75% {
width: 30%;
margin-left: 0%;
}
100% {
width: 0%;
margin-left: 100%;
}
}
Here is the working on ie-edge example https://jsfiddle.net/vwty99s9/1/
I guess these browsers have trouble applying left values on animations, so simply change left for margin-left and you're good to go.
Related
I'm creating a scene with a bunch of scrolling layers (foreground, midground, background etc...) but annoyingly I get a flicker on Safari (14.0.3) when the animation restarts. This doesn't occur on Chrome or Firefox.
I've created a minimum reproducible example here:
https://brendon.github.io/safari_flicker/index.html
Here's the code:
.animation {
position: relative;
height: 395px;
background-image: linear-gradient(#1b9dd9, #00b6ed 44%, #ffe56c 75%);
}
.animation .scrollingAnimation {
position: absolute;
overflow: hidden;
height: 100%;
width: 100%;
}
.animation .scrollingAnimation:before {
content: "";
position: absolute;
height: 100%;
width: 200%;
}
.animation .foreground:before {
/* Dimensions: */
/* width: 1696px; */
/* height: 74px; */
min-width: 6784px;
background-image: url("https://brendon.github.io/safari_flicker/foreground.png");
background-position: left bottom -11px;
background-repeat: repeat-x;
background-size: auto 74px;
transform: translateX(-1696px);
animation: foreground 10s linear infinite;
}
#keyframes foreground {
0% {
transform: translateX(-1696px);
}
to {
transform: translateX(-3392px);
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<div class="animation">
<div class="foreground scrollingAnimation"></div>
</div>
</body>
</html>
Here is a video of the issue:
https://github.com/brendon/safari_flicker/raw/main/flicker_video.mp4
I've tried many things to get rid of the issue. It seems to sometimes go away depending on the window width, but I'm looking for a solid solution :D
The issue also exists on iOS Safari.
I should mention that I don't want to animate the background-position property as this causes performance problems and isn't accelerated by the GPU.
Have you thought about using 2 elments with the same image and animation, and offsetting - using delay - the first elements animation by -duration / 2 ?
The idea being that at all times there's one of them on screen and any render delay shouldn't be visible.
See below, I'm animating two pseudo elements.
html, body {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
.animation, .foreground {
height: 100%;
width: 100%;
background: black;
}
.foreground:before, .foreground:after {
height: 100%;
width: 200%;
position: absolute;
top: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 50vmin;
}
.foreground {
position: relative;
overflow-x: hidden;
}
.foreground:before {
content: 'A';
background: red;
animation: 10s linear -5s infinite foreground;
}
.foreground:after {
content: 'B';
background: blue;
animation: 10s linear 0s infinite foreground;
}
#keyframes foreground {
0% {
transform: translateX(100%);
}
to {
transform: translateX(-100%);
}
}
<div class="animation">
<div class="foreground scrollingAnimation"></div>
</div>
I ended up using GSAP fromTo() to manage the transition work instead of relying on the CSS animation:
<div class="foreground scrollingAnimation"><div></div></div>
gsap.fromTo(
'.foreground > div',
{ xPercent: -25 },
{ xPercent: -50, duration: 10, repeat: -1, ease: 'none' }
)
.scrollingAnimation {
position: absolute;
overflow: hidden;
height: 100%;
width: 100%;
> div {
position: absolute;
height: 100%;
}
}
.foreground {
> div {
width: calc(1696px * 4);
background: {
image: url("https://brendon.github.io/safari_flicker/foreground.png");
position: left bottom;
repeat: repeat-x;
size: auto 74px;
}
}
}
It breaks down on very wide screens, but really, if you're rocking a 6000px wide window, good luck to you sir.
The way GSAP animates is that it changes the translateX value via javascript during a requestAnimationFrame (I think) so it's nice and smooth, and the flicker problem doesn't exist in this context.
I would like to create some css animation on the home page of my site, with some notes falling.
Here is the example: http://labandallonnaise.org/joomla/(link no longer demonstrates behavior)
We can see that the notes are falling, then we have nothing before the next sequence.
Here is the code
.notes-wrapper {
overflow: hidden;
height: 630px;
top: 0px;
right: 0px;
bottom: 0px;
left: 0px;
margin-top: -75px;
margin-bottom: -75px;
margin-left: -500px;
margin-right: -500px;
}
.notes {
background: url("gantry-theme://custom/images/background.svg") center !important;
height: 6300px;
animation: fall 10s linear infinite;
overflow: hidden;
z-index: 0;
background-repeat: repeat;
background-size: cover;
top: 0px;
}
.notes img {
animation: none;
background: transparent;
}
#keyframes fall {
0% {
transform: translateY(-1050px);
}
}
<div class="notes-wrapper">
<img style="display: block; margin-left: auto; margin-right: auto; animation: none; background: transparent;" src="images/logo/BandAllonnaisedudule.png" alt="" />
<div class="notes"> </div>
</div>
how can I have continuous animation?
It depends on exactly what effect you want with various viewport aspect ratios.
Whatever the details, you need two copies of the SVG so that you don't get a gap when one has reached the bottom and starts again.
Here's one way to get continuity which puts before and after pseudo elements on the notes div both of which animate down the full height of the viewport. One starts in the viewport, the other above it.
This is a simplistic way of doing it as it doesn't require you to know anything about the aspect ratio of the background image. It would be possible to get better control and produce different results depending on what you'd like to happen on narrow or wide devices. For example, should the notes always fit in completely horizontally, however small they then go? Should there always be only one copy of the background however wide the device and so on.
.notes-wrapper {
overflow: hidden;
position: relative;
top: 0px;
right: 0px;
bottom: 0px;
left: 0px;
width: 100vw;
height: 100vh;
}
.notes {
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
}
.notes::before, .notes::after {
content: '';
background-image: url("https://ahweb.org.uk/background.svg");
position: absolute;
height: 100%;
width: 100%;
animation: fall 10s linear infinite;
overflow: hidden;
z-index: -1;
background-repeat: repeat no-repeat;
background-size: auto 100%;
background-position: center;
}
.notes::before {
top: -100%;
}
.notes::after {
top: 0;
}
#keyframes fall {
100% {
transform: translateY(100vh);
}
}
<div class="notes-wrapper">
<img style="display: block; margin-left: auto; margin-right: auto; animation: none; background: transparent;" src="images/logo/BandAllonnaisedudule.png" alt="" />
<div class="notes"></div>
</div>
I have a container of variable height, and would like to put an element at the middle of it. So I've set these styles:
#parent {
position: relative;
}
#child {
position: absolute;
top: 50%;
transform: translateY(-50%);
}
Which work in most cases. However, the container's height is not only variable, but it also changes constantly.
Because of this, that code won't work. An example:
#keyframes changeSize {
0% {
height: 100px;
}
50% {
height: 150px;
}
100% {
height: 100px;
}
}
#parent {
position: relative;
width: 400px;
height: 300px;
background: red;
animation-name: changeSize;
animation-duration: 2s;
animation-iteration-count: infinite;
animation-timing-function: ease-in-out;
}
#child {
position: absolute;
margin-block-start: 0;
margin-block-end: 0;
right: 0;
top: 50%;
transform: translateY(-50%);
}
<div id="parent">
<p id="child">I should not be moving...</p>
</div>
As you can see, it's moving. So, my question is, is there a way to place it in the middle of the element (vertically) but without having it move if the container changes size - just with CSS?
The issue is that percentage measure units are relative to the containing element. Since the #parent is changing in height through the animation, the value of a percentage unit changes. The unit change affects the percentage height property applied to the #child. The work-around might be some very complicated CSS (might not work in every situation) so the best solution is to use JavaScript to capture the initial 50% height in pixels so the unit no longer changes. It is important to also use a resize event listener to apply a new 50% height should the browser window be resized.
window.addEventListener('load',resizeDiv());
window.addEventListener('resize',resizeDiv());
function resizeDiv(){
var initialHeight = document.getElementById('parent').offsetHeight/2;
document.getElementById('child').style.top = initialHeight+'px';
}
#keyframes changeSize {
0% {
height: 100px;
}
50% {
height: 150px;
}
100% {
height: 100px;
}
}
#parent {
position: relative;
width: 400px;
height: 300px;
background: red;
animation-name: changeSize;
animation-duration: 2s;
animation-iteration-count: infinite;
animation-timing-function: ease-in-out;
}
#child {
position: absolute;
margin-block-start: 0;
margin-block-end: 0;
right: 0;
}
<div id="parent">
<p id="child">Hey!! I'm not moving anymore!</p>
</div>
I have an idea for an Ajax-loader.
This is what I have accomplished so far:
body {
background-color: lightGrey;
counter-reset: h1-counter;
}
.wrap {
max-width: 200px;
height: 50px;
margin: 50px auto;
position: relative;
overflow: hidden;
}
.wrap div {
background: linear-gradient(#0032f0, white, #0032f0);
position: absolute;
width: 100%;
height: 100%;
z-index: 1000;
opacity: .8;
}
.wrap div.dark-bar {
position: absolute;
width: 10%;
height: 100%;
animation: moveDarkBar 3s linear infinite;
z-index: 1;
}
#keyframes moveDarkBar {
from {
left: -20%;
}
to {
left: 120%;
}
}
<div class="wrap">
<div></div>
<div class="dark-bar"></div>
</div>
I want the moving indicator (.dark-bar) to be "melted" with foreground-div. Currently there is a hard line which is visually distinguishable.
Is there a way to get the moving indicator (.dark-bar) to be blurred on the left-, right edge?
You could make use of CSS filter to add blur to top layer which is animated as below,
filter - The filter property provides graphical effects like blurring,
sharpening, or color shifting an element. Filters are commonly used to
adjust the rendering of images, backgrounds, and borders.
Do include vendor prefixes for other browsers such as -webkit-,-o-,-moz-,-ms- to filter.
body {
background-color: lightGrey;
counter-reset: h1-counter;
}
.wrap {
max-width: 200px;
height: 50px;
margin: 50px auto;
position: relative;
overflow: hidden;
}
.wrap div {
background: linear-gradient(#0032f0, white, #0032f0);
position: absolute;
width: 100%;
height: 100%;
z-index: 1000;
opacity: .8;
}
.wrap div.dark-bar {
position: absolute;
width: 10%;
height: 100%;
animation: moveDarkBar 3s linear infinite;
z-index: 1;
-webkit-filter:blur(2px); /*Add this*/
}
#keyframes moveDarkBar {
from {
left: -20%;
}
to {
left: 120%;
}
}
<div class="wrap">
<div></div>
<div class="dark-bar"></div>
</div>
Try using the box-shadow property and set the vertical and horizontal axis values to 0. Something like this:
div {
box-shadow: 0 0 10px black;
}
This might be a similar effect for the one you want.
I'm scaling a div up with the transform property, but I want to keep its children (which have 1px width or height) the same size. I counter-scaled them by .5, with the expected result that an element of 1px scaled by 2, and then .5, should end up back at 1px, but they wind up a blurry 2px.
Here's the box before scaling it:
.container {
width: 200px;
height: 200px;
margin: 100px;
background-color: #EEE;
position: absolute;
}
.outline {
position: absolute;
background: #1899ef;
z-index: 999999;
opacity: 1 !important;
}
.outlineBottom, .outlineTop {
width: 100%;
height: 1px;
}
.outlineLeft, .outlineRight {
height: 100%;
width: 1px;
}
.outlineRight {
right: 0px;
}
.outlineBottom {
bottom: 0px;
}
<div class="container">
<div class="outline outlineTop"></div>
<div class="outline outlineRight"></div>
<div class="outline outlineBottom"></div>
<div class="outline outlineLeft"></div>
</div>
As you can see, the elements at the edges are a clear, dark 1px blue. Here's what the box looks like after scaling, though:
.container {
width: 200px;
height: 200px;
margin: 100px;
background-color: #EEE;
position: absolute;
transform: scale(2);
}
.outline {
position: absolute;
background: #1899ef;
z-index: 999999;
opacity: 1 !important;
transform: scale(.5);
}
.outlineBottom, .outlineTop {
width: 100%;
height: 1px;
transform: scale(1,.5);
}
.outlineLeft, .outlineRight {
height: 100%;
width: 1px;
transform: scale(.5,1);
}
.outlineRight {
right: 0px;
}
.outlineBottom {
bottom: 0px;
}
<div class="container">
<div class="outline outlineTop"></div>
<div class="outline outlineRight"></div>
<div class="outline outlineBottom"></div>
<div class="outline outlineLeft"></div>
</div>
And here's a post-scaled render from Chrome 41.0.2272.89 Mac, which is what I'm running.
Adding transform-3d(0, 0, 0) didn't appear to help. A solution was found using the zoom property, but since zoom isn't well supported I'd like to avoid that. Adding filter: blur(0px); didn't appear to have any effect either.
It was posited in chat that perhaps the children are first scaled to .5 and then doubled in size, causing them to be scaled down to .5px and then back up from there. Is there any way to ensure the order that they're rendered in causes them to first be scaled up to 2px and then halved? Against my better judgement, I tried forcing the render order with JS, but unsurprisingly, that didn't have any effect (though, interestingly, the bottom element did maintain its original color).
Failing that, are there any other solutions floating around out there? I can't be the only one who's run into this problem.
It is to do with the default transform-origin on the scaled elements. It defaults to 50% 50% for any element being transformed, but this has issues when scaling down 1px values as it has to centre the scale on a half pixel and the rendering of the elements has issues from here on out. You can see it working here with the transform-origin moved to the relevant extremes for each item.
A bit of playing about shows that this same blurring happens on scaled elements for any dimension where the scaling ends up halving a pixel.
body {
padding: 1em;
}
.container {
width: 200px;
height: 200px;
margin: 100px;
background-color: #EEE;
position: absolute;
transform: scale(2);
}
.outline {
position: absolute;
background: #1899ef;
z-index: 999999;
opacity: 1 !important;
}
.outlineBottom, .outlineTop {
width: 100%;
height: 1px;
transform: scale(1, 0.5);
}
.outlineBottom {
bottom: 0;
transform-origin: 0 100%;
}
.outlineTop {
transform-origin: 0 0;
}
.outlineLeft, .outlineRight {
height: 100%;
width: 1px;
transform: scale(.5,1);
}
.outlineRight {
right: 0px;
transform-origin: 100% 0;
}
.outlineLeft {
left: 0px;
transform-origin: 0 0;
}
<div class="container">
<div class="outline outlineTop"></div>
<div class="outline outlineRight"></div>
<div class="outline outlineBottom"></div>
<div class="outline outlineLeft"></div>
</div>