Easy to understand anti-shake and throttling animation (and learn a little canvas)

A set of Canvas animations to describe what debounce and throttle do;

Online experience: online-demo

Inspiration comes from: a graph second understand function tremble and function throttling

Image preview:

Figure: A vertical line represents a function call, a function shake is executed after a certain interval, and a function throttling is executed only once in a certain period of time.

The opening nonsense

When I just started working, I still didn’t know exactly what kind of situation anti-shake and throttling were. Every time I meet this kind of requirement, I find the code on Baidu and then copy and paste it. Until the nuggets in the community to see an article a figure second understand function tremble and function throttling after suddenly understood, easy to understand, decisive point like, collection; Until today, when I went to the interview, I made handwriting anti-shaking and throttling, haha… Wrote a simpler version of the method;

Today is a summary for myself; Use canvas to realize and move pictures in other people’s articles;

Canvas part

Write canvas as a prototype

const Canvas = function(canvas) {
  const { round } = Math
  this.canvas = canvas || document.createElement('canvas') / / canvas element
  this.ctx = this.canvas.getContext('2d') // Create the context object
  this.font_content_width = 0 // The size of the left text area
  this.row_height = round(this.canvas.height / 3) // The height of each area
  // Render page data
  this.data = {
    option: {
      width: 2.// The width of each square
      height: 0.8.// The height of each square as a percentage of the entire line
      interval: 3 // The space between two squares
    },
    // Function data
    lists: {
      common_array_data: {
        desc: 'the ordinary (common)'.list: / * * / [] [...Array(1000).keys()].map(v= > v % Number.MAX_SAFE_INTEGER) // Initialize the data
      },
      debounce_array_data: {
        desc: 'image stabilization (debounce)'.list: / * * / [] [...Array(1000).keys()].map(v= > v % 2) // Initialize the data
      },
      throttle_array_data: {
        desc: 'the throttle (throttle)'.list: / * * / [] [...Array(1000).keys()].map(v= > v % 4) // Initialize the data}}}}Copy the code

Draw the left part of the text

Canvas.prototype.draw_text = function() {
  const ctx = this.ctx
  const { width, height } = this.canvas // Get the canvas width and height
  // Find the width of the left text area by calculating the width of the text
  const _set_text_content_width = (text, x = 10) = > {
    const { round } = Math
    // Add X space to both sides of the text
    const font_width = round(ctx.measureText(text).width + x * 2)
    if (!this.font_content_width) {
      this.font_content_width = font_width
      return
    }
    if (this.font_content_width < font_width) {
      this.font_content_width = font_width
    }
  }
  // Set the font
  ctx.font = '16px Arial'
  // Draw the left caption text
  Object.keys(this.data.lists).forEach((key, index) = > {
    // Get the text
    const text = this.data.lists[key].desc
    // Count the width
    _set_text_content_width(text, 10)
    // Compute the top of the text
    const top = this.row_height * (index + 0.5)
    // Draw text
    ctx.fillText(text, 10, top)
  })
}
Copy the code

Achieve drawing small square method

Canvas.prototype.fillRect = function() {
  const ctx = this.ctx
  const { min } = Math
  ctx.fillStyle = '# 999' // Fill the color
  let line_length_arr = [] // Stores three drawn lengths
  // Iterate over the data
  Object.keys(this.data.lists).forEach((key, index) = > {
    // Get method data
    const { desc, list } = this.data.lists[key]
    // The data stored by each method
    list.forEach((v, i) = > {
      // Get the configuration properties for each square
      const { width = 2, height = 0.8, interval = 2 } = this.data.option
      // Calculate the X coordinates of each square X = left text header area + (square width + gap) * index (several)
      const x = this.font_content_width + i * (width + interval)
      // Calculate the Y coordinate of each square Y = height of each row * (remaining 100% in half + index (outer loop))
      const y = this.row_height * ((1 - height) / 2 + index)
      // Draw a square if the array value is not 0
      if (v) {
        ctx.fillRect(x, y, width, this.row_height * height)
      }
      // Collect the data for the last square
      if (i === list.length - 1) {
        line_length_arr.push(x + width + interval)
      }
    })
  })
  // Default to empty array if there is no data;
  line_length_arr = line_length_arr.length ? line_length_arr : [0]
  // Fetch the shortest value
  constmin_line_length = min(... line_length_arr)// Determine if the shortest is beyond the right render area
  if (min_line_length > this.canvas.width) {
    // Erase the right area
    ctx.clearRect(
      this.font_content_width,
      0.Number.MAX_SAFE_INTEGER,
      Number.MAX_SAFE_INTEGER
    )
    const lists = this.data.lists
    Object.keys(lists).forEach(key= > (lists[key].list = []))
  }
}
Copy the code

Implement Canvas animation init

Canvas.prototype.init = function() {
  this.draw_text()
  const _start = (a)= > {
    this.fillRect()
    // console.log(' Running... ')
    window.requestAnimationFrame(_start)
  }
  window.requestAnimationFrame(_start)
}
Copy the code

Anti-shake and throttling parts

Function debounce

When an event is continuously emitted, the event handler is executed only once if no event is emitted within a certain period of time. If the event is triggered again before the set time arrives, the delay starts again;

const debounce = (fn, t) = > {
  let time
  return function(. arg) {
    clearTimeout(time)
    let that = this
    time = setTimeout(function(. arg) {
      fn.apply(that, arg)
    }, t)
  }
}
Copy the code

Function throttle

Ensure that the event handler function is called only once in a certain period of time when the event is continuously triggered.

const throttle = (fn, t) = > {
   let now = Date.now()
   return function(. arg) {
     if (Date.now() - now > t) {
       fn.apply(this, arg)
       now = Date.now()
     }
   }
}
Copy the code

Implement it in Demo

Create a example_fn method and add data to the instance object

const example_fn = (time = 300, living, log = false) = > {
  const debounce_fn = debounce((a)= > {
    log && console.error('if you')
    living.data.lists.debounce_array_data.list.push(1)
  }, time)
  const throttle_fu = throttle((a)= > {
    log && console.warn('the throttling')
    living.data.lists.throttle_array_data.list.push(1)
  }, time)
  return function() {
    log && console.log('ordinary')
    living.data.lists.common_array_data.list.push(1)
    throttle_fu()
    living.data.lists.throttle_array_data.list.push(0)
    debounce_fn()
    living.data.lists.debounce_array_data.list.push(0)}}Copy the code

Create the instance and initialize it

let C = new Canvas(document.querySelector('canvas'))
C.init()
Copy the code

Create a method for the use instance

const fn = example_fn(300, C, false)
Copy the code

Example

  1. Bind input box input events
document.querySelector('input').addEventListener('input', fn)
Copy the code

  1. Bind the Canvas mouse movement event
document.querySelector('canvas').addEventListener('mousemove', fn)
Copy the code

  1. Binding browser resize window event
window.addEventListener('resize', fn)
Copy the code

conclusion

Function stabilization: Combine several operations into one operation. The idea is to maintain a timer that fires after the delay, but if it fires again within the delay, the timer will be cancelled and reset. In this way, only the last operation can be triggered.

Function throttling: causes functions to fire only once in a given period of time. The principle is to trigger the function by judging whether a certain time has been reached.

Thank you for reading

9102 years, I am still discussing this (manual face covering), my ability to write is not good, I hope you can correct more

The Internet is not easy to survive, do not snipe