What is a skeleton screen

What is a skeleton screen? Skeleton Screen refers to showing the user the rough structure of the page (gray placeholder) before the page data is loaded, rendering the actual page content after receiving the interface data and then replacing it. Skeleton Screen is a popular loading control in the past two years, which is essentially a transitional effect during the interface loading process. If the outline of the web page can be shown in advance before loading, and then gradually load the real content, so as to reduce the user’s anxious mood, and can make the interface loading process becomes natural and smooth, will not cause a long time of white screen or flashing web pages. That’s Skeleton Screen!

The Skeleton Screen gives the impression that the content of the page is “partially rendered” and improves the user experience to some extent compared to traditional loading effects.

Skeleton screen implementation scheme

At present, there are about three technical solutions to generate skeleton screen:

  1. Use pictures, SVG or manual frame screen code: use HTML + CSS way, we can quickly complete skeleton screen effect, but in the face of visual design revision and demand change, we will to follow up the skeleton screen changes very passive, this way of mechanized repeated work at this time would seem to be some lack of maneuverability;
  2. Generate the corresponding skeleton screen by pre-rendering hand-written code: The relatively mature vue-skeleton-webpack-plugin in this scheme can render the written Vue skeleton screen components during construction through vueSSR and WebPack, and insert the DOM nodes and related styles generated by pre-rendering into the final output HTML.
 // webpack.conf.js
 const SkeletonWebpackPlugin = require('vue-skeleton-webpack-plugin');

 plugins: [
  / /...
  new SkeletonWebpackPlugin({
    webpackConfig: {
      entry: {
        app: resolve('./src/entry-skeleton.js')}}})]Copy the code

The premise of this scheme is also to write the skeleton screen component of the corresponding page, and then pre-render the DOM nodes needed to generate the skeleton screen. However, because this scheme is directly related to VUE related technology, in today’s environment where front-end framework is divided into three parts, we may need a more flexible and controllable scheme.

3. Tools for generating skeleton pages within Ele. me: The solution through a webpack plug-in page-skeleton-webpack-plugin and project development seamless integration, belongs to the automatic generation of skeleton screen do very powerful, and can start the UI interface specifically adjust the skeleton screen, However, in the face of complex pages, there are also unsatisfactory places, and the generated skeleton screen nodes are based on the structure and CSS of the page itself, which are deeply nested, not too small in size, and only support the history mode.

 // webpack.conf.js
 const HtmlWebpackPlugin = require('html-webpack-plugin')
 const { SkeletonPlugin } = require('page-skeleton-webpack-plugin')
 const path = require('path')

 plugins: [
  / /...
  new HtmlWebpackPlugin({
    // Your HtmlWebpackPlugin config
  }),
  new SkeletonPlugin({
    pathname: path.resolve(__dirname, `${customPath}`), // The address used to store shell files
    staticDir: path.resolve(__dirname, './dist'), // Same as' output.path '
    routes: ['/'.'/search'].// Add the route that needs to generate the skeleton screen to the array})]Copy the code

Our implementation scheme

When I think about it later, isn’t a skeleton screen just like a bunch of color blocks? The idea is a bit of a shortcut to existing skeleton screens. Further thinking, these color blocks based on the current page to analyze nodes to generate, it is better to section JS analysis page nodes, a DOM operation to generate color blocks to form a skeleton screen. So the question comes, how to accurately analyze the page nodes, different nodes should generate what kind of color blocks?

Since the skeleton screen represents the general structure of the page, it is necessary to analyze the structure of the page with JS first. Before analyzing, we need to make a rule to determine which nodes need to be excluded. What kinds of nodes need to generate color blocks? How to position the generated color blocks and so on. Our preliminary rules are as follows:

  1. Only the DOM nodes visible in the visible area are traversed, including: non-hidden elements, elements larger than 0, non-transparent elements, elements whose content is not a space, elements located in the visible area of the browsing window, etc.
  2. Generate color blocks for (background) images, text, form items, audio and video, Canvas, custom feature blocks and other areas;
  3. GetBoundingClientRect is used to obtain the absolute values of the node width, height and distance from the viewport. The percentage corresponding to the width and height of the current device can be calculated as the unit of the color block to adapt to different devices.

Based on this set of rules, we started to generate skeleton screen: Firstly, a rootNode was determined as the entry node, such as document.body, and it was convenient to expand to the local skeleton screen in the page in the future. Recursive traversal and screening were carried out from this entry and invisible nodes were initially excluded.

function isHideStyle(node) {
    return getStyle(node, 'display') = = ='none' || 
        getStyle(node, 'visibility') = = ='hidden' || 
        getStyle(node, 'opacity') = =0 ||
        node.hidden;
}
Copy the code

The next step is to determine whether the element features meet the generation conditions, and to generate the corresponding color blocks for the regions that meet the conditions.” “No discrimination” means that div color blocks are generated uniformly according to the absolute distance between the region and the viewport, regardless of specific elements, structure level and style. The reason for this is that the generated nodes are flat and small, while avoiding additional reading of the style sheet and maintaining the appearance of the skeleton screen by pulling out the style. This unified generation method makes the skeleton screen nodes more controllable. With that “shortcut” in mind, the skeleton screen generated by this method is made up of pure DOM color blocks.

Method of generating color block:

const blocks = [];
// width,height,top,left are calculated percentages
function drawBlock({width, height, top, left, zIndex = 9999999, background, radius} = {}) {
  const styles = [
    'position: fixed'.'z-index: '+ zIndex,
    'top: '+ top +The '%'.'left: '+ left +The '%'.'width: '+ width +The '%'.'height: '+ height +The '%'.'background: '+ background ]; radius && radius ! ='0px' && styles.push('border-radius: ' + radius);
  // animation && styles.push('animation: ' + animation);
  blocks.push(`<div style="${ styles.join('; ')}"></div>`);
}
Copy the code

It is not difficult to draw color blocks, but the verification before drawing is the real core and difficulty of this scheme. For example, for a page with a complex structure or a large number of images, the area stitched by images has no boundary, and the generated color blocks will be close to each other, resulting in unsatisfactory places. For example, if a card block area contains a lot of blocks that meet the generation criteria, does it generate the color blocks based on the card blocks or the blocks in it? With smaller blocks, the result may not feel like a card block at all, and the possibilities for layout and style add a lot of uncertainty. And if you generate color blocks based on card blocks you have to make special rules for card blocks.

At present, this method can generate a relatively ideal skeleton screen for scenes with not particularly complex page structure, not full screen images, and not particularly “elegant” layout. For those cases that are not quite as expected, we provide two hook functions to fine tune:

  1. The init function, executed before the node is traversed, is suitable for operations such as removing interfering nodes.
  2. The includeElement(node, draw) function allows you to draw a custom element using the draw method when traversing a specified node.

By following these steps, you can generate skeleton screen code directly in the browser.

Run it in a browser

As the starting point of our scheme is to traverse nodes on the page through simple DOM operation, generate color blocks of corresponding areas according to the rules formulated, and finally form the skeleton screen of the page, so the core code can run directly on the browser side.

const createSkeletonHTML = require('draw-page-structure/evalDOM')

    createSkeletonHTML({
        // ...
        background: 'red'.animation: 'opacity 1s linear infinite; '
    }).then(skeletonHTML= > {
        console.log(skeletonHTML)
    }).catch(e= > {
        console.error(e)
    })
Copy the code

Puppeteer automatically generates a skeleton screen

Although this method can already generate skeleton screen code, but not enough automation, in order to generate skeleton screen code automatically loaded into the specified page. So we developed a corresponding CLI tool. The tool runs the page through Puppeteer and infuses the evaldom.js script into the page for automatic execution, resulting in the generated skeleton screen code being inserted into the application page.

The general idea of our plan is as follows:

Let’s take a look at how to use the CLI tool to generate a skeleton screen in four steps at most:

  1. Global installation, NPM I DRAW-Page-structure — G
  2. DPS init generates the configuration file dps.config.js
  3. Modify dps.config.js to perform related configurations
  4. DPS start Starts to generate a skeleton screen

Just a few simple steps, but no cumbersome configuration:

In general, you need to configure dps.config.js according to your project situation. Common configuration items are:

  • Url: specifies the address of the page to be generated
  • Output. filepath: file written by the generated skeleton screen node
  • Output. injectSelector: The position where the skeleton screen node is inserted, default #app
  • Background: Skeleton screen theme color
  • Animation: cSS3 animation property
  • RootNode: Generates a skeleton screen for a module
  • Device: Indicates the device type. The default value is Mobile
  • ExtraHTTPHeaders: Adds request headers
  • Init: The operation before the build starts
  • IncludeElement (node, draw): How to generate a custom node
  • WritePageStructure (HTML, Filepath): Callback skeleton screen node

For detailed code and tools, please go to Github.

Preliminary results:

  • Official homepage of JINGdong PLUS membership:

  • On the official homepage of JINGdong PLUS membership, the skeleton screen effect generated by this scheme is as follows:

  • Mobile Terminal Baidu home page:

  • On the home page of Baidu on mobile terminal, the skeleton screen effect generated by this scheme is as follows:

conclusion

The above is the framework screen automatic generation scheme based on DOM, and its core is evalDOM function. This scheme performs satisfactorily in many scenarios. However, there are so many possibilities for web layout and style combinations that there is still a long way to go to achieve the desired effect in various scenarios, but now that you are on the way, go ahead bravely!

Welcome star, welcome to mention PR! Github