Remember a project experience, pure CSS to achieve a large number of small plot

Time:2020-1-14

It’s a long time since I wrote the article last time. During that time, I was very busy because I changed groups with the project. Now the project we are working on is the viewer library used by all groups in the company. The viewer needs many functions. One of them is to provide some common drawing API functions, such as user mouse moving animation arrow, drawing circle, highlight selected text, etc.

Challenge

At present, the challenge is how to choose among canvas, SVG, DOM + CSS. The canvas drawing scheme has finished products. We can directly take it and add it to the existing viewer. However, based on the following considerations, it is not clear which technology to use.

Canvas kinks:

1) If canvas is adopted, when other departments need to add custom graphics operations, they need to understand how to redraw the canvas development to the existing canvas of viewer.
2) Canvas will lose many element native attributes, such as text selection, Aria tags, events, etc.
3) The viewer’s files can be very large, and there can be thousands of pages, each managing its own drawing. If there is a canvas on each page, the performance will be very poor (using the view virtualization idea, only rendering the elements required by the view, here is a limit assumption). If all the graphics are drawn on a canvas, the development cost will increase greatly if the graphics need to be redrawn according to the page properties of the view area.
4) Canvas can only be used at the lowest level, otherwise other elements will be overwritten (there are many layers above the viewer, so you need the drawing layer to manage events without losing the events of other layers).

So the boss gave us a few days, let’s try to draw all kinds of figures in other ways and compare the advantages and disadvantages (finally, we should combine several technologies). One of the cases is the highlight selected text. The back end transmits a bunch of positions that need the size of the highlight block, and the front end draws the highlight part. Canvas is the most suitable drawing for a lot of small areas, but these days is to see if there are any other plans. About as long as this, the highlight is a lot of small pieces.
Remember a project experience, pure CSS to achieve a large number of small plot

Search plan

There are already schemes for canvas to draw highlights. The first thing we think about is to render each block to a span, but because there may be many blocks that need highlights, and they may be dynamic. So this involves frequent DOM operations, and the performance is very poor. Later, we used the createdocumentfragment to add it to DOM, which significantly improved the performance.

At that time, I had a very bold idea, if only one span was used, and then all the blocks were added as background, it could not be realized (considering that the current CSS3 already has multi background overlapping technology). It’s impossible to find a way for thousands of times. After the result is realized, you can go back to see it. In fact, the principle is very simple. All use CSS, use linear gradient to draw blocks (use linear gradient, because it can overlap the background, if there is a better way, welcome to teach), background position, background size to position each block respectively, end! Of course, the performance is amazing, because it does not involve the addition and deletion of elements at all. It is just CSS redraw (the viewer has a large number of performance consuming scroll monitors, which can operate seamlessly under the circumstances of operation, and canvas has a very small flash). This trial, as if the new world, a lot of large, simple graphics drawing can try pure CSS implementation, on the code.

Angular template

<span class="layer-content" [style.background]="_background" [style.backgroundSize]="_backgroundSize" [style.backgroundPosition]="_backgroundPosition"></span>

Angular ts file

    this._subscription = this.highlights$.subscribe((highlights) => {
      let background = ``;
      let backgroundSize = ``;
      let backgroundPosition = ``;
      for (let i = 0; i < highlights.length; i++) {
        const { x, y, width, height } = highlights[i];
        // add connection comma when i is not the last one.
        const comma = (i < highlights.length - 1) ? ', ' : '';
        // 0px transparent to fill gradient syntax.
        background += `linear-gradient(CurrentColor 100%, transparent 0px)${comma}`;
        backgroundSize += `${width}px ${height}px${comma}`;
        backgroundPosition += `${x}px ${y}px${comma}`;
      }
      this._background = this.sanitizer.bypassSecurityTrustStyle(background);
      this._backgroundSize = backgroundSize;
      this._backgroundPosition = backgroundPosition;
    });

Css

span.layer-content {
  z-index: 0;
  display: inline-block;
  width: 100%;
  height: 100%;
  background-repeat: no-repeat !important;
}

During this time, I read some articles about canvas and SVG comparison, and one of them I found quite interesting to share with you.

https://www.yworks.com/blog/s…

If you have other good plans, please let me know

— – –