Saturday, September 29, 2018

Why will changing this CSS effect using JS only work smoothly for certain values?

Leave a Comment

I am trying to move an image slowly relative to the viewport when the user scrolls the page. Similar to the effects found here https://ihatetomatoes.net/demos/parallax-scroll-effect-part-2/

If the image is moved by a small value then it moves smoothly. If it is moved by a larger amount then it becomes very janky.

var imageOffset = lastScrollY * 0.9; $image.css({top: `${imageOffset}px`});     //Runs badly  var imageOffset = lastScrollY * 0.3; $image.css({top: `${imageOffset}px`});     //Runs well 

Why does the value affect the performance so much?

I have tried all the different CSS styles (transform, top, bottom, background-position). Dev tools says that I am well in the time limit for 60fps. This happens if there is nothing but the image on the page and on multiple browsers and devices. This is also not just for images but for text or anything else as well.

Bad Version: https://jsfiddle.net/4vcg8mpk/58/

Good Version: https://jsfiddle.net/4vcg8mpk/59/

Problem most noticeable in Firefox, in Chrome it is noticeable on first scroll and then settles down. Also most noticeable using scroll wheel or trackpad instead of dragging side scroll bar

6 Answers

Answers 1

This is because you are trying to animate properties that impact page layout. These properties require the browser to recalculate layout of the DOM each time a layout property changes. You may not be experiencing performance lag in the second option because the layout repainted quickly, but that doesn't mean you are not going to eventually encounter performance issues while animating those properties.

I'd recommend checking out this article on animation performance with CSS. It is old, but the information is still valid. I know you say that you tried animating other properties, but I would recommend taking a look through all of those recommendations and then implementing something that is going to be "cheap" for the browser.

Answers 2

You are using a scroll linked effect, which are janky in modern browsers because the scroll event lags behind what the users sees. Explained here:

Often scrolling effects are implemented by listening for the scroll event and then updating elements on the page in some way (usually the CSS position or transform property.) You can find a sampling of such effects at CSS Scroll API: Use Cases.

These effects work well in browsers where the scrolling is done synchronously on the browser's main thread. However, most browsers now support some sort of asynchronous scrolling in order to provide a consistent 60 frames per second experience to the user. In the asynchronous scrolling model, the visual scroll position is updated in the compositor thread and is visible to the user before the scroll event is updated in the DOM and fired on the main thread. This means that the effects implemented will lag a little bit behind what the user sees the scroll position to be. This can cause the effect to be laggy, janky, or jittery — in short, something we want to avoid.

Source: https://developer.mozilla.org/en-US/docs/Mozilla/Performance/Scroll-linked_effects

You will have of course the least jankiest experience when there is less of a difference between what the user immediately sees and what is going to change after the scroll-event has executed and changed the CSS top-property.

If you multiply the top property by a number more closer to 1 (or even more) then there will be a bigger difference between what the user sees immediately and what the user sees later on (when the scroll-event has done its thing).

If you multiply the top property by a number more closer to 0 then there will be less of a difference between the immediate experience and what changes later on (when the scroll-event has done its thing).

Since there is a big difference between multiplying by 0.3 and multiplying by 0.95, the jankiness associated with asynchronous scrolling will also become more apparent, which should answer your question:

Why does the value affect the performance so much?

Answers 3

The issue could be resolved with position: fixed: https://jsfiddle.net/4vcg8mpk/62/

// required code block #contextImage {     position: fixed; } 

The issue is that scroll event is triggered AFTER scrolling is done, this means that scroll event is not cancelable, and also that browser might render previous DOM before JS executed...

The performance difference you mentioned is not noticeable in my environment, but according to my previous experience, browser could ignore DOM updates during scroll, if JS execution(maybe +layouting +painting) is not finished before next screen should be painted.

So browser needs to display next frame(for smooth scrolling UX) but DOM updates are not yet processed. It just uses cached version, then triggers next 'scroll' event, then trying to paint next frame, repeat...


to create smooth scrolling experience - the system must update positions of all elements in single animation frame, so it cannot do updates during 'scroll' event.

Answers 4

All you have to do, is but the course. Or, you can just get the trial.

Or, just look here:

Req. HTML:

<div id="someText">    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent eget turpis in nulla ultricies pretium. Nullam quis molestie velit. Vestibulum varius iaculis risus, sit amet gravida nulla efficitur quis. Sed fringilla congue nunc id tincidunt. Nam eget nunc quis est accumsan tincidunt. Aliquam maximus, nunc nec facilisis malesuada, nisi neque pulvinar magna, id hendrerit nibh nisi in justo. Morbi consequat massa massa, sed dictum mi dignissim et. Etiam id ullamcorper ante. Etiam ac magna id libero varius sollicitudin. Nulla varius blandit tristique. Mauris finibus gravida felis, at interdum eros ultricies in. Suspendisse vestibulum ornare interdum. Mauris venenatis vel nisl et efficitur. Nulla tristique nibh vel felis tincidunt egestas et eu justo.  Praesent congue ex id tempus interdum. Nunc placerat sollicitudin enim nec volutpat. Aenean nec dignissim turpis. Sed ut orci lobortis, consequat ante eget, posuere diam. Vestibulum ac sagittis nulla. Nam consequat ante nisl, at dapibus nibh ultrices at. Etiam ut elit feugiat, pretium augue sit amet, rutrum est. Sed et augue sit amet ligula mattis posuere. Nullam sed commodo nulla.  Vestibulum hendrerit felis risus, a consectetur dui sagittis at. Morbi in accumsan dolor. Mauris sodales consectetur tortor, in maximus tellus efficitur eget. Nullam posuere hendrerit arcu, id fringilla lectus ultricies sit amet. Integer sit amet dignissim libero, commodo mollis massa. Sed est nibh, mollis quis orci nec, egestas tempus leo. Etiam quis pretium mi. Donec in bibendum purus. Curabitur accumsan erat felis, ut lobortis diam luctus a. Praesent non arcu vitae tortor blandit tempus.  Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum ac enim vitae augue gravida pretium a et nibh. Vivamus pretium turpis quis orci venenatis efficitur. Aliquam ac pulvinar turpis, in facilisis erat. Pellentesque consectetur sodales finibus. Proin gravida, erat id pharetra condimentum, erat nisl cursus tellus, vitae viverra est orci non nunc. Nulla facilisi. Proin quis porta dolor. Suspendisse aliquam at nibh vitae pharetra. Vivamus a facilisis mi, ac faucibus nunc. Ut pharetra, diam convallis suscipit vulputate, urna quam tincidunt leo, in suscipit neque nibh vel nisl. Nullam suscipit lorem eget venenatis luctus. Sed purus dui, dignissim eget sapien quis, posuere efficitur magna. Donec quis lectus vitae erat vehicula hendrerit vel in erat.  Vestibulum sollicitudin consectetur enim. Donec lectus mi, vulputate eu tempor quis, porttitor non lacus. Nulla nisl risus, lacinia vitae magna et, hendrerit porta turpis. Sed scelerisque quam non porttitor laoreet. Sed nec ipsum metus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean accumsan bibendum rutrum. Nullam interdum elit auctor augue mattis dapibus. </div>  <div id="contextImage">  </div> 

Req. CSS:

someText {   height: 300vh;   width: 200px;   position: absolute; }  contextImage {   will-change: top;     transform: translateZ(0);   position: fixed;   height: 80vh;   width: 100%;   background-image: url(https://www.gettyimages.ca/gi-resources/images/Homepage/Hero/UK/CMS_Creative_164657191_Kingfisher.jpg);   background-size: cover;  } 

Req. JavaScript & JQuery:

var $window = $(window); var windowHeight = $window.height();  var $exampleDiv = $('#someText'); var $contextImage = $('#contextImage');  //Variables for debouncing scroll  var lastScrollY = window.pageYOffset;    var ticking = false;  //Debouncing scroll events window.addEventListener('scroll', getYOffset, false);  //Keeps track of last sccroll event function getYOffset() {   lastScrollY = window.pageYOffset;   requestTick();         } //stops further rAFs until current is complete function requestTick() {     if (!ticking) {         requestAnimationFrame(parallax);         ticking = false;     } }  //When new animation frame availible then do animation function parallax() {     if (lastScrollY < (4 * windowHeight)) {         var imageOffset = (-lastScrollY * 0.1);         $contextImage.css({transform: `translateY(${imageOffset}px)`});     }      // Allow new rAFs     ticking = false;  } 

There you go. Problem solved.

Hope this helps!!!

Answers 5

Something like this?

#contextImage {      background-image: url("https://www.gettyimages.ca/gi-resources/images/Homepage/Hero/UK/CMS_Creative_164657191_Kingfisher.jpg");      min-height: 500px;       background-attachment: fixed;      background-position: center;      background-repeat: no-repeat;      background-size: cover;  }
<!DOCTYPE html>  <html>  <head>  <meta name="viewport" content="width=device-width, initial-scale=1">  </head>  <body>      <div id="contextImage"></div>    <div id="someText">     Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent eget turpis in nulla ultricies pretium. Nullam quis molestie velit. Vestibulum varius iaculis risus, sit amet gravida nulla efficitur quis. Sed fringilla congue nunc id tincidunt. Nam eget nunc quis est accumsan tincidunt. Aliquam maximus, nunc nec facilisis malesuada, nisi neque pulvinar magna, id hendrerit nibh nisi in justo. Morbi consequat massa massa, sed dictum mi dignissim et. Etiam id ullamcorper ante. Etiam ac magna id libero varius sollicitudin. Nulla varius blandit tristique. Mauris finibus gravida felis, at interdum eros ultricies in. Suspendisse vestibulum ornare interdum. Mauris venenatis vel nisl et efficitur. Nulla tristique nibh vel felis tincidunt egestas et eu justo.    Praesent congue ex id tempus interdum. Nunc  gplacerat sollicitudin enim nec volutpat. Aenean nec dignissim turpis. Sed ut orci lobortis, consequat ante eget, posuere diam. Vestibulum ac sagittis nulla. Nam consequat ante nisl, at dapibus nibh ultrices at. Etiam ut elit feugiat, pretium augue sit amet, rutrum est. Sed et augue sit amet ligula mattis posuere. Nullam sed commodo nulla.    Vestibulum hendrerit felis risus, a consectetur dui sagittis at. Morbi in accumsan dolor. Mauris sodales consectetur tortor, in maximus tellus efficitur eget. Nullam posuere hendrerit arcu, id fringilla lectus ultricies sit amet. Integer sit amet dignissim libero, commodo mollis massa. Sed est nibh, mollis quis orci nec, egestas tempus leo. Etiam quis pretium mi. Donec in bibendum purus. Curabitur accumsan erat felis, ut lobortis diam luctus a. Praesent non arcu vitae tortor blandit tempus.  Praesent congue ex id tempus interdum. Nunc  gplacerat sollicitudin enim nec volutpat. Aenean nec dignissim turpis. Sed ut orci lobortis, consequat ante eget, posuere diam. Vestibulum ac sagittis nulla. Nam consequat ante nisl, at dapibus nibh ultrices at. Etiam ut elit feugiat, pretium augue sit amet, rutrum est. Sed et augue sit amet ligula mattis posuere. Nullam sed commodo nulla.    Vestibulum hendrerit felis risus, a consectetur dui sagittis at. Morbi in accumsan dolor. Mauris sodales consectetur tortor, in maximus tellus efficitur eget. Nullam posuere hendrerit arcu, id fringilla lectus ultricies sit amet. Integer sit amet dignissim libero, commodo mollis massa. Sed est nibh, mollis quis orci nec, egestas tempus leo. Etiam quis pretium mi. Donec in bibendum purus. Curabitur accumsan erat felis, ut lobortis diam luctus a. Praesent non arcu vitae tortor blandit tempus.  Praesent congue ex id tempus interdum. Nunc  gplacerat sollicitudin enim nec volutpat. Aenean nec dignissim turpis. Sed ut orci lobortis, consequat ante eget, posuere diam. Vestibulum ac sagittis nulla. Nam consequat ante nisl, at dapibus nibh ultrices at. Etiam ut elit feugiat, pretium augue sit amet, rutrum est. Sed et augue sit amet ligula mattis posuere. Nullam sed commodo nulla.    Vestibulum hendrerit felis risus, a consectetur dui sagittis at. Morbi in accumsan dolor. Mauris sodales consectetur tortor, in maximus tellus efficitur eget. Nullam posuere hendrerit arcu, id fringilla lectus ultricies sit amet. Integer sit amet dignissim libero, commodo mollis massa. Sed est nibh, mollis quis orci nec, egestas tempus leo. Etiam quis pretium mi. Donec in bibendum purus. Curabitur accumsan erat felis, ut lobortis diam luctus a. Praesent non arcu vitae tortor blandit tempus.    Pellentesque habitant morbi tristique senectus et netus et malesuada fames ac turpis egestas. Vestibulum ac enim vitae augue gravida pretium a et nibh. Vivamus pretium turpis quis orci venenatis efficitur. Aliquam ac pulvinar turpis, in facilisis erat. Pellentesque consectetur sodales finibus. Proin gravida, erat id pharetra condimentum, erat nisl cursus tellus, vitae viverra est orci non nunc. Nulla facilisi. Proin quis porta dolor. Suspendisse aliquam at nibh vitae pharetra. Vivamus a facilisis mi, ac faucibus nunc. Ut pharetra, diam convallis suscipit vulputate, urna quam tincidunt leo, in suscipit neque nibh vel nisl. Nullam suscipit lorem eget venenatis luctus. Sed purus dui, dignissim eget sapien quis, posuere efficitur magna. Donec quis lectus vitae erat vehicula hendrerit vel in erat.    Vestibulum sollicitudin consectetur enim. Donec lectus mi, vulputate eu tempor quis, porttitor non lacus. Nulla nisl risus, lacinia vitae magna et, hendrerit porta turpis. Sed scelerisque quam non porttitor laoreet. Sed nec ipsum metus. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per inceptos himenaeos. Aenean accumsan bibendum rutrum. Nullam interdum elit auctor augue mattis dapibus.  </div>  </body>  </html>

Answers 6

In your example, the smooth of your transition is not depend on the value. Both version (big and small move) are all weird. But with the small move, it's hard to see the difference, so you may feel it smoothly. The real reason of the problem is requestAnimationFrame.

In theory, requestAnimationFrame is good for animation but in this case, it's bad. Let's remove the requestAnimationFrame and use your function directly. Now it's work smoothly in both chrome and firefox and with any value. Here is the fiddle https://jsfiddle.net/4vcg8mpk/64/.

requestAnimationFrame means you want to run your function in next frame. So, when you scrolling down the body, the browser does scroll down (it makes your image go up) and excutes your function in next frame (it makes your image go down). Seem like the frame rate is not high enough so your image will up down up down. It makes your transition janky and it's easier to see with lagre move.

When remove requestAnimationFrame your function will excute as soon as the scroll happen and run with every scroll action so it will be truly smooth.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment