I have a piece of JS code to generate random numbers and output them as variables to be used here in place of the rotation values
#-webkit-keyframes rotate {
0% {-webkit-transform: rotate(-10deg);}
100% {-webkit-transform: rotate(10deg);}
}
#dog{
/*irrelevant settings*/
-webkit-animation: rotate 5s infinite alternate ease-in-out;
}
The code above works fine however when I try to stick the variables from the javascript into rotate(variable); I cannot get it to work. I am new to this so I'm 90% sure I just haven't got the syntax right for the variable (seriously I am terrible at remembering if something needs brackets, quotes, squigglys etc and I have tried all I can think of).
Or it might be because the variable is local to the function and CSS cannot read that.
So basically I just need some kind stranger to tell me the correct syntax and how to get CSS to read the variable if possible.
Otherwise it looks like I will need the function to create the entirety of:
#-webkit-keyframes rotate {
0% {-webkit-transform: rotate(-10deg);}
100% {-webkit-transform: rotate(10deg);}
}
...which might be a bit messy since the random variable will likely be applied to multiple css elements.
Oh and currently the variable is formatted to include the deg after the number so that is not the issue. In fact, for the sake of ease just assume I am using var dogValue = "20deg"; and forget the random element.
Thanks.
Okay, not what your actual code looks like, but you can't throw JavaScript variables into CSS, it won't recognize them.
Instead, you need to create the CSS rules through JavaScript and insert them into the CSSOM (CSS Object Model). This can be done a few ways -- you can either just create a keyframe animation and add it in, or you can overwrite an existing animation. For this sake of this question, I'm going to assume you want to continually overwrite an existing keyframe animation with different rotation values.
I've put together (and documented) a JSFiddle for you to take a look at: http://jsfiddle.net/russelluresti/RHhBz/2/
It starts off running a -10 -> 10 degree rotation animation, but then you can click the button to have it execute a rotation between random values (between -360 and 360 degrees).
This example was hacked together primarily from earlier experimentation done by Simon Hausmann. You can see the source here: http://www.gitorious.org/~simon/qtwebkit/simons-clone/blobs/ceaa977f87947290fa2d063861f90c820417827f/LayoutTests/animations/change-keyframes.html (for as long as that link works, gitorious is pretty bad at maintaining urls).
I've also taken the randomFromTo JavaScript function code from here: http://www.admixweb.com/2010/08/24/javascript-tip-get-a-random-number-between-two-integers/
I've added documentation to the parts of the Simon Hausmann script that I've taken from him (though I've slightly modified them). I've also used jQuery just to attach the click event to my button--all other script is written in native JavaScript.
I've tested this for Chrome 18 and Safari 5.1, and it seems to work fine in both browsers.
Hope this helps.
In chrome 49 onwards and Firefox 48 onwards you can leverage the new Javascript API element.animate() to push in your keyframe animations.
Please be informed that this API is experimental and doesn't work cross browser except for the aforementioned.
Dated solutions like adding class or injecting keyframes could be leveraged for backward compatibility. A shim for the same was not available immediately.
Yanked the MDN example
document.getElementById("tunnel").animate([
// keyframes
{ transform: 'translateY(0px)' },
{ transform: 'translateY(-300px)' }
], {
// timing options
duration: 1000,
iterations: Infinity
});
refer:
caniuse.com/#feat=web-animation
MDN documentation
For anyone that is looking for this answer in 2017 here's what's changed in RussellUresti answer.
In his example this won't work anymore:
keyframes.insertRule("0% { -webkit-transform: rotate(" + randomFromTo(-360,360) + "deg); }");
keyframes.insertRule("100% { -webkit-transform: rotate(" + randomFromTo(-360,360) + "deg); }");
This is due to .insertRule() being a non standard name. It is now .appendRule(), so RusselsUresti's code will now be:
keyframes.appendRule("0% { -webkit-transform: rotate(" + randomFromTo(-360,360) + "deg); }");
keyframes.appendRule("100% { -webkit-transform: rotate(" + randomFromTo(-360,360) + "deg); }");
(Simply replace insertRule with appendRule.)
More information on the CSSKeyframeRule can be found here
Related
I literally just started learning jquery and velocity at the same time for animation purposes. I was reading the velocity.js docs and trying to manipulate the "scale" of my box and ran into some trouble. Basically I have this code:
HTML:
<div id="box"></div>
CSS:
.box {
background-color:red;
height:300px;
width:300px;
transform:scale(0.3);
}
Velocity JS:
$("#box").velocity({
scale:1,
}, {
duration: 800
});
The idea is that the box would appear small(30% of its original size) at first and when the Velocity JS triggers, it scales into its normal size at 800ms. Problem is that when I'm setting the scale, it completely ignores the transform:scale(0.3) portion of the css. For example, if scale in the velocity js code was at 2, the animation starts of from 1 -> 2 in 800ms.
Any help is greatly appreciated. Thank you very much!
Bearing in mind that the velocityjs.org website docs are out of date (they refer to Velocity V1 which is no longer supported) - instead go to the Velocity Wiki and read on there.
In general the transform property is not simple to use, the browser changes the nice-to-read "scale(1)" into a "matrix(...)", and Velocity doesn't (currently) try to parse it (it's very easy to get that wrong, so we've been avoiding it).
Instead you should use ForceFeeding - this means you supply both the start and end values to Velocity, so it doesn't need to guess what you're actually meaning.
In addition the entire shortcut part of Velocity V1 has been removed because the premise itself is too broken (ie, there is no longer any scale property) - you need to use the transform property directly -
$("#box").velocity({
transform: ["scale(1)", "scale(0.3)"],
}, {
duration: 800,
});
It should be relatively clear there that the ForceFeeding is an array with a "[to, from]" layout. Potentially you can also add specific easings in between them, but check the documentation for more information.
I was wondering if it is possible to generate a webkit animation with javascript. Basically all I need is that if i click element A, element B should be animated with a random parameter every time(this is why I cant use a pre-fixed CSS). I'm testing if I can actually generate all this through javascript and Ive gotten pretty far. My code does not do anything random yet but it is really easy to make it random once I get it to work right with javasript. So right now I just want to animate element B every time I click element A. My code looks like this:
$("#elementA").live('touchstart mousedown',function() {
$("head style").remove();
var cssAnimation = document.createElement('style');
cssAnimation.type = 'text/css';
var rules = document.createTextNode('#-webkit-keyframes random_spin {'+
'from { -webkit-transform: rotate(0deg); }'+
'to { -webkit-transform: rotate(1440deg); }'+
'}');
cssAnimation.appendChild(rules);
document.getElementsByTagName("head")[0].appendChild(cssAnimation);
$("#elementB").removeClass("random_spin");
$("#elementB").css({'-webkit-animation-name': ''});
$("#elementB").css({'-webkit-animation-name': 'random_spin'});
$("#elementB").addClass("random_spin");
});
There I just added the animation to the header and I applied it to elementB.
My "random_spin" class is a CSS I already predefined :
.random_spin {
-webkit-animation-duration: 5s;
-webkit-animation-timing-function: ease;
}
My intention with this is that I should be able to make my elementB spin every time I click on elementA. Unfortunately it only does it once and no matter how much I click on it or how many times I reset the animation name it still only does it once. What am I doing wrong?
To restart a CSS3 animation you cannot just remove and add a class without putting a small delay between the commands. This allows the browsers time to actually remove the class before adding the new one. You can do this with a simple setTimeout
setTimeout(function(){
$("#elementB").addClass("random_spin");
}, 100);
More information and examples can be found below.
http://css-tricks.com/restart-css-animation/
Opening this fiddle on Webkit will show what I'm talking about.
How can I specify an element's style when it is first specified, and then its final state?
It should be possible to specify a single step animation fully this way (without having to start using #keyframes) but it seems like there is a lot of implementation specific strangeness I must deal with at this point. Note how in Firefox no animation is performed...
Seems to be the same issue as described here: CSS3 transitions to dynamically created elements
so
$("#one").on('click',function(){
var word = $("<div style='opacity: 0; height:0'>word</div>");
$('body').prepend(word);
window.getComputedStyle(word[0]).getPropertyValue("top");
word.css({height: 100, opacity: 1});
});
also works in this case: http://jsfiddle.net/wWnnH/3/
Alternatively, you can use jQuery.animate()
word.animate({height: 100, opacity: 1}, 5000);
Will work without the CSS, and on both webkit and mozilla. Although this defeats the purpose of trying to use CSS3 I guess.
I'm fighting against an oddity (I think) of the offsetWidth property.
this is the scenario:
I've got, let's say, a span tag, in my js, at a certain point I perform a css3 transform
to this element, like:
el.set('styles', {
'transform':'scale('+scale+', '+scale+')', /* As things would be in a normal world */
'-ms-transform':'scale('+scale+', '+scale+')', /* IE 9 */
'-moz-transform':'scale('+scale+', '+scale+')', /* Moz */
'-webkit-transform':'scale('+scale+', '+scale+')', /* Safari / Chrome */
'-o-transform':'scale('+scale+')' /* Oprah Winfrey */
});
w = el.getWidth();
console.log('after scaling: ' + w);
at this point the log returns me fuzzy values, like it doesn't know what to say.
any suggestion?
getBoundingClientRect() returns the correct values for me.
jsFiddle.
Transforms don't affect the intrinsic CSS properties, so you are seeing correct behavior. You need to look at the Current Transformation Matrix - as returned from getComputedStyle().webkitTransform in order to figure out how big something has been scaled.
Update: In Firefox 12 and later and Chrome/Safari - as Alex says below, you can use getBoundingClientRect() to take into account any transforms applied to an element
The scale transition starts from 50%/50% because that's the default & correct behavior. If you want it to start from the origin, then you need to set transform-origin: 0% 0%;
I take it the span tag has display:block? transforms are only supposed to work for block elements. When I goto console and load jquery (just for ease) and do this:
$('span#foo').css({"-webkit-transform": "scale(2.0, 2.0)"})
nothing happens. But if I do this:
$('span#foo').css('display','block').css({"-webkit-transform": "scale(2.0, 2.0)"})
suddenly it changes, and offsetHeight increases. Disappointingly, the offset hight goes from 16 to 19, and not to 32 (although the visual appearance is double the size and maybe it is accurate) — but at least it does appear to work as advertised.
getBoundingClientRect() didn't work for me (in Chrome 49).
This answer led me to another solution to my problem: https://stackoverflow.com/a/7894886/1699320
function getStyleProp(elem, prop){
if(window.getComputedStyle)
return window.getComputedStyle(elem, null).getPropertyValue(prop);
else if(elem.currentStyle) return elem.currentStyle[prop]; //IE
}
getStyleProp(element, 'zoom') //gives zoom factor to use in calculations
Why this example not working in IE http://jsfiddle.net/8RZVt/
I'm getting this error in IE8.
Message: Invalid argument.
Line: 156
Char: 295
Code: 0
URI: http://code.jquery.com/jquery-1.4.4.min.js
According to jQuery, this is because, as stated on the animate documentation page:
All animated properties should be a
single numeric value (except as noted
below); properties that are
non-numeric cannot be animated using
basic jQuery functionality....
So, in fact, in Firefox you are using undefined behavior. The correct thing to do would be to animate on backgroundPositionX, however Firefox does not support this.
There is, however, a jQuery plugin that does what you want:
http://plugins.jquery.com/project/backgroundPosition-Effect
Update
On closer inspection, the plugin does not support += or -= formats.
I hacked it into this example:
http://jsfiddle.net/CxqSs/ (See new example at bottom.)
Could definitely use some cleanup, and should probably be added to that plug-in, but it works in both browsers and doesn't rely on undefined behavior.
BTW, I don't know if it's worth noting, but if you leave this animation running a long time, it will eventually overflow the value and break. This could be overcome by animating the full length of the background image and then resetting the offset to 0px in the callback before the next animate. This would also avoid needing the += format.
Also,
It should be noted that speed: 1, step: 1 and speed: 50, step: 50 are equivalent.
The reason they look different speeds is because
There is more overhead in a speed of 1 (which is really a millisecond duration) because animate gets called more often.
The default easing is "swing", meaning that the animation speeds up and slows down slightly throughout it's course, meaning that the overall speed is affected a bit. You should change the easing to "linear" for your scrolling case:
var animate = function() {
element.animate({
...
}, speed, "linear", animate);
};
This means that you could use the backgroundPosition-Effect plugin, without the '+=', by setting your step to 2247 (the width of the image), like I stated above.
And that finally brings us to... wait for it...
http://jsfiddle.net/zyQj3/20/
Cross-platform, non-kludgy, non-overflowing, correctly easing, extra parameter-lacking, solution.
The script fails at this point because you are passing an invalid CSS value:
element.animate({
backgroundPosition: animStep + " 0px" /* evaluates to "+=50px 0px" */
}, speed, animate);
OK here we go again :D
http://jsfiddle.net/c7rKV/1/
Again identical to original however again just animating backgroundPositionX when in IE.
Apologies on not actually looking at FF/Chrome last time.
Additionally: this of course is not very graceful and Adam Prax is absolutely correct on what the problem is. I just wanted to post a solution to it.
If you check the source code of jQuery, you will see it uses this regexp to parse the parameter (which in your case is +=50px 0px). So it will see it as += (increase) 50 (to fifty) px 0px (unit, append after the number). When trying to read the current value, jQuery uses parseFloat, which just grabs the number at the start of the string. So it works perfectly, even if a multi-dimensional property is probably not what the jQuery programmers had in mind.
Except that IE8 does not support getting the current value of background-position. There is background-position-x and background-position-y but no background-position. Duh. So your best bet is checking the browser type, and animating either background-position or background-position-x depending on that: http://jsfiddle.net/22UWW/
(There is actually a jQuery bug report about this, with a more elegant solution.)