timing

CSS animations and transitions are nice! But gradients are only animatable in Edge (and IE 10+). Sure, we will do all sorts of tricks with background-position, background-size, background-blend-mode and even opacity and rework on a pseudo-element, however typically these are simply not sufficient. Point out that we run into comparable issues when eager to animate SVG attributes with out a CSS correspondent.

Utilizing a number of examples, we clarify methods to easily go from one state to a different in a similar way utilizing just a bit little bit of JavaScript, with out having to depend on a library, so with out pointless code which will change into an enormous burden sooner or later.

This isn’t how the CSS timing capabilities work. That is an method that I discover less complicated and extra intuitive than working with curves. I will present methods to experiment with completely different timing capabilities utilizing JavaScript and dissect use instances. It isn’t a tutorial on methods to do lovely animation.

Let’s begin with a left to proper linear-gradient() with a pointy transition the place we need to animate the primary cease. Here is a method to categorical that utilizing CSS customized properties:

background: linear-gradient(90deg, #ff9800 var(--stop, zero%), #3c3c3c zero);

On click on, we would like the worth of this cease to go from zero% to 100% (or the opposite approach round, relying on the state it is already in) over the course of NF frames. If an animation is already working on the time of the clicking, we cease it, change its course, then restart it.

We additionally want a number of variables such because the request ID (this will get returned by requestAnimationFrame), the index of the present body (an integer within the [0, NF] interval, beginning at zero) and the course our transition goes in (which is 1 when going in the direction of 100% and -1 when going in the direction of zero%).

Whereas nothing is altering, the request ID is null. We additionally set the present body index to zero initially and the course to -1, as if we have simply arrived to zero% from 100%.

const NF = 80; // variety of frames transition occurs over

let rID = null, f = zero, dir = -1;

perform stopAni() 
  cancelAnimationFrame(rID);
  rID = null;  
" class="synonym">;

perform replace() " class="synonym">;

addEventListener('click on', e => 
  if(rID) stopAni(); // if an animation is already working, cease it
  dir *= -1; // change animation course
  replace();
, false);

Now all that is left is to populate the replace() perform. Inside it, we replace the present body index f. Then we compute a progress variable ok because the ratio between this present body index f and the overall variety of frames NF. On condition that f goes from zero to NF (included), which means that our progress ok goes from zero to 1. Multiply this with 100% and we get the specified cease.

After this, we examine whether or not we have reached one of many finish states. If now we have, we cease the animation and exit the replace() perform.

perform replace() {
  f += dir; // replace present body index
  
  let ok = f/NF; // compute progress
  
  doc.physique.model.setProperty('--stop', `$+(ok*100).toFixed(2)%`);
  
  if(!(f%NF)) 
    stopAni();
    return
  " class="synonym">
  
  rID = requestAnimationFrame(replace)
};

The consequence could be seen within the Pen beneath (notice that we return on a second click on):

The way in which the pseudo-element is made to distinction with the background beneath is defined in an older article.

The above demo might seem like one thing we may simply obtain with a component and translating a pseudo-element that may totally cowl it, however issues get much more fascinating if we give the background-size a worth that is smaller than 100% alongside the x axis, as an example 5em:

This offers us a type of a “vertical blinds” impact that can’t be replicated in a clear method with simply CSS if we do not need to use multiple aspect.

An alternative choice could be to not alternate the course and at all times sweep from left to proper, besides solely odd sweeps could be orange. This requires tweaking the CSS a bit:

--c0: #ff9800;
--c1: #3c3c3c;
background: linear-gradient(90deg, 
    var(--gczero, var(--czero)) var(--stop, zero%), 
    var(--gc1, var(--c1)) zero)

Within the JavaScript, we ditch the course variable and add a sort one (typ) that switches between zero and 1 on the finish of each transition. That is after we additionally replace all customized properties:

const S = doc.physique.model;

let typ = zero;

perform replace() {
  let ok = ++f/NF;
  
  S.setProperty('--stop', `$+(ok*100).toFixed(2)%`);
  
  if(!(f%NF)) 
    f = zero;
    S.setProperty('--gc1', `var(--c$typ" class="synonym">)`);
    typ = 1 - typ;
    S.setProperty('--gc0', `var(--c$typ" class="synonym">)`);
    S.setProperty('--stop', `zero%`);
    stopAni();
    return
  
  
  rID = requestAnimationFrame(replace)
};

This offers us the specified consequence (click on no less than twice to see how the impact differs from that within the first demo):

We may additionally change the gradient angle as an alternative of the cease. On this case, the background rule turns into:

background: linear-gradient(var(--angle, zerodeg), 
    #ff9800 50%, #3c3c3c zero);

Within the JavaScript code, we tweak the replace() perform:

perform replace() {
  f += dir;
	
  let ok = f/NF;
  
  doc.physique.model.setProperty(
    '--angle', 
    `$deg`
  );
  
  if(!(f%NF)) 
    stopAni();
    return
  " class="synonym">
  
  rID = requestAnimationFrame(replace)
};

We now have a gradient angle transition in between the 2 states (0deg and 180deg):

On this case, we would additionally need to preserve going clockwise to get again to the 0deg state as an alternative of adjusting the course. So we simply ditch the dir variable altogether, discard any clicks taking place in the course of the transition, and at all times increment the body index f, resetting it to zero after we’ve accomplished a full rotation across the circle:

perform replace() {
  let ok = ++f/NF;
  
  doc.physique.model.setProperty(
    '--angle', 
    `$deg`
  );
  
  if(!(f%NF)) 
    f = f%(2*NF);
    stopAni();
    return
  " class="synonym">
  
  rID = requestAnimationFrame(replace)
};

addEventListener('click on', e => , false);

The next Pen illustrates the consequence – our rotation is now at all times clockwise:

One thing else we may do is use a radial-gradient() and animate the radial cease:

background: radial-gradient(circle, 
    #ff9800 var(--stop, zero%), #3c3c3c zero);

The JavaScript code is similar to that of the primary demo and the consequence could be seen beneath:

We might also not need to return when clicking once more, however as an alternative make one other blob develop and canopy your entire viewport. On this case, we add a number of extra customized properties to the CSS:

--c0: #ff9800;
--c1: #3c3c3c;
background: radial-gradient(circle, 
    var(--gczero, var(--czero)) var(--stop, zero%), 
    var(--gc1, var(--c1)) zero)

The JavaScript is similar as within the case of the third linear-gradient() demo. This offers us the consequence we would like:

A enjoyable tweak to this may be to make our circle begin rising from the purpose we clicked. To take action, we introduce two extra customized properties, --x and --y:

background: radial-gradient(circle at var(--x, 50%) var(--y, 50%), 
    var(--gczero, var(--czero)) var(--stop, zero%), 
    var(--gc1, var(--c1)) zero)

When clicking, we set these to the coordinates of the purpose the place the clicking occurred:

addEventListener('click on', e => {
  if(!rID) 
    S.setProperty('--x', `$e.clientX" class="synonym">px`);
    S.setProperty('--y', `$e.clientY" class="synonym">px`);
    replace();
  
}, false);

This offers us the next consequence the place now we have a disc rising from the purpose the place we clicked:

An alternative choice could be utilizing a conic-gradient() and animating the angular cease:

background: conic-gradient(#ff9800 var(--stop, zero%), #3c3c3c zero%)

Notice that within the case of conic-gradient(), we should use a unit for the zero worth (whether or not that unit is % or an angular one like deg does not matter), in any other case our code will not work – writing conic-gradient(#ff9800 var(--stop, zero%), #3c3c3c zero) means nothing will get displayed.

The JavaScript is similar as for animating the cease within the linear or radial case, however keep in mind that this at present solely works in Chrome with Experimental Internet Platform Options enabled in chrome://flags.

Leave a Reply

Your email address will not be published. Required fields are marked *