I would like to use webkit animation with #-webkit-keyframes but being able to dynamically modify the values on the rule, so that the animation is not static.
All the samples I found use a static #-webkit-frames, is there a way to customize with Javascript?
I had to create a new style rule in the loaded style sheets. Seems to work great in chrome 5.0.342.9 beta (at least)
var lastSheet = document.styleSheets[document.styleSheets.length - 1];
lastSheet.insertRule("#-webkit-keyframes " + newName + " { from { top: 0px; } to {top: " + newHeight + "px;} }", lastSheet.cssRules.length);
and then assign the animation name using element.style
element.style.webkitAnimationName = newName;
I wish I could credit for this, but here's a link to someone who managed to modify an existing animation, as opposed to creating a new animation.
http://gitorious.org/webkit/webkit/blobs/438fd0b118bd9c2c82b6ab23956447be9c24f136/LayoutTests/animations/change-keyframes.html
I've ran this to verify that it does, indeed, work.
EDIT
So that link is dead and I don't trust Gitorious to maintain URLS anymore so here's a link to a JSFiddle I created to answer a similar question: http://jsfiddle.net/russelluresti/RHhBz/3/
This contains script to find an existing animation, update its values, and assign it to an element to make the animation occur. I have tested this in Chrome 18 and Safari 5.1
Related
I'm having some major headache trying to apply CSS3 transitions to a slideshow trough JavaScript.
Basically the JavaScript gets all of the slides in the slideshow and applies CSS classes to the correct elements to give a nice animated effect, if there is no CSS3 transitions support it will just apply the styles without a transition.
Now, my 'little' problem. All works as expected, all slides get the correct styles, the code runs without bugs (so far). But the specified transitions do not work, even though the correct styles where applied. Also, styles and transitions work when I apply them myself trough the inspector.
Since I couldn't find a logical explanation myself I thought someone here could answer it, pretty please?
I've put together a little example of what the code is right now: http://g2f.nl/38rvma
Or use JSfiddle (no images): http://jsfiddle.net/5RgGV/1/
To make transition work, three things have to happen.
the element has to have the property explicitly defined, in this case: opacity: 0;
the element must have the transition defined: transition: opacity 2s;
the new property must be set: opacity: 1
If you are assigning 1 and 2 dynamically, like you are in your example, there needs to be a delay before 3 so the browser can process the request. The reason it works when you are debugging it is that you are creating this delay by stepping through it, giving the browser time to process. Give a delay to assigning .target-fadein:
window.setTimeout(function() {
slides[targetIndex].className += " target-fadein";
}, 100);
Or put .target-fadein-begin into your HTML directly so it's parsed on load and will be ready for the transition.
Adding transition to an element is not what triggers the animation, changing the property does.
// Works
document.getElementById('fade1').className += ' fade-in'
// Doesn't work
document.getElementById('fade2').className = 'fadeable'
document.getElementById('fade2').className += ' fade-in'
// Works
document.getElementById('fade3').className = 'fadeable'
window.setTimeout(function() {
document.getElementById('fade3').className += ' fade-in'
}, 50)
.fadeable {
opacity: 0;
}
.fade-in {
opacity: 1;
transition: opacity 2s;
}
<div id="fade1" class="fadeable">fade 1 - works</div>
<div id="fade2">fade 2 - doesn't work</div>
<div id="fade3">fade 3 - works</div>
Trick the layout engine!
function finalizeAndCleanUp (event) {
if (event.propertyName == 'opacity') {
this.style.opacity = '0'
this.removeEventListener('transitionend', finalizeAndCleanUp)
}
}
element.style.transition = 'opacity 1s'
element.style.opacity = '0'
element.addEventListener('transitionend', finalizeAndCleanUp)
// next line's important but there's no need to store the value
element.offsetHeight
element.style.opacity = '1'
As already mentioned, transitions work by interpolating from state A to state B. If your script makes changes in the same function, layout engine cannot separate where state A ends and B begins. Unless you give it a hint.
Since there is no official way to make the hint, you must rely on side effects of some functions. In this case .offsetHeight getter which implicitly makes the layout engine to stop, evaluate and calculate all properties that are set, and return a value. Typically, this should be avoided for performance implications, but in our case this is exactly what's needed: state consolidation.
Cleanup code added for completeness.
Some people have asked about why there is a delay. The standard wants to allow multiple transitions, known as a style change event, to happen at once (such as an element fading in at the same time it rotates into view). Unfortunately it does not define an explicit way to group which transitions you want to occur at the same time. Instead it lets the browsers arbitrarily choose which transitions occur at the same time by how far apart they are called. Most browsers seem to use their refresh rate to define this time.
Here is the standard if you want more details:
http://dev.w3.org/csswg/css-transitions/#starting
Edit:
For whatever reason, this does not work with JavaScript. It won't let you set the background image multiple times. To fix this, I set the backgroundPosition property to calc(50% + translation_x) calc(50% + translation_y) instead of setting a transformation in the image. This seems to work (for now).
I am unable to set the "background-image" property of an element dynamically with JavaScript. I have had no issues doing so in the past, but my current script does not work at all. There are no error messages in the console, so I haven't the slightest idea what's happening.
I have created vector graphics for a game and I am attempting to move them across the screen using a group (<g transform = "translate(x, y)">). My script takes the current background of the element, background.style.backgroundImage, and replaces the line containing the group with the transformed version.
Now, if I run
background.style.backgroundImage = "url(\"" + transformed_background + "\")";
console.log(background.style.backgroundImage);
the old version of the background image (without transformations) is (predictably) displayed in the output and the image does not update. For instance, the output might be
… <svg xmlns=\'http://www.w3.org/2000/svg\' width=\'5000\' height=\'5000\'><g> …
while the output of
console.log(transformed_background);
is correct, and is something to the effect of
… <svg xmlns=\"http://www.w3.org/2000/svg\" width=\"5000\" height=\"5000\"><g transform="translate(1327.0717423133253, 1819.0229885057504)"> …
The background of the element will not change, despite my best efforts. I have tried everything imaginable in an attempt to resolve this issue. I believed it might be caused by the loop it is in (with an interval of about 50 ms), but increasing the interval had no effect. This system works elsewhere in the script (where the background is set initially), and this segment uses the same method.
The script is available here, and the issue is on line 175.
Any information would be much appreciated.
Thanks!
The only thing I see is at line 177,
where you use the var from line 175.
You wrote there
document.getElementById("background").backgroundImage = "url(\"" + build_background + ")";
But if I am not wrong you change the background with js like that.
document.getElementById("background").style.backgroundImage = "url(\"" + build_background + ")";
So you forgot the style.
Btw why do you write the js in the html window and not in the window for js?
I want to change the background color of in-viewport elements (using overflow: scroll)
So here was my first attempt:
http://jsfiddle.net/2YeZG/
As you see, there is a brief flicker of the previous color before the new color is painted. Others have had similar problems.
Following the HTML5 rocks instructions, I tried to introduce requestAnimationFrame to fix this problem to no avail:
http://jsfiddle.net/RETbF/
What am I doing wrong here?
Here is a simpler example showing the same problem: http://jsfiddle.net/HJ9ng/
Filed bug with Chromium here: http://code.google.com/p/chromium/issues/detail?id=151880
if it is only the background color, well why don't you just change the parent background color to red and once it scroll just change it to pink?
I change your CSS to that
#dad
{
overflow-y: scroll;
overflow-x: hidden;
width: 100px;
height: 600px;
background-color:red;
}
I remove some of you Jquery and change it to this
dad.bind('scroll', function() {
dad.css('background-color', 'pink');
});
And I remove this line
iChild.css('backgroundColor', 'red');
But is the Red color it is important that won't work for sure http://jsfiddle.net/2YeZG/5/
I like Manuel's Solution.
But even though I don't get what you're exactly trying to do, I want to point out a few things.
In your fiddle code, I saw that you included Paul Irish's Shim for requestAnimationFrame.
But you never use it.
(It's basically a reliable setTimeOut, nothing else) it's from frame based animations.)
So since you just want to change some CSS properties, I don't see why you would need it. Even if you want transitions, you should rely on CSS transitions.
Other than that your code could look something like
dad.bind('scroll', function() {
dad.css('background-color', 'pink');
eachElemNameHere.css('background-color','randomColor');
});
Also you should ideally not use something like that if you can help it. You should just add and remove class names and add all these properties in your CSS. Makes it work faster.
Also, again I don't quite get it, but you could use the jQuery function to find out each elements' position from the top to have better control.
Your problem seems to be that you only change the background color of the elements which have already been scrolled into view. Your code expects that the browser waits for your code to handle the scroll event before the browser redraws its view. This is most probably not a guarantee given by the HTML spec. That's why it flickers.
What you should do instead is to change the elements which are going to be scrolled into view. This is related to off screen rendering or double buffering as it is called in computer games programming. You build your scene off screen and copy the finished scene to the visible frame buffer.
I modified your first JSFiddle to include a multiplier for the height of the scroll area: http://jsfiddle.net/2YeZG/13/.
dad.bind('scroll', function() {
// new: query multiplier from input field (for demonstration only) and print message
var multiplier = +($("#multiplier")[0].value);
$("#message")[0].innerHTML=(multiplier*100)-100 + "% of screen rendering";
// your original code
var newScrollY = newScrollY = dad.scrollTop();
var isForward = newScrollY > oldScrollY;
var minVal = bSearch(bots, newScrollY, true);
// new: expand covered height by the given multiplier
// multiplier = 1 is similar to your code
// multiplier = 2 would be complete off screen rendering
var newScrollYHt = newScrollY + multiplier * dadHeight;
// your original code (continued)
var maxVal;
for (maxVal = minVal; maxVal < botsLen; maxVal++) {
var nxtTopSide = tops[maxVal];
if (nxtTopSide >= newScrollYHt) {
break;
}
}
maxVal = Math.min(maxVal, botsLen);
$(dadKids.slice(minVal, maxVal)).css('background', 'pink');
});
Your code had a multiplier of 1, meaning that you update the elements which are currently visible (100% of scroll area height). If you set the multiplier to 2, you get complete off screen updates for all your elements. The browser updates enough elements to the new background color so that even a 100% scroll would show updated elements. Since the browser seldom scrolls 100% of the area in one step (depends of the operating system and the scroll method!), it may be sufficient to reduce the multiplier to e.g. 1.5 (meaning 50% off screen rendering). On my machine (Google Chrome, Mac OS X with touch pad) I cannot produce any flicker if the multiplier is 1.7 or above.
BTW: If you do something more complicated than just changing the background color, you should not do it again and again. Instead you should check whether the element has already been updated and perform the change only afterwards.
I understand that CSS3 animations do not work in IE. I was just wondering if there is a JavaScript workaround for this problem.
Here's a link to what I want to recreate in IE: http://animation.kashoo.co.uk/
Any advice would be great.
After a quick Google search I found a jQuery plugin that changes jQuery's standard $.animate() function so that it will use CSS3 transitions whenever possible:
$.animate-enhanced
edit:
After trying the above plugin on a site of mine, the site broke. I'm not sure if you will have the same problem or not, but here is my workaround:
You will need Modernizr.js
Basically, you check (with Modernizr) whether the browser supports a given feature, and then decide whether to animate with CSS3 or Javascript.
For example:
(Let's say you are animation an object to move to the right by 200px)
if(Modernizr.csstransitions) {
// use your appropriate browser prefixes
yourDomObject.style.transition = 'left 2s';
yourDomObject.style.left = parseInt(yourDomObject.style.left) + 200 + 'px'
} else {
var left = parseInt($(yourDomObject).css('left')) + 200 + 'px';
$(yourDomObject).animate({
'left' : left
},2000,'easeOutExpo');
}
Check out jQuery's animate functions:
http://api.jquery.com/animate/
There are many JQuery plugins that provide animations. Here's one that has a flip effect similar to the one you are looking for. http://lab.smashup.it/flip/
I wrote this code that gives for each class .hpCarousel the relating background image.
The image names being: 0bg.jpg, 1jpg.bg, 2bg.jpg, etc...
for (i=0; i < 8; i++) {
$('.hpCarousel:eq('+i+')').css('background-image', 'url(wp-content/themes/blankslate/assets/carousel/'+i+'bg.jpg');
}
It works fine in Firefox. The classes have a style with their correct background-image assigned.
It does not work in Chrome OSX&WIN /Safari OSX/ IE. The .hpCarousel class divs have no style.
I thought at first it was something to do with Chrome's background refresh bug. But finding it on other browsers has made me think otherwise.
Am I clearly doing something wrong?
These classes are hidden on load. Does that make any difference? Then they fade in and out after one another to produce a carousel
Do you have errors in FireBug?
You could use another (more general) selector in the loop:
$('.hpCarousel:nth-child(' + i + ')')
Also the $.each iterator is a more convenient way to iterate through your backgrounds.
$('.hpCarousel').each(function(index) {
$(this).css('background-image', 'url(wp-content/themes/blankslate/assets/carousel/'+index+'bg.jpg');
});