15

If I have a CSS keyframe animation like this

@keyframes flash-red {
  50% {
    background: #f00;
  }
}

#goflash.anm-flash {
  animation-name: flash-red;
  animation-duration: .5s;
  background: rgba(255, 0, 0, 0);
}

Then I can always trigger the animation like this:

var gf = document.querySelector("#goflash");
gf.classList.remove("anm-flash");
setTimeout(function() {
  gf.classList.add("anm-flash");
}, 50);

Is there any way to override the animation-duration/animation-timing-function to be dependent on JavaScript? I'd like to be able to say something like gf.animate("flash-red", "50%") to make the background of gf red, or gf.animate("flash-red", "75%") to make the background more like rgba(255, 0, 0, .5).

Ideally, the same technique would work for transitions. gf.transitionTo("new-class", "50%") would show the element as half way transitioned.

Obviously the flash-red is just an example—I'd like to be able to do this with any animation.

3
  • No, you can't do that. Commented Jun 20, 2013 at 18:20
  • CSS3 animations (and transitions) don't appear to be flexible enough for this. You could create (or search for) a JavaScript function or jQuery plug-in to calculate and apply the affected styles, given 2 CSS rules (one for the start state and one for the end state) and a percentage value. In some cases, it might be easier to just hard code a set of CSS rules for the intermediate states. Commented Jun 25, 2013 at 20:13
  • Edited my answer to include a working example, I think that this is what you wanted ? Commented Jun 29, 2013 at 19:03

5 Answers 5

12
+200

With the built-in animation:

Unfortunately, no

The internals of the transition isn't exposed for JavaScript so you cannot tap into it to set or get the data. And this is for a purpose - if the data were exposed it would mean reduced efficiency as the JavaScript event queue had to be updated. As JS is single-threaded and the animation goes on a separate thread you'll would soon loose the benefit of it running in compiled code internally on a separate thread.

You can however make your own transitions. This involve calculation transitions yourselves.

This is not as complicated as it sounds like as you simply use an interpolation formula for what you want to animate:

current = source + (destination - source) * fraction;

For example, for color you can use it with the color component. Lets assume we have color objects with properties r, g, b:

var color1 = {r: 100, g: 200, b: 55};  //some random color
var color2 = {r: 0,   g: 100, b: 100};

var fraction = 0.5; //0-1

Here the current RGB would be:

r = color1.r + (color2.r - color1.r) * fraction;
g = color1.g + (color2.g - color1.g) * fraction;
b = color1.b + (color2.b - color1.b) * fraction;

For positions:

 var pos1x = 100;
 var pos1y = 100;
 var pos2x = 500;
 var pos2y = 250;

var fraction = 1; //0-1

posX = pos1x + (pos2x - pos1x) * fraction;
posY = pos1y + (pos2y - pos1y) * fraction;

And so forth.

By making wrapper functions you can easily calculate these and even put them in a loop to animate them.

Example function for setting transition between color 1 and color 2. Style can be ie. backgroundColor, color etc.:

function setColor(element, style, color1, color2, fraction) {

    var r = color1.r + (color2.r - color1.r) * fraction;
    var g = color1.g + (color2.g - color1.g) * fraction;
    var b = color1.b + (color2.b - color1.b) * fraction;

    element.style[style] = 'rgb(' + (r|0) + ',' + (g|0) + ',' + (b|0) + ')';
}

(the r|0 is simply cutting off the decimal part).

And for position, for example:

var pos1 = {x: 0, y: 0};
var pos2 = {x: 200, y: 0};

function setPosition(element, pos1, pos2, fraction) {

    var x = pos1.x + (pos2.x - pos1.x) * fraction;
    var y = pos1.y + (pos2.y - pos1.y) * fraction;

    element.style.left = x + 'px';
    element.style.top = y + 'px';
}

A simple demo (use Chrome or Aurora 23 to see sliders, slider comes in next version of FF 23).

Fiddle

enter image description here

Manually set transition at any point between source and destiny, or animate them.

Sign up to request clarification or add additional context in comments.

2 Comments

Do you know any of the libraries that will allow you to specify two classes for properties? So something like getMixed(el, 'classA', 'classB', .4)? If so, that could work.
@AaronYodaiken I dunno of a library which can do this and allow setting a fraction. You can perhaps utilize f.ex. window.getComputedStyle(elem, null).getPropertyValue('width') to extract values. The challenge with this is the destination style which must belong to an element that is part of the DOM to get a computed style so it will end up being hack-ish as you need to set the dest. class, get style, and then set it back to source. Also, the style returned from source class is current so you'll need to cache the styles from initial state.
4

say you have only one animation over your element gf, you can simply control it with animation-delay and animation-play-state:

gf.__proto__.animate = function(percent) {
  this.style["animation-play-state"] = "paused";
  this.style["animation-delay"] = (
    (parseFloat(this.style["animation-duration"] || 1) * -percent) + "s"
  );
};

and you can get the computed style as following:

window.getComputedStyle(gf).background

to step through at any speed:

(function animation(time) {
  gf.animate( ((time || 0) % desireSpeed ) / desireSpeed );
  requestAnimationFrame(animation);
})();

note: this will override animation-delay from css so you'll probably want to keep it in a variable and add it as an offset in gf.__proto__.animate().

Comments

3

You can't do that as you want it.

Your only posibility is to change play-state after a given delay.

In your case, since the animation lasts 0.5 seconds, to get the animation at 50% you should set a timeout of 0.25 seconds and then set animation-play-state : paused.

Of course that won't be exactly at 50%, don't trust the precision of this method.

editing

Added demo for webkit:

fiddle

The HTML is trivial

<div id="goflash">TEST</div>
<input type="button" value="animate" onclick="animate()">

And the CSS easy

#goflash {
    width: 200px;
    height: 200px;
    left: 35px;
    top: 35px;
    position: absolute;
    background-color: red;
}
.anm-flash {
    -webkit-animation-name: flash;
    -webkit-animation-duration: 5s;
    -webkit-animation-timing-function: linear;
    -webkit-animation-iteration-count: 1;
}

@-webkit-keyframes flash {
    from {   -webkit-transform: rotate(0deg);
             background-color: red;        }
    50% {    -webkit-transform: rotate(120deg);
             background-color: yellow;}
    to {    -webkit-transform: rotate(360deg);
            background-color: red;
    }
}

And the javascript is an extension from what you supplied:

function animate () {
    var gf = document.querySelector("#goflash");
    gf.classList.remove("anm-flash");
    setTimeout(function() {
        gf.classList.add("anm-flash");
        gf.style.webkitAnimationPlayState = "running";
    }, 50);
    setTimeout(function() {
        gf.style.webkitAnimationPlayState = "paused";
    }, 2550);
}

You reset the class, after a small pause start the animation, and a calculated delay after the start, you stop it. Since the animation time was 5s,and the initial delay 50 ms, the second delay has to be (5000/2) + 50. Since you have set now the play state to paused, to de able to re-run the animation you have to set the state to running again.

Comments

1

Perhaps using CSS DOM to parse animation's intent (if that's even possible?) and then reconstructing everything in JavaScript.

But that's no mean feat!

I wonder if a CSS preprocessor would help constructing code like this. This is very much all in theory.

Comments

0

Yes, You can just overide the duration or timing of an animation. Hope I understood what you want to do:

http://jsfiddle.net/SEHyW/

var gf = document.querySelector("#goflash"),
    animationDuration = '1s'

gf.classList.remove("anm-flash");
setTimeout(function() {
  gf.classList.add("anm-flash");
  gf.style["-webkit-animation-duration"] = animationDuration;
}, 1000);

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.