I'd like to know if it's possible to start a non-looping GIF from the beginning with CSS animation? For my example I have two GIFs. The first counts 1,2,3 each second - the second GIF counts 4,5,6 each second.
My goal is to show the first GIF until it counts to 3, then switch to the second GIF until it counts to 6, where it stops. My issue is that the second GIF will always be on 6 if I get it to show, at all.
My setup is as follows:
<div id="gif-two" class="gif"></div>
<div id="gif-one" class="gif"></div>
CSS
.gif {
width: 300px;
height: 300px;
position: absolute;
top: 0;
left: 50%;
margin-left: -150px;
}
#gif-one {
background: url("http://i.imgur.com/jLTVHdY.gif");
}
#gif-two {
background: url("http://i.imgur.com/O61k5Nf.gif");
}
I'm at a loss on how to handle this at the moment. I've tried toggling visibility - but have no idea how to make this work in just CSS.
Note: It is possible to achieve what you want by using CSS animations (as you will see from this answer) but I'd recommend you to combine them into the same GIF and use it that way because the approach used in this answer is too complex (so much so that I may struggle to explain it).
When the GIF is added as abackground-image, both images are loaded at pretty much same time by the browser and the GIF animation starts executing immediately. This means that change from 4-5 on the second GIF is happening at almost the same time as 1-2 on the first GIF. So, when the first GIF's loop ends, the second GIF's loop would also have ended and this is why you'd always see only 6.
The only way to avoid that from happening is to make the browser load the image after the first loop is completed. Below snippet achieves just this in a very complicated manner. The below is how:
Initially the background image is added only to the first div (because it needs to start its own loop immediately).
An animation with duration of 3s is set to the first div. This animation will hide the first div (and its background image) after around 3s, which is, the time take for its loop to complete. The opacity change from 1 to 0 starts at 95% itself to create a fade-out effect. Otherwise, at the end of loop it looks as though it blinks off and the second GIF blinks on (which doesn't look pleasing).
The second div doesn't have any background image added to it at the start because if it is added then it will load immediately and start its loop. An animation with 3s duration is added to this also.
The animation on the second div is such that it gets the background image added to it only at the end of the animation (100%). This means that the second image is loaded only after 3s (duration) and it starts its loop only from that time (by which, the loop of the first is already completed).
.gif {
width: 300px;
height: 300px;
position: absolute;
top: 0;
left: 50%;
margin-left: -150px;
}
#gif-one {
animation: img 3s linear forwards;
background: url(http://i.imgur.com/jLTVHdY.gif?3);
}
#gif-two {
animation: img2 3s linear forwards;
z-index: -1;
}
#keyframes img {
0%, 95% {
opacity: 1;
}
100% {
opacity: 0;
}
}
#keyframes img2 {
0%, 99% {
background-image: none;
}
100%{
background: url(http://i.imgur.com/O61k5Nf.gif?3);
}
}
<div id="gif-one" class="gif"></div>
<div id="gif-two" class="gif"></div>
Note: As I was writing this answer, I realized that setting the background-image within #keyframes rules does not work in IE and Firefox. Hence, the above solution is WebKit only. Also, as far as I am aware there is no other pure CSS solution possible. You would have to use JavaScript and that would help in the cache busting that CBroe is referring to also. Below is a sample demo using JS.
Using single element + JS:
window.onload = function() {
var el = document.querySelector('.gif');
el.style.backgroundImage = 'url(http://i.imgur.com/jLTVHdY.gif' + Math.random() + ')';
setTimeout(function() {
el.style.backgroundImage = 'url(http://i.imgur.com/O61k5Nf.gif' + Math.random() + ')';
}, 3250);
}
.gif {
width: 300px;
height: 300px;
position: absolute;
top: 0;
left: 50%;
margin-left: -150px;
}
<div class="gif"></div>
Using two elements + JS:
window.onload = function() {
var el1 = document.querySelector('#gif-one'),
el2 = document.querySelector('#gif-two');
el1.style.backgroundImage = 'url(http://i.imgur.com/jLTVHdY.gif' + Math.random() + ')';
setTimeout(function() {
el1.style.opacity = '0';
el2.style.backgroundImage = 'url(http://i.imgur.com/O61k5Nf.gif' + Math.random() + ')';
}, 3250);
}
.gif {
width: 300px;
height: 300px;
position: absolute;
top: 0;
left: 50%;
margin-left: -150px;
transition: opacity .05s .5s;
}
<div id="gif-one" class="gif"></div>
<div id="gif-two" class="gif"></div>
Related
I am trying to make the background color of a row change on hover with only the area of the cursor changing color/being highlighted.
I have a white background color set, and would like to have the area of the cursor highlighted with a yellow feathered circle when hovering over the background.
I can't seem to find the proper code for it, but only finding codes to change the complete background on hover.
Is this something that's possible to do in CSS?
.vc_row {
-webkit-transition:all 1s;
transition:all 1s;
}
.vc_row:hover {
background: -webkit-gradient(
radial, 500 25%, 20, 500 25%, 40, from(#faf9f4), to(#cef230)
);
}
Even through my predisposition to using JavaScript (it is where my skills lie), I believe you can't just do this in CSS, but also need JavaScript to do this. There might be a way, but I don't know it, and I am curious for someone else to answer with a magical full CSS solution and blow our minds. :D
For one approach of doing this, you need to use ::after to create the hover-element inside the row. You can then use CSS variables to pass your mouse position (gathered through JavaScript) into the hover-element, making it track the mouse position. Here is an example:
<!-- HTML -->
<div class="row">
</div>
/* CSS */
.row {
width: 300px;
height: 300px;
margin: 30px 30px;
position: relative;
overflow: hidden;
background: white;
}
.row::after {
content: "";
position: absolute;
top: calc(var(--y, 0) * 1px - 50px);
left: calc(var(--x, 0) * 1px - 50px);
width: 100px;
height: 100px;
opacity: 0;
background: radial-gradient(#cef230, #ffffff00 80%);
transition: opacity 1s;
}
.row:hover::after {
opacity: 1;
}
// JavaScript
const element = document.querySelector(".row");
element.addEventListener("mousemove", (e) => {
const { x, y } = element.getBoundingClientRect();
element.style.setProperty("--x", e.clientX - x);
element.style.setProperty("--y", e.clientY - y);
});
Key elements are the ::after to create the hover-element, the use of position: absolute; to allow for "top" and "left" attributes to position the hover-element, and applying overflow: hidden; to the row: in my testing the hover-element kept the mouse-move event firing even outside the row, unless overflow was hidden.
Need to create linear gradient animation in body but it uses cpu a lot which causes fans to run loudly.
I found this solution
animated linear gradient devouring CPU usage
this trick lowers cpu usage down to 3-5% which is great. But when you resize the window it create a bug in the background.
Tried to create resize function because I guess when you resize the window the width property changes which causes the bug. But it didn't work out.
[Codepen](https://codepen.io/iclassici/pen/poPXRyp)
In doing:
body.classList.remove('bg');
body.classList.add('bg');
actually nothing happens because the class is restored before the system has had a chance to recalculate/redraw stuff.
You'll need to remove the class as you have done, and then wait before reinstating it. Try setTimeout or requestAnimationFrame.
If you could make you code into a snippet which we can run in your question that would help us test things and give a fuller answer.
UPDATE: taking the code given in the codepen, this snippet makes a change to the resize function, removing a bganimation class, setting a short timeout and reinstating the class. This ensures that the system will reset whatever parameters it is giving the GPU.
Note: on my reasonably powerful laptop, Windows 10, the animation of the background is taking less than 2% CPU mostly and around 20% of the GPU.
window.addEventListener("resize", myFunction);
function myFunction() {
body = document.getElementsByTagName('body')[0];
body.classList.remove('bganimation');
setTimeout(function() {
body.classList.add('bganimation');
}, 100);
}
.bg {
width: 100vw;
height: 100vh;
}
.bg::before {
content: '';
position: fixed;
z-index: -2;
top: 0;
left: 0;
width: 600%;
height: 100%;
bottom: 0;
background: linear-gradient(45deg, #F17C58, #E94584, #24AADB, #27DBB1, #FFDC18, #FF3706);
background-size: 100% 100vh;
background-repeat: no-repeat no-repeat;
background-position: left top;
}
.bganimation {
animation: gradient 16s linear infinite alternate;
}
#keyframes gradient {
0% {
transform: translateX(0);
}
100% {
transform: translateX(-400vw);
/* 5/6x100% */
}
}
<body class="bg bganimation">
</body>
The following snippet should play frames 1-3 three times and (in my opinion) end on frame 3, instead it displays frame 4 after finishing, which is not animated in the 3 iterations before.
Since this works the same in Chrome, Safari & FF, I can only ask: "How can this be correct behavior?" Background offsets are: 0px, -50, -100, -0, -50, -100, -0, -50, -100, -150px
In any case it seems impossible to play an animation-sequence n-times and end on its last frame that was part of the previous 3 iterations.
Is there a way to parametrize this animation to end on the desired frame after it finishes?
Desired results can be parametrized for one iteration and infinite iterations, but nothing inbetween.
For now I assume it is impossible. This seems to be an unintended result of how W3C specified the steps() function and fill-mode.
https://github.com/w3c/csswg-drafts/issues/136
.hi {
width: 50px;
height: 72px;
background-image: url("http://s.cdpn.io/79/sprite-steps.png");
animation: play 3s steps(3) 3 forwards;
}
#keyframes play {
from { background-position: 0px; }
to { background-position: -150px; }
}
<img src="http://s.cdpn.io/79/sprite-steps.png" />
<div id="x7" class="hi"></div>
Ref: http://jsfiddle.net/simurai/CGmCe/light/
Change your css.
.hi {
width: 50px;
height: 72px;
background-image: url("http://s.cdpn.io/79/sprite-steps.png");
animation: play .8s steps(10);
}
#keyframes play {
from { background-position: 0px; }
to { background-position: -500px; }
}
This works perfectly.
So, I understand how to perform both CSS3 transitions and animations. What is not clear, and I've googled, is when to use which.
For example, if I want to make a ball bounce, it is clear that animation is the way to go. I could provide keyframes and the browser would do the intermediates frames and I'll have a nice animation going.
However, there are cases when a said effect can be achieved either way. A simple and common example would be implement the facebook style sliding drawer menu:
This effect can be achieved through transitions like so:
.sf-page {
-webkit-transition: -webkit-transform .2s ease-out;
}
.sf-page.out {
-webkit-transform: translateX(240px);
}
http://jsfiddle.net/NwEGz/
Or, through animations like so:
.sf-page {
-webkit-animation-duration: .4s;
-webkit-transition-timing-function: ease-out;
}
.sf-page.in {
-webkit-animation-name: sf-slidein;
-webkit-transform: translate3d(0, 0, 0);
}
.sf-page.out {
-webkit-animation-name: sf-slideout;
-webkit-transform: translateX(240px);
}
#-webkit-keyframes sf-slideout {
from { -webkit-transform: translate3d(0, 0, 0); }
to { -webkit-transform: translate3d(240px, 0, 0); }
}
#-webkit-keyframes sf-slidein {
from { -webkit-transform: translate3d(240px, 0, 0); }
to { -webkit-transform: translate3d(0, 0, 0); }
}
http://jsfiddle.net/4Z5Mr/
With HTML that looks like so:
<div class="sf-container">
<div class="sf-page in" id="content-container">
<button type="button">Click Me</button>
</div>
<div class="sf-drawer">
</div>
</div>
And, this accompanying jQuery script:
$("#content-container").click(function(){
$("#content-container").toggleClass("out");
// below is only required for css animation route
$("#content-container").toggleClass("in");
});
What I'd like to understand is what are the pros and cons of these approaches.
One obvious difference is that animating is taking a whole lot more code.
Animation gives better flexibility. I can have different animation for sliding out and in
Is there something that can be said about performance. Do both take advantage of h/w acceleration?
Which is more modern and the way going forward
Anything else you could add?
It looks like you've got a handle on how to do them, just not when to do them.
A transition is an animation, just one that is performed between two distinct states - i.e. a start state and an end state. Like a drawer menu, the start state could be open and the end state could be closed, or vice versa.
If you want to perform something that does not specifically involve a start state and an end state, or you need more fine-grained control over the keyframes in a transition, then you've got to use an animation.
I'll let the definitions speak for themselves (according to Merriam-Webster):
Transition: A movement, development, or evolution from one form, stage, or style to another
Animation: Endowed with life or the qualities of life; full of movement
The names appropriately fit their purposes in CSS
So, the example you gave should use transitions because it is only a change from one state to another
A shorter answer, straight on point:
Transition:
Needs a triggering element (:hover, :focus etc.)
Only 2 animation states (start and end)
Used for simpler animations (buttons, dropdown menus and so on)
Easier to create but not so many animation/effect possibilities
Animation #keyframes:
It can be used for endless animations
Can set more than 2 states
No boundaries
Both use CPU acceleration for a much smoother effect.
Animation takes a lot more code unless you're using the same transition over and over, in which case an animation would be better.
You can have different effects for sliding in and out without an animation. Just have a different transition on both the original rule and the modified rule:
.two-transitions {
transition: all 50ms linear;
}
.two-transitions:hover {
transition: all 800ms ease-out;
}
Animations are just abstractions of transitions, so if the transition is hardware accelerated, the animation will be. It makes no difference.
Both are very modern.
My rule of thumb is if I use the same transition three times, it should probably be an animation. This is easier to maintain and alter in the future. But if you are only using it once, it is more typing to make the animation and maybe not worth it.
Animations are just that - a smooth behavior of set of properties. In other words it specifies what should happen to a set of element's properties. You define an animation and describe how this set of properties should behave during the animation process.
Transitions on the other side specify how a property (or properties) should perform their change. Each change. Setting a new value for certain property, be it with JavaScript or CSS, is always a transition, but by default it is not smooth. By setting transition in the css style you define different (smooth) way to perform these changes.
It can be said that transitions define a default animation that should be performed every time the specified property has changed.
Is there something that can be said about performance. Do both take
advantage of h/w acceleration?
In modern browsers, h/w acceleration occurs for the properties filter, opacity and transform. This is for both CSS Animations and CSS Transitions.
.yourClass {
transition: all 0.5s;
color: #00f;
margin: 50px;
font-size: 20px;
cursor: pointer;
}
.yourClass:hover {
color: #f00;
}
<p class="yourClass"> Hover me </p>
CSS3 Transitions brought frontend developers a significant ability to modify the appearance and behavior of an element as relative to a change in his state. CSS3 animations extends this ability and allow to modify the appearance and behavior of an element in multiple keyframes, so transitions provides us the ability to change from one state to another, while that animations can set multiple points of transition within different keyframes.
So, let's look at this transition sample where applied a transition with 2 points, start point at left: 0 and an end point at left: 500px
.container {
background: gainsboro;
border-radius: 6px;
height: 300px;
position: relative;
}
.ball {
transition: left 2s linear;
background: green;
border-radius: 50%;
height: 50px;
position: absolute;
width: 50px;
left: 0px;
}
.container:hover .ball{
left: 500px;
}
<div class="container">
<figure class="ball"></figure>
</div>
The above can be also created via animation like so:
#keyframes slide {
0% {
left: 0;
}
100% {
left: 500px;
}
}
.container {
background: gainsboro;
border-radius: 6px;
height: 200px;
position: relative;
}
.ball {
background: green;
border-radius: 50%;
height: 50px;
position: absolute;
width: 50px;
}
.container:hover .ball {
animation: slide 2s linear;
}
<div class="container">
<figure class="ball"></figure>
</div>
And if we would like another in-between point, it would be possible to achieve only via animation, we can add another keyFrame to achieve this and this is the real power of animation over transition:
#keyframes slide {
0% {
left: 0;
}
50% {
left: 250px;
top: 100px;
}
100% {
left: 500px;
}
}
.container {
background: gainsboro;
border-radius: 6px;
height: 200px;
position: relative;
}
.ball {
background: green;
border-radius: 50%;
height: 50px;
position: absolute;
width: 50px;
}
.container:hover .ball {
animation: slide 2s linear;
}
<div class="container">
<figure class="ball"></figure>
</div>
transition can go reverse from middle of the way, but animation replay the keyframes from start to end.
const transContainer = document.querySelector(".trans");
transContainer.onclick = () => {
transContainer.classList.toggle("trans-active");
}
const animContainer = document.querySelector(".anim");
animContainer.onclick = () => {
if(animContainer.classList.contains("anim-open")){
animContainer.classList.remove("anim-open");
animContainer.classList.add("anim-close");
}else{
animContainer.classList.remove("anim-close");
animContainer.classList.add("anim-open");
}
}
*{
font: 16px sans-serif;
}
p{
width: 100%;
background-color: #ff0;
}
.sq{
width: 80px;
height: 80px;
margin: 10px;
background-color: #f00;
display: flex;
justify-content: center;
align-items: center;
}
.trans{
transition: width 3s;
}
.trans-active{
width: 200px;
}
.anim-close{
animation: closingAnimation 3s forwards;
}
.anim-open{
animation: openingAnimation 3s forwards;
}
#keyframes openingAnimation {
from{width: 80px}
to{width: 200px}
}
#keyframes closingAnimation {
from{width: 200px}
to{width: 80px}
}
<p>Try click them before reaching end of movement:</p>
<div class="sq trans">Transition</div>
<div class="sq anim">Animation</div>
in addition, if you want the javascript to listen for end of transition, you'll get one event for each property that you change.
for example transition: width 0.5s, height 0.5s. the transitionend event will trigger two times, one for width and one for height.
Just a summary, thanks to this post, there are 5 main differences between CSS transitions vs CSS animations:
1/ CSS transitions:
Animate an object from one state to another, implicitly by browser
Cannot loop
Need a trigger to run (:hover, :focus)
Simple, less code, limited powerful
Easy to work in JavaScript
2/ CSS animations:
Freely switch between multiple states, with various properties and time frame
Can loop
Donโt need any kind of external trigger
More complex, more code, more flexible
Hard to work in JavaScript due to syntax for manipulating keyframes
I believe CSS3 animation vs CSS3 transition will give you the answer you want.
Basically below are some takeaways :
If performance is a concern, then choose CSS3 transition.
If state is to be maintained after each transition, then choose CSS3 transition.
If the animation needs to be repeated, choose CSS3 animation. Because it supports animation-iteration-count.
If a complicated animation is desired. Then CSS3 animation is preferred.
Don't bother yourself which is better. My give away is that, if you can solve your problem with just one or two lines of code then just do it rather than writing bunch of codes that will result to similar behavior.
Anyway, transition is like a subset of animation. It simply means transition can solve certain problems while animation on the other hand can solve all problems.
Animation enables you to have control of each stage starting from 0% all the way to 100% which is something transition cannot really do.
Animation require you writing bunch of codes while transition uses one or two lines of code to perform the same result depending on what you are working on.
Coming from the point of JavaScript, it is best to use transition. Anything that involve just two phase i.e. start and finish use transition.
Summary, if it is stressful don't use it since both can produce similar result
I have a case where I need an element to appear for a second and then disappear, and I must not use javascript for it, so I'm trying to make it work with CSS.
Here's an example:
#-webkit-keyframes slide-one-pager {
0% { left: 0; }
50% { left: 100px; }
100% { left: 0; }
}
So in this example the property will gradually transition from 0 to 100 and back to 0. However, I need to get rid of that transition, so the property stays at 0 and gets to 100 as soon as it hits 50%. It doesn't work if I say left: 0; at 49%, because there is still a transition.
Another example, slightly more different than my original question, but if I find a solution for it it will do as well:
#-webkit-keyframes slide-one-pager {
0% { display: none; }
50% { display: block; }
75% { display: block; }
100% { display: none; }
}
Here I want to show an element for a period of time. No, using opacity is not an option, because the element is still there and is still clickable, and I need access to elements below. Unfortunately the "display" property doesn't seem to accept animating. If anyone can think of a solution how to show and hide an element with an animation (without transition!) I will be extremely grateful.
Any ideas?
You can use step-start or step-end (graphs) in your animation configuration so the curve will act like a "steps" (not curvy) so there will be no visual transition between frames, thus the animation will just "jump" between frames.
Example CSS:
animation:1s move infinite step-end;
The above example will call the move keyframes (which I didn't write because it's irrelevant), and will loop on the frames endlessly with the "step" argument which was described earlier, without a transitioned curve.
#keyframes foo{
0%{ margin-left:0 }
50%{ margin-left:50% }
}
div{
width: 50px;
height: 50px;
background: black;
border-radius: 50%;
animation:1s foo infinite;
}
input:checked + div{
animation-timing-function: step-end;
}
<label>
<input type='checkbox' checked /> Disable Animation transition
<div></div>
</label>
๐ Cool demo using this technique
I searched the same thing as you actually.
You can set a greatful parameters in animation, called animation-timing-function allowing you to set perfectly and mathematicaly the animation : With bezier curve values or, if, like me, you're not that good mathematician, a parameter call "step()".
For an example, in none shorthand writing :
.hiding {
animation-name:slide-one-pager;
animation-duration:2s;
animation-timing-function:steps(1);
}
By default, the value of this parameter is set to 0, meaning no steps.
You can read more about this interesting feature here : https://developer.mozilla.org/en-US/docs/Web/CSS/timing-function
And here a shorthand notation for your animation:
.hiding {
animation:slide-one-pager 2s steps(1);
}
For me, it works fine at least on firefox 23.0.1.
Even if I think you solved the problem since one year, maybe could help some people like me here :)
I made it using the -webkit-animation-fill-mode: forwards; property, that stops the animation at 100% without returning the element to the original state. I made up a fiddle with a working example, you can check it out here.
Although in the fiddle you can find a better example, I basically did this (Assuming absolute positioned elements):
.hiding {
-webkit-animation: slide-one-pager 2s;
-webkit-animation-fill-mode: forwards;
}
#-webkit-keyframes slide-one-pager {
0% { left: 0; }
49% { left: 0; }
50% { left: -100px; }
100% { left: -100px; }
}โ
It just jumps from 0 to -100 in the middle of the transition (49% -> 50% as you 'suggested' :P), and stays there at 100%. As said, with -webkit-animation-fill-mode: forwards; the element will stay as in 100% without going back to it's original state.
I don't know if it'll work in your scenario, but I believe there'd be an easy solution if it doesn't.
You can use this:
animation: typing 1s cubic-bezier(1,-1, 0, 2) infinite;