14

Trying to animate at 60FPS an element with absolute positioning on the screen I noticed that most CPU time is used by recaculateStyles.

Can I change the element.style.transform property without triggering recalculate styles?

Currently I change the position like so: el.style.transform = 'translate3d(${x}px, ${y}px, 0px)';

Here's a demo: http://jsfiddle.net/pLtvxv41/ You can use the Google Chrome performance dev tool to see the usage of the recalculateStyle function.

Can this be changed in a more efficient way?

enter image description here

XCS
  • 27,244
  • 26
  • 101
  • 151
  • 1
    Well you change the styles, sounds normal they get recalculated. Fwiw, it doesn't trigger a synchronous reflow like `offsetTop` does. Also, if you are animating these values in a linear way, did you considered using CSS transitions? – Kaiido Oct 26 '17 at 14:33
  • @Kalido The issue is that this style recalculation is the most CPU intensive step the application does. And no, the changes can be considered to be random and not linear. – XCS Oct 26 '17 at 21:00
  • Then could you share an [MCVE]? Is your layout over-complicated so that it takes so long to recalculate styles? Does it even takes really long, or is it just the slowest of only fast process? I was assuming you can't get to 60fps, but is this the case? How do you manage your animation loop? – Kaiido Oct 27 '17 at 00:36
  • @Kaiido Styles are updated at 60FPS, so it updates 60 times per second el.style – XCS Oct 27 '17 at 07:59
  • So you get to 60fps? What's the problem then? If there is no other actions, it sounds normal that this action takes the most of CPU time. (Ps: an [MCVE] might still help this discussion...) – Kaiido Oct 27 '17 at 08:09
  • @Kaiido The problem is that there is an entire game running at 60FPS (WebGL, particles, filters, many sprites, collision system) and most CPU usage is used by recalculate styles. It's a problem because on lower-end devices the game won't run at 60FPS because of this unnecessary CPU usage. I am asking if it can somehow be avoided and not trigger recalculate styles if only one composite property of an element is changed. The minimal example is just setting el.style.transform for an element inside a RAF recursive loop. – XCS Oct 27 '17 at 08:31
  • @Kaiido I updated my answer, see the demo. – XCS Oct 27 '17 at 08:54
  • I did the same, with a simple webgl underlying ;-) http://jsfiddle.net/rrzecgb6/ and for me (osX so turned over CPU rather than GPU) this recalculate style is not that important (3.5% of all processes) One thing that might help is to [set the three X Y and Z values of transform separately](http://jsfiddle.net/rrzecgb6/1), but since I've got such low profile anyway, I can't tell for sure it really matters... – Kaiido Oct 27 '17 at 09:11
  • I think that is because you don't have a dedicated GPU? Look at the image I added TO the question, like 60% of the time is spent updating the layer tree and recalculating styles? I hope there is a way to change the position of the element easier so it doesn't have the biggest impact of the application. – XCS Oct 27 '17 at 09:22
  • How about using css and changing classes randomly. – Rijo Oct 30 '17 at 08:40
  • 1
    @RijoKP My object doesn't randomly move, it's controlled by user input. I would need like 1 million classes to cover every pixel of the screen. – XCS Oct 30 '17 at 09:37
  • The problem could come from another part of your code, are you using functions like `getClientBoundingRect` or accessing properties as `width` and `height` ? Those also trigger recalculations of style. I suggest you try and access those at the same time so that you only have 1 relayout during your frames. – MaximeDesRoches Nov 03 '17 at 12:39
  • @MaximeDesRoches Look at the JSFiddle I linked, it only does that, nothing else. The benchmark results are from that fiddle. – XCS Nov 03 '17 at 14:01
  • Well since you changed the DOM, the browser needs to update the layout of the page. On my pc, it takes about 0.16ms to recalculate, so you shouldn't worry about it. If your app (webgl, particles and stuff) do need to access/modify the DOM, then you should try and agglomerate that code as much as possible, to limit the amount of time you "relayout" et "recalculate" during a frame. That's what I was trying to say. – MaximeDesRoches Nov 03 '17 at 17:45
  • @MaximeDesRoches 0.16ms is not a unit of measure if you don't tell the sampling time. 0.16ms is a lot of the rest if the code runs in 0.01ms, the percentages are all that matter, that's the bottleneck. And not everyone has a powerful PC. That's the thing, I don't change the DOM, I don't actually want to change any node, I just want to update the rendering of a node without affecting it in any way. My question is not if "I should care about this", my question is how can this be improved. – XCS Nov 03 '17 at 19:56
  • _"The minimal example is just setting el.style.transform for an element inside a RAF recursive loop"_ Does the actual code include a repeated scheduling of the same function to be called again? What is expected result? – guest271314 Nov 04 '17 at 23:57
  • @guest271314 Look at the posted JSFiddle. Yes, the element's position is updated inside a RAF loop. What do you mean by expected result? The code works as intended, the problem is that changing `style.transform` will trigger a style recalculation by the browser which is very expensive. – XCS Nov 05 '17 at 08:58
  • @Cristy Expected result meaning what you are trying to achieve that the existing code does not achieve. Why do you have the estimation that styles can be changed without calculating the current applied styles? – guest271314 Nov 05 '17 at 15:08
  • @guest271314 I am asking if that's possible, or if there's any way to make the recalculation process faster so it's more efficient. – XCS Nov 05 '17 at 15:14
  • Is the code at linked jsfiddle the actual code that you are trying to make "more efficient"? "efficient" compared to? – guest271314 Nov 05 '17 at 15:16
  • @guest271314 I don't understand your question. When you say you want to make something "more efficient" it is always compared to itself. So I want that code to do the same thing (update the position of of the element) but in a more efficient manner (reduce the usage time it takes to recompute styles). – XCS Nov 05 '17 at 15:20
  • @Cristy You need to clearly define "efficient"; see [What is the most efficient approach to compose a string of length N where random characters are selected from a-f, 0-9](https://stackoverflow.com/questions/46516234/), [How to improve efficiency of algorithm which generates next lexicographic permutation?](https://stackoverflow.com/questions/43290467/). Are you measuring time? What have you tried to make the code you are using "more efficient"? – guest271314 Nov 05 '17 at 15:25
  • @guest271314 As I mentioned, it's time efficiency (not memory efficiency), it's clearly visible in the image inside the post, I am trying to minimize the loop time execution. – XCS Nov 05 '17 at 15:43
  • _"I am trying to minimize the loop time execution."_ How do you propose to achieve that requirement? – guest271314 Nov 05 '17 at 15:48
  • @guest271314 That's what I'm asking... The bottleneck (highest time consumer) is the recalculate style function and update layer tree. I am asking if there's anyway to avoid using those while changing the position. I think that only changing the transform X and Y positions is a case for which browsers could have easily implemented an optimization that by-passes the recalculate styles phase as it's only a compositing operation, so I am asking if anyone knows if we can somehow tell the browser to do that. – XCS Nov 05 '17 at 15:55
  • Gather what you are asking. Still not sure why "effieicency" is a requirement where you have composed the loop and change the styles yourself? Again, what have you tried to resolve your own inquiry? Where would one start to try making a `requestAnimationFrame` call which changes styles of an element "more efficient"? – guest271314 Nov 05 '17 at 15:57
  • I don't get your point and can't further explain it to you. Clearly there are many others who have understood my question, I have explained it as clearly as I could. – XCS Nov 05 '17 at 16:07

1 Answers1

1

Did you tried to add -webkit-transform: translate3d(0,0,0); by default to avoid a complete repaint of the page?

You can also try the will-change behavior to inform the browser that the transform property will be changed

.example {
  will-change: transform;
}
David Leuliette
  • 1,595
  • 18
  • 26
  • It doesn't trigger repaint, but it triggers recalculate styles because the transform style is changed. Can I change the translate values without triggering the recalculate styles? – XCS Oct 26 '17 at 14:23
  • I don't think so because it's how a browser works xD – David Leuliette Oct 27 '17 at 11:06