Reputation: 83755
So here is my animation function:
function animation(el, isHorizontal, from, to, callback) {
var start = true === isHorizontal ? from.x : from.y,
stop = true === isHorizontal ? to.x : to.y,
intervalId, diff;
intervalId = setInterval(function () {
diff = Math.abs(stop - start);
if (stop < start) {
if (diff < 4) {
start -= diff;
} else {
start -= 4;
}
} else {
if (diff < 4) {
start += diff;
} else {
start += 4;
}
}
if (true === isHorizontal) {
el.style.left = start + 'px';
} else {
el.style.top = start + 'px';
}
if (start === stop) {
clearInterval(intervalId);
callback();
}
}, 1);
}
It works perfectly in Chrome, although in Safari, the animated elements seem to stop few pixels before they are supposed to.
Let's say I am going to move from left: 8 to left: 116. In Safari, the element ends at around left: 108, when I open the Inspector to see the console and check that the left offset is correct on the element, the element repositions itself correctly.
All I need to do is open the developer console and the element jumps to correct position.
So strange.
Here's some of my CSS that might be relevant:
.box {
position: relative;
}
.item {
position: absolute;
background-position: left top;
background-repeat: no-repeat;
background-size: 100% auto;
-webkit-transform: transform;
}
Upvotes: 1
Views: 860
Reputation: 83755
So it seems in Safari I have to trigger hardware acceleration otherwise Safari can't keep up and repaint images fast enough:
-webkit-transform-style: preserve-3d;
.item {
position: absolute;
background-position: left top;
background-repeat: no-repeat;
background-size: 100% auto;
-webkit-transform-style: preserve-3d;
}
Upvotes: 0
Reputation: 5781
It looks like there's nothing wrong with your code, but that Safari doesn't perform a repaint when it should (since the problem goes away when you open the developer console).
Do you still get the problem if you increase the 1ms interval to say 50ms?
One hack that usually works is to force the browser to repaint the whole page by toggling a dummy class on the body at the end of the animation:
if (start === stop) {
jQuery("body").toggleClass("forceRepaint");
clearInterval(intervalId);
callback();
}
You could do this without jQuery of course, but it's going to be less concise.
Also, you would most likely avoid this problem if you used requestAnimationFrame() instead of setInterval() for animations like this (as well as better performance). There's several polyfills that fall back to setInterval/setTimeout in old browsers.
Upvotes: 1