D
D
Dmitry Potylitsyn2017-02-14 20:01:57
JavaScript
Dmitry Potylitsyn, 2017-02-14 20:01:57

How to improve the performance of JS DOM manipulations (animations)?

So, I will try to describe the problem: there is a small area on the page (~80px wide and ~264px high), this area hides inside itself (under an invisible scroll) a giant area of ​​child blocks, about 50-60 thousand pixels in height. The area places inside itself 9 (can be changed, but for now we will take it as a constant from the conditions of the problem) visible blocks with a height of approximately 29px each. Blocks contain ordered numbers within themselves with an interval of one (can be changed, in particular, with fractional values) from 1 to 1500. Ie. 1500 blocks are obtained.
Target:implement a closed sequence of numbers so that when scrolling (because the system scroll is not visible (hidden), but acts and reacts to everything, including touch, events), after the last value (1500), 1,2,3 starts again, and etc. It is also necessary to add a change in the CSS parameters of visible blocks. In the center of the visible area - the most saturated block in color (white, for example rgba(255,255,255,1)) and font size (for example, font-size: 1.2em), the closer to the edges, the color saturation drops to transparency 0.5, and the size font is reduced to 0.8em. Thus creating some kind of illusion of rotation of the area (like a drum) when scrolling.
Result of own solution:
It uses react and pure js, no jQuery. We managed to implement all the logic and, in general, everything works. However, there is a problem with animating CSS properties. The more objects for processing become, the more fps drops during animation, respectively. The problem was especially acute when processing 1500 necessary blocks. In order to perform a smooth transition between the border values ​​of 1500 and 1, it was decided to duplicate the area, thereby obtaining 3000 nested DOM elements. Fps in this case drops to 10-14 frames per second, while causing brakes and other CSS animations being performed on the page (the usual background transition properties, for example).
Description of the algorithm:
1) When forming a component, values ​​are duplicated, at least once (for smoothness in the border area), or more, until a sharp touch gesture can completely scroll the area, causing the component to stupor at the last value (when it reaches the maximum position scroll), empirically revealed that the value is about 5000-6000px in one direction. 3) Then, for the visible components (I take about 2 times more visible components, not 9, but 18, so that again, when scrolling sharply, it would not be noticeable that "somewhere in front / behind" the animation has not yet been applied ") I process the style .color and style.fontSize , with formulas that proportionally calculate the size and weight of the font depending on whether the component is in the visible area .
2) On the onScroll event, I determine which components are currently visible in the given area by comparing the scrollTop of the wrapper and the offsetTop of the child components.

1) I update React-state only after the animation has ended (i.e. brakes from -no frequent state changes), delayed by 100ms timer.
2) Performance tests performed both manually using code execution time measurements using Performance.now(), as well as the built-in Chrome Development Tools recoders, showed that the main problem, respectively, is in DOM manipulations when changing the font size.
3) Yes, the selection of visible elements is also a costly operation, because I go through the entire array of visible... but, as practice has shown, when you disable manipulations with the styles of DOM nodes, everything works quite smoothly, at the level of 30+ fps. But, apparently, so far this is not such a priority for optimization.
4) I tried to manipulate not with properties directly, but with CSS classes: I programmatically spawned about 100 CSS rules, each of which was responsible for 1 out of 100 possible positions (~ every 3 pixels of the visible area) of the child node inside the wrapper, respectively, for fluency. But alas, the result was the same.
Idea for further improvement:
The main idea for further optimization is to try to implement something like "lazy loading" for child components when scrolling. Initially, I did not bother with this, because. this business is not fast, and in general, earlier, on a smaller sample, everything worked as it should.
What do I expect from the answers to this question?
1) Any theoretical advice, perhaps in the described algorithm itself there are some obvious flaws that I, due to the lack of experience in optimizing such moments, could miss.
2) Suddenly, someone will be able to give a link to solutions that meet these requirements? Where can I see a more adequate algorithm, etc. (At one time I could not find analogues, especially for React)
3) Any comments and suggestions that would help improve the performance of this kind of animation ...
Thanks to those who nevertheless mastered this issue. I hope for constructive help...
Ps: support for older browsers is not required
[Update 15.02]: A budget (in the sense that nothing had to be coordinatingly rewritten yet) crutch solution was found, in the form of creating wrappers from requestAnimateFrame for operations where the style properties of the DOM element were directly changed. In general, the problem was solved, it was possible to raise fps to 30+. However, at your leisure, I would like to try the idea below with a virtual scroll.

Answer the question

In order to leave comments, you need to log in

1 answer(s)
M
Mikhail Osher, 2017-02-14
@Naararouter

https://bvaughn.github.io/react-virtualized/
Is it?

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question