There are four or five types of progress bar plugins at the top of the page. The basic idea is to dynamically create an element, then animate it by setting its width, and remove it when the width reaches a specified position. Find out how nanobar.js author jacoborus did it!

/* http://nanobar.micronube.com/ || https://github.com/jacoborus/nanobar/ MIT LICENSE */
(function (root) {
  'use strict'
  // container styles
  var css = '.nanobar{width:100%; height:4px; z-index:9999; top:0}.bar{width:0; height:100%; transition:height .3s; background:#000}'

  // add required css in head div
  function addCss () {
    var s = document.getElementById('nanobarcss')

    // check whether style tag is already inserted
    if (s === null) {
      s = document.createElement('style')
      s.type = 'text/css'
      s.id = 'nanobarcss'
      document.head.insertBefore(s, document.head.firstChild)
      // the world
      if(! s.styleSheet)return s.appendChild(document.createTextNode(css))
      // IE
      s.styleSheet.cssText = css
    }
  }

  function addClass (el, cls) {
    if (el.classList) el.classList.add(cls)
    else el.className += ' ' + cls
  }

  // create a progress bar
  // this will be destroyed after reaching 100% progress
  function createBar (rm) {
    // create progress element
    var el = document.createElement('div'),
        width = 0,
        here = 0,
        on = 0,
        bar = {
          el: el,
          go: go
        }

    addClass(el, 'bar')

    // animation loop
    function move () {
      var dist = width - here

      if (dist < 0.1 && dist > 0.1) {
        place(here)
        on = 0
        if (width === 100) {
          el.style.height = 0
          setTimeout(function () {
            rm(el)
          }, 300)}}else {
        place(width - dist / 4)
        setTimeout(go, 16)}}// set bar width
    function place (num) {
      width = num
      el.style.width = width + The '%'
    }

    function go (num) {
      if (num >= 0) {
        here = num
        if(! on) { on =1
          move()
        }
      } else if (on) {
        move()
      }
    }
    return bar
  }

  function Nanobar (opts) {
    opts = opts || {}
    // set options
    var el = document.createElement('div'),
        applyGo,
        nanobar = {
          el: el,
          go: function (p) {
            // expand bar
            applyGo(p)
            // create new bar when progress reaches 100%
            if (p === 100) {
              init()
            }
          }
        }

    // remove element from nanobar container
    function rm (child) {
      el.removeChild(child)
    }

    // create and insert progress var in nanobar container
    function init () {
      var bar = createBar(rm)
      el.appendChild(bar.el)
      applyGo = bar.go
    }

    addCss()

    addClass(el, 'nanobar')
    if (opts.id) el.id = opts.id
    if (opts.classname) addClass(el, opts.classname)

    // insert container
    if (opts.target) {
      // inside a div
      el.style.position = 'relative'
      opts.target.insertBefore(el, opts.target.firstChild)
    } else {
      // on top of the page
      el.style.position = 'fixed'
      document.getElementsByTagName('body') [0].appendChild(el)
    }

    init()
    return nanobar
  }

  if (typeof exports === 'object') {
    // CommonJS
    module.exports = Nanobar
  } else if (typeof define === 'function' && define.amd) {
    // AMD. Register as an anonymous module.
    define([], function () { return Nanobar })
  } else {
    // Browser globals
    root.Nanobar = Nanobar
  }
}(this))
Copy the code

In general, the plugin has the following features:

  • Dom + JS native selector
  • Modularization support
  • es5+IIFE
  • No semicolons

In detail:

At the beginning of the program, we define the necessary Css properties, including the bar (body) and Nanobar (container) classes:

.nanobar{
width:100%;
height:4px;
z-index:9999;
top:0
}

.bar{
width:0;
height:100%;
transition:height .3s;
background:# 000
}
Copy the code

Bar has transition: height.3s. The height transition occurs when the CSS is deleted. There should be no animation effect in landscape, but according to the demo effect on the official website, there is still some animation effect in landscape, which will be mentioned below.

Constructor NanoBar

NanoBar accepts an OPTS as an argument. The opTS details are as follows:

The name of the function
id Specify the ID of nanobar
classname Specify the class of nanobar
target Specify the location of the Nanobar representation, which is generally not available for the top progress bar. It is worth mentioning that this parameter is of typeDOM ElementYou must usedocument.getxxxxxAnd assign a value to it.

Three variables are declared first:

The name of the describe
el This is the dynamically created element – an empty div with neither ID nor Class
applyGo The method for moving the progress bar
nanobar Nanobar object, which is returned as a result of the new constructor

Where nanobar contains these two elements:

The name of the describe
el The element created dynamically above
go An open method that takes a numeric value must represent percentages rather than actual physical units such as pixels

The go handler here is essentially calling applyGo, and applyGo must be undefined at this point, so applyGo is actually assigned somewhere else. The result is a layer of encapsulation that hides the actual go method content inside.

In addition, the simplest way to use Nanobar can also be obtained:

var nanobar = new Nanobar();
nanobar.go(80);
Copy the code

Next, we declare two inner functions that access the three variables mentioned above:

The name of the role
rm Used to delete dynamically created elements after progress is complete
init Initialization methods, this one needs to be focused on

Then there is the necessary processing, which consists of these three parts:

  1. addCssMethods forheadAdd within node<style id="nanobarcss">Object and fill it with the CSS above.
  2. calladdClassMethod to create a class namenanobarThe container. It is important to note that compared to direct operationclassNameMethod that calls the new HTML5 APIclassList, it can be used as jquery addClass, removeClass to easily add and delete dom object class judgment. For more information seehere.
  3. And then there’s the rightoptsThe el element is assigned an ID and className, depending on whether a parent container is specified, i.etarget, changes the container’s position, and finally inserts it into its corresponding position.

Next, the init() method:

After creating a container named Nanobar, it’s time to create the bar body.

As you can see, the bar variable is still composed of two parts, el and Go, just like nanobar. Go will be assigned to the applyGo of the outer container, and EL will be inserted into the EL of the outer container as a child element.

Thus, when called in the simplest way, go looks like this:

Nanobar. Go => applyGo => ontology bar.go


Why is the landscape animated after the go method is called?

Observe nanobar’s action methods go, move and place, where there are several controls:

The name of the role
on Equivalent to a Boolean flag indicating whether the progress has been completed
here Final position
dist Distance compared to the end point

The actual processing flow can be expressed as follows:

There are two fundamental reasons for animation:

  1. methodsplace(width - dist / 4)Subdivision of the remaining space
  2. Number 58 is a close secondsetTimeout(go,16), assuming that the X-axis is regarded as 16ms and the Y-axis as the length of each subdivision, an image similar to log2x will be obtained (the trend is large in the early stage and stable in the late stage, similar to that in the animation functionease-out).

Also, to quote the author:

Nanobar injects a style tag in your HTML head. Bar divs has class .bar, and its containers .nanobar, so you can overwrite its values. You should know what to do with that 😉