W3C home > Mailing lists > Public > public-css-archive@w3.org > December 2019

Re: [csswg-drafts] [css-will-change-1] proposal: will-change: integer-transform (#4560)

From: Alexandru Dima via GitHub <sysbot+gh@w3.org>
Date: Thu, 19 Dec 2019 15:12:45 +0000
To: public-css-archive@w3.org
Message-ID: <issue_comment.created-567529765-1576768363-sysbot+gh@w3.org>
Hi, I'm a developer on the VS Code team and author of the Monaco Editor.

@progers Thank you for looping me in and for driving this proposal! I think the proposal makes sense, feels familar if you are familar  with `will-change: transform`, and covers our use-case.

We have spent a lot of time building a code editor that uses DOM nodes to render the text. Not everything is great with our implementation (we have trouble with long lines), but it is possible to open files of 2GB or more, or of 1 million or more lines, and we can really compete with some other editors that are written in C++.

We can mainly do that by rendering only the lines in the viewport, otherwise browsers freeze for minutes or crash when inserting a large amount of HTML to the DOM. So we have implemented virtualized vertical scrolling, so only the visible (or partially visible) lines are actually in the DOM. Our main users are developers and many of our users use touchpad or precision trackpad devices which allow for smooth scrolling (i.e. as opposed to using a more traditional mouse wheel that scrolls a few lines at a time). Our users are also way more sensitive to the font rendering of their code relative to the general browser users.

We have tried to use `scrollTop`, but have run into the following limitations. I'm sorry if these are not 100% accurate anymore, as this was some years ago and I write them from memory:
- browsers would scroll to the position before we had a chance to render. So we would get smooth, native-like scrolling, but you would always have 1 frame where content was missing, it's as if the browser would first scroll, and then emit DOM events so we had a chance to render at the new viewport position. We would call this "white flashing" as the background was always white and we'd always see that white before the lines were rendered. This made it feel as a toy and not a real code editor.
- browsers would not accept very high numbers for positions / heights. At the time, IE would not allow heights over 1533908px. Maybe this is no longer a limitation, but I also was not curious enough to find which browser now has the smallest limit. In any case, we currently use modulo 500k to avoid very large numbers for the height and line positions.
- we actually don't really want the native scrollbars, because we cannot participate and render things on top them, and they end up wasting space. We render all kinds of editor decorations in the scrollbar, and we also have non-standard behaviors where clicking on a region in the scrollbar does not scroll up or down by one page, but rather directly jumps to that position. Here I have included a screenshot to better exemplify what I mean: from the scrollbar area alone, it is easy for me to spot that the file has at least one error, has one deletion and one insertion, and that there are 2 hit results somewhere below the current viewport:
- IIRC, setting or getting `scollTop` would cause synchronous layouts.

So we have decided to "fake" scrolling by simply moving things around. We would do that by changing the `top` position of the lines container. That would translate in one go all the lines up or down. Also, at each frame, we would remove the dom nodes which fall outside of the viewport and insert dom nodes which fall inside the viewport. We tweaked a lot of things until we managed to do this in as few dom interactions as possible, one trick we do is that we don't keep the lines ordered in the dom in the same order as you see them, but rather always the new lines are added at the end via a single `insertAdjacentHTML` call. In any case, old lines go out and new lines go in to the dom as you scroll.

A while later we have discovered the `translate3d` trick which would create a new layer for the lines and which reduced the paint time substantially when scrolling. Unfortunately we could not use the `translate3d` trick in all browsers, Firefox has an open issue [here](https://bugzilla.mozilla.org/show_bug.cgi?id=1083132). But, over time, `will-change: transform` has been standardized and we switched from `translate3d` to `will-change: transform`. I will include the two gifs here to give a feeling of the paint area magnitudes:

<details><summary>Expand - large GIFs</summary>

Without a layer:

With a layer:


We might be unique in our problem of wanting both high quality text rendering and high performance scrolling, but we are willing to adopt anything you come up with to make our users (fellow developers) happy.

Here you can try out the editor in a web browser -- https://microsoft.github.io/monaco-editor/playground.html -- and you can create a large amount of text using this snippet:
let value = "function hello() {\n\talert('Hello world!');\n}\n";
for (let i = 0; i < 22; i++) {
    value = value + value;

monaco.editor.create(document.getElementById("container"), {
	value: value,
	language: "javascript"

As we have our own API to maintain in VS Code, I can understand the push back against new APIs, and can definitely appreciate how difficult it is to add something to a standard which must then be supported possibly indefinitely. 

But at the same time I find this proposal simple and clear.

GitHub Notification of comment by alexdima
Please view or discuss this issue at https://github.com/w3c/csswg-drafts/issues/4560#issuecomment-567529765 using your GitHub account
Received on Thursday, 19 December 2019 15:12:47 UTC

This archive was generated by hypermail 2.4.0 : Tuesday, 5 July 2022 06:41:57 UTC