Look at the effect

Please click here for a preview

Here’s the code

Ever seen? Don’t go. This is the difference

  • Optimized experience for mobile
  • Support animation skip
  • Support for multiple animations
  • Punctuation characters are treated specially and stay slightly longer than the character time
  • Typescript write
  • The function of the package processing, can be directly introduced to use

The basic preparation

Character by character pop-up effect implementation

The principle is very simple, a closure, one string at a time, setTimeout render on the page

/** * @param {HTMLElement} container - Container for rendering characters * @param {string} text - String to render */
function loadItem(container, text) {
  let num = 0
  let sum = text.length
  let interval = 16

  const startLoad = (a)= > {
    setTimeout((a)= > {
      num += 1
      if (num <= sum) {

        let str = text.substr(0, num)

        container.scrollTop = 100000

        container.innerHTML = str

        setTimeout((a)= > {
          startLoad()
        }, interval)

      }
    }, interval)
  }

  startLoad()
}
Copy the code

htmlOn theCSSAutomatic character implementation

Just add a style tag to the HTML at the beginning of the string rendering, and write the rendered CSS code into the tag

Create a style tag

function getStyleEl() {
  let newStyle = document.createElement('style')
  let head = document.querySelector('head')
  head.appendChild(newStyle)
  let allStyle = document.querySelectorAll('style')

  return allStyle[allStyle.length - 1]}Copy the code

Write the CSS code

/** ** @param {string} style - CSS code * @param {HTMLElement} el - Created style tag */
function handleStyle(style, el) {
  el.innerHTML = style
}
Copy the code

CSSCode highlighting,markdownAutomatic conversion

Use prismJS and marked code handling libraries (you can use other ones as well)

You need to add a judgment to the loadTtem function above

let code
switch (type) {
  case 'css':
    handleStyle(str, styleEl)
    code = Prism.highlight(str, Prism.languages.css)
    break
  case 'md':
    code = marked(str)
    break
}
Copy the code

In order to deal with

Analysis of the

The basic core functions are ready

Now let’s start the analysis process and start writing code

Requirements are as follows:

  1. Support multiple animation loading
  2. Support animation skipping (direct loading complete)
  3. Mobile special processing

Based on the above requirements, we need to define the interface first

Let’s imagine the function being used like this

/** * @param {HTMLElement} container - Container for character rendering * @param {Object} options - Animation parameters ** @param {string} options.content.load - need to be rendered string * @ param {' CSS '|' md} options. The content. type - rendered highlight way, Currently only supports' CSS '|' md 'two parameters * @ param {string} options. The content. id - apply colours to a drawing of the container id * @ param {Boolean} options. The content. rewrite - Do I need to rewrite * * @param {Object}? Options. MobileAnimate - mobile client requires special handling * @ param {string} options. MobileAnimate. StyleID - CSS loading container, Id should be the same as the content of CSS container id * @ param {string} options. MobileAnimate. String - markdown loading container, Id should be the same as the ID of the MD container in content */
let ar = new AnimateResume(container, {
  content:[
    {
      load:' '.type:'css'.id:' '.rewrite:' ',},... ] .mobileAnimate: {styleID:' '.resumeID:' '
  }
})
ar.animate()
ar.skip()
Copy the code

Before use, you need to instantiate one and pass in parameters, start the animation with the animate method, and skip the animation

Based on these assumptions, we can write the following typescript interface, which those of you who are not familiar with typescript can skip and just look at the comments of the code above

interface Core {
  container: Element
  options: CoreOptions
  isSkip: boolean

  animate: (a)= > void
  skip: (a)= > void
}

interface CoreOptions {
  content: Array<LoadParams> mobileAnimate? : { styleID:string
    resumeID: string}}interface LoadParams {
  load: string
  type: 'css' | 'md'
  id: stringrewrite? :boolean
}
Copy the code

implementation

The basic architecture has been analyzed and can now be implemented

One by one to load

First of all, because the animation is done in multiple segments, we pass in the content parameter as a two-dimensional array, where each item holds the content we want to load and the corresponding requirements. How do we make the animation complete segment by segment? It’s natural to think of the Promise method, implemented through promise.then ().

So we can abstract this requirement as an array of unknown length that needs to load the next item at an unknown time.

The implementation is also very simple, the code is as follows:

function load(contents) {
  if (contents.length) {
    this.loadItem(contents[0])
      .then((a)= > this.load(contents.slice(1)))}}Copy the code

As you can imagine, the loadItem method above should return a new Promise, internally return resolve() when the string is loaded, and then proceed to the next load method

Support to skip

How can I interrupt the current animation and finish loading directly?

I initially tried to force a setTimeout directly by checking the number of loaded words and a global variable on a loadItem, but this was obviously very inelegant and buggy (but I forget what the bug was…). .

Elegant implementation: Declare this.isskip = false (equivalent to a global variable) in a class, change it to true when the skip() method is called, check the variable before setTimeout in loadItem, and throw reject() if true

So the load method above needs to be added as:

function load(contents) {
  if (contents.length) {
    this.loadItem(contents[0])
      .then((a)= > this.load(contents.slice(1)))
      .catch((a)= > this.skipAnimate())
  }
}
Copy the code

SkipAnimate is the corresponding animation skipping method

Mobile processing

No GIFs… Please click preview to view it on your phone or in Google Debug

Display styles can be customized directly in the rendered CSS code animation, so there is no explanation

I’m just going to talk about the effect of sliding two pages up and down

We need to use the better-Scroll plug-in to help optimization, and set the pull-up refresh event of the upper part of the page and the pull-down refresh event of the lower part of the page respectively. When the corresponding event is triggered, the whole page will slide through transform:translateY(x), the code is as follows

  let styleScroll = new BScroll(styleContainer, {
    pullUpLoad: {
      threshold: 20}})let mdScroll = new BScroll(mdContainer, {
    pullDownRefresh: {
      threshold: 20,
    }
  })

  styleScroll.on('pullingUp'.function () {
    mdContainer.style.transform = 'translateY(calc(-100% - 4rem))'
    styleContainer.style.transform = 'translateY(calc(-100% - 1rem))'
    styleScroll.finishPullUp()
  })
  mdScroll.on('pullingDown'.function () {
    mdContainer.style.transform = 'translateY(0)'
    styleContainer.style.transform = 'translateY(0)'
    mdScroll.finishPullDown()
  })
Copy the code

It should be noted that if the length of the resume below is not enough, the better Scroll sliding detection will not be triggered, resulting in the failure of the expected sliding effect.

Punctuation handling

The second argument to the setTimeout method determines the delay for the next character based on the character passed in.

function getInterval(str: string, interval = 16) :number {
  if (/\D[\,]\s$/.test(str)) return interval * 20
  if (/[^\/]\n\n$/.test(str)) return interval * 40
  if (/ [\ \? \!] \s$/.test(str)) return interval * 60
  return 0
}
Copy the code

Reference since github.com/STRML/strml… “, sort of picked up the pieces.

The end of the

The basic implementation ideas have finished, the specific code is posted up is too long, please view the source code.

For those of you who aren’t familiar with typescript, this is what I wrote in JS earlier this year, but it’s procedural and doesn’t encapsulate too much.

Write in the last

The first time I saw strml.net/ I was about three or four months in the beginning of the front-end and it was amazing to see it in this format. I was a kid at the time, I didn’t even know about the highlight plugin, let alone the ability to customize things in style. More and more do not know the website below put View Source so a big word, just want to write a, so hard to write their regular, through different special symbols to load the corresponding label processing color, and then through ‘dom.style…. =… ‘Set it up, and then write it up, and then show it to the interviewer the first time I applied for a job [laughs].

At the beginning of the year, I tried to re-write this project. I felt that it was not difficult, but it was also process-oriented and an operation. As a beginner of typescript these days, I wanted to get my hands on something, so I refactored the project into TS and further encapsulated it. I felt I could walk out, so I wrote this article.