I'm dissatisfied with existing parallax libraries, so I'm trying to write my own. My current one consists of three main classes:
ScrollDetector
tracks an element's scroll position relative to the screen; it has functions to return a float representing its current position:0
represents the top edge of the element being at the bottom edge of the viewport1
represents the bottom edge of the element being at the top edge of the viewport- All other positions are interpolated/extrapolated linearly.
ScrollAnimation
uses aScrollDetector
instance to interpolate arbitrary CSS values on another element, based on theScrollDetector
element.ParallaxativeAnimation
extendsScrollAnimation
for the special case of a background image that should scroll at a precise factor of the window scroll speed.
My current situation is this:
ScrollAnimation
s usingtransform: translateY(x)
work smoothly.ParallaxativeAnimation
s usingtranslateY(x)
work, but animate jerkily.ParallaxativeAnimation
s usingtranslate3d(0, x, 0)
are jerky, but not as badly.- The Rellax library's animations, which use
translate3d(0, x, 0)
, work perfectly smoothly.
You can see the comparison on this pen. (The jerkiness shows up best in Firefox.) My library is on Bitbucket.
I don't know where the problem in my library lies and I don't know how to figure it out. Here is an abridged paste of where the heavy lifting is done while scrolling in the ScrollAnimation
class that works smoothly:
getCSSValue(set, scrollPosition) { return set.valueFormat.replace(set.substitutionString, ((set.endValue - set.startValue) * scrollPosition + set.startValue).toString() + set.unit) } updateCSS() { var cssValues = []; var scrollPosition = this.scrollDetector.clampedRelativeScrollPosition(); var length = this.valueSets.length; for(var i = 0; i < length; i++) { cssValues.push(getCSSValue(valueSets[i], scrollPosition) ); } this.setCSS(cssValues); this.ticking = false; } requestUpdate() { if(!this.ticking) { requestAnimationFrame(() => { this.updateCSS(); }); } this.ticking = true; }
And here's the equivalent in the ParallaxativeAnimation
class that is jerky:
updateCSS() { var scrollPosition = this.scrollDetector.clampedRelativeScrollPosition(); var cssValues = []; var length = this.valueSets.length; for(var i = 0; i < length; i++) { var scrollTranslate = -((this.scrollTargetSize - this.valueSets[i].parallaxSize) * scrollPosition); cssValues.push( this.valueSets[i].valueFormat.replace(this.valueSets[i].substitutionString, scrollTranslate.toString() + 'px') ); } this.setCSS(cssValues); this.ticking = false; } requestUpdate() { if(!this.ticking) { requestAnimationFrame(() => { this.updateCSS(); }); } this.ticking = true; }
The math doesn't seem any more complicated, so I can't figure how that's affecting animation performance. I thought the difference might have been my styling on the parallax image, but in the pen above, the Rellax version has the exact same CSS on it, but animates perfectly smoothly. Rellax seems to maybe be doing more complicated math on each frame:
var updatePosition = function(percentage, speed) { var value = (speed * (100 * (1 - percentage))); return self.options.round ? Math.round(value) : Math.round(value * 100) / 100; }; // var update = function() { if (setPosition() && pause === false) { animate(); } // loop again loop(update); }; // Transform3d on parallax element var animate = function() { for (var i = 0; i < self.elems.length; i++){ var percentage = ((posY - blocks[i].top + screenY) / (blocks[i].height + screenY)); // Subtracting initialize value, so element stays in same spot as HTML var position = updatePosition(percentage, blocks[i].speed) - blocks[i].base; var zindex = blocks[i].zindex; // Move that element // (Set the new translation and append initial inline transforms.) var translate = 'translate3d(0,' + position + 'px,' + zindex + 'px) ' + blocks[i].transform; self.elems[i].style[transformProp] = translate; } self.options.callback(position); };
The only thing I can really tell from Chrome Developer Tools is that the framerate isn't dipping too far below 60 fps, so maybe it's not that I'm doing too much work each frame, but that I'm doing something mathematically incorrect when I calculate the position?
So I don't know. I'm clearly in way over my head here. I'm sorry to throw a whole library at StackOverflow and say "FIX IT", but if anyone can tell what I'm doing wrong, or tell me how to use Developer Tools to maybe figure out what I'm doing wrong, I'd appreciate it very much.
EDIT
Okay, I've figured out that the most important factor in the jitteriness of the scrolling is the height of the element being translated. I had a miscalculation in my library that was causing the background images to be much taller than they needed to be when my scrollPixelsPerParallaxPixel
property was high. I'm in the process of trying to correct that now.
2 Answers
Answers 1
Aside from the calculations, you could try running it asynchroneously by using Promise:
await Promise.all([ loop(update); ]);
just to see if it has a positive impact on the performance.
I'd comment, but I don't have enough reputation yet.
Answers 2
Anything touching the DOM will be slow. CSS animations are fine, but if you update the CSS you are touching the DOM and it will be slow. Consider using a canvas element instead!
0 comments:
Post a Comment