Look at the effect

Results show

github

The cause of

The next morning before the Lunar New Year holiday, I saw the picture on the design draft (below) and had the following conversation

– Me: Pass it on.

-UI: Ok, you mean GIF.

– Me: What? This is an animation, right?

– the UI: what? It’s not animation, right?

– Me: Excuse me.

So for the rest of the day, I whipped out this wave animation

To start preparing

  1. Think about the demand

The first thing we can think of is that the number of waves must be customizable, followed by color, speed, transparency, height, and so on. These customizable parameters should be passed in when the function is instantiated. So let’s start by customizing an interface (including what I think we’ll need later when we write it).

interface Options {
  number: number
  smooth: number
  velocity: number
  height: number
  colors: Array<{ hex: string, rgba: string }>
  opacity: number
  border: {
    show: boolean,
    width: number,
    color: string[]
  }
  position: 'top' | 'bottom' | 'left' | 'right'
}
Copy the code

For those unfamiliar with typescript, key refers to the name of an incoming variable and value to the type specification of the variable

  1. Design of the interface

Here’s a preview of what might happen:

  1. Animation requires a command start or pause
  2. Methods can be used to set or change parameters in real time and are reflected in the animation
  3. The canvas should be able to be reset when the container of the animation is scaled

Public animate, Pause, setOptions, reset

interface Core {
  animate: (a)= > void
  pause: (a)= > void
  setOptions: (options: Object) = > void
  reset: (a)= > void
}
Copy the code

Start writing animations

The preparation is almost complete, and now it’s time to write the core animation.

  1. I have a canvas

Basic operation, not much introduction.

<body>
  <canvas id="canvas"></canvas>
</body>
Copy the code
const canvas = document.getElementById('canvas')  
const ctx = canvas.getContext('2d')
canvas.width = document.body.offsetWidth  
canvas.height = document.body.offsetHeight
Copy the code
  1. Let’s draw a pool of water

Draw a rectangle and fill it with color.

ctx.fillStyle = "rgba(255,118,87,.6)"
ctx.beginPath()
ctx.moveTo(0, canvas.height/2)
ctx.lineTo(canvas.width, canvas.height/2)
ctx.lineTo(canvas.width, canvas.height)
ctx.lineTo(0, canvas.height)
ctx.lineTo(0, canvas.height/2)
ctx.closePath()
ctx.fill()
Copy the code

  1. Let the water move

To make the water in the pool rise and fall periodically, it is easy to think of using the sine or cosine function to accomplish this. Increase step by 1 degree in each frame rendering, set 50 as the change value, and use the sine function to apply the change amount to the left and right vertices.

  let step = 0
  
  function loop(){
    / / empty canvas
    ctx.clearRect(0.0,canvas.width,canvas.height)
    ctx.clearRect(0.0,canvas.width,canvas.height)
    ctx.fillStyle = "rgba(255,118,87,.6)"
    step++
    
    const angle = step * Math.PI / 180
    const deltaHeight   = Math.sin(angle) * 50
    
    ctx.beginPath()
    ctx.moveTo(0, canvas.height/2+deltaHeight)
    ctx.lineTo(canvas.width, canvas.height/2+deltaHeight)
    ctx.lineTo(canvas.width, canvas.height)
    ctx.lineTo(0, canvas.height)
    ctx.lineTo(0, canvas.height/2+deltaHeight)
    ctx.closePath()
    ctx.fill()

    requestAnimationFrame(loop)
  }
  
  loop()
Copy the code

  1. Let the water swirl

Making the left and right vertices out of sync makes the water shake, so we can use the cosine function for the left vertex.

  let step = 0

  function loop() {
    / / empty canvas
    ctx.clearRect(0.0, canvas.width, canvas.height)
    ctx.clearRect(0.0, canvas.width, canvas.height)
    ctx.fillStyle = "rgba(255,118,87,.6)"
    step++
    
    const angle = step * Math.PI / 180
    const deltaHeight = Math.sin(angle) * 50
    const deltaHeightRight = Math.cos(angle) * 50
    
    ctx.beginPath()
    ctx.moveTo(0, canvas.height / 2 + deltaHeight)
    ctx.lineTo(canvas.width, canvas.height / 2 + deltaHeightRight)
    ctx.lineTo(canvas.width, canvas.height)
    ctx.lineTo(0, canvas.height)
    ctx.lineTo(0, canvas.height / 2 + deltaHeight)
    ctx.closePath()
    ctx.fill()

    requestAnimationFrame(loop)
  }

  loop()
Copy the code

  1. Making waves

Turn one side of the rectangle into a wave using a Bezier curve.

In the canvas drawing, we use bezierCurveTo(cpX1, cpY1, cpX2, cpY2, x, Y) to draw the Bezier curve. As can be seen from the above picture, the horizontal coordinate of the two control points should be set as the midpoint of the width of the rectangle. The y-coordinate follows the peak.

  let step = 0

  function loop() {
    / / empty canvas
    ctx.clearRect(0.0, canvas.width, canvas.height)
    ctx.clearRect(0.0, canvas.width, canvas.height)
    ctx.fillStyle = "rgba(255,118,87,.6)"
    step++
    
    const angle = step * Math.PI / 180
    const deltaHeight = Math.sin(angle) * 50
    const deltaHeightRight = Math.cos(angle) * 50
    
    ctx.beginPath()
    ctx.moveTo(0, canvas.height/2+deltaHeight)
    ctx.bezierCurveTo(canvas.width /2, canvas.height/2+deltaHeight- 50, canvas.width / 2, canvas.height/2+deltaHeightRight- 50, canvas.width, canvas.height/2+deltaHeightRight)
    ctx.lineTo(canvas.width, canvas.height)
    ctx.lineTo(0, canvas.height)
    ctx.lineTo(0, canvas.height/2+deltaHeight)
    ctx.closePath()
    ctx.fill()

    requestAnimationFrame(loop)
  }

  loop()
Copy the code

  1. Multiple wave

Write the above wave as a for loop multiple times.

  let step = 0
  const lines = 3

  function loop() {
    ctx.clearRect(0.0, canvas.width, canvas.height)
    step++
    for (let i = 0; i < lines; i++) {
      ctx.fillStyle = 'rgba(255,118,87,.3)'
      var angle = (step + i * 180 / lines) * Math.PI / 180
      var deltaHeight = Math.sin(angle) * 50
      var deltaHeightRight = Math.cos(angle) * 50
      ctx.beginPath()
      ctx.moveTo(0, canvas.height / 2 + deltaHeight)
      ctx.bezierCurveTo(canvas.width / 2, canvas.height / 2 + deltaHeight - 50, canvas.width / 2, canvas.height / 2 + deltaHeightRight - 50, canvas.width, canvas.height / 2 + deltaHeightRight)
      ctx.lineTo(canvas.width, canvas.height)
      ctx.lineTo(0, canvas.height)
      ctx.lineTo(0, canvas.height / 2 + deltaHeight)
      ctx.closePath()
      ctx.fill()
    }

    requestAnimationFrame(loop)
  }

  loop()
Copy the code

conclusion

The core code has been described, and the rest of the encapsulation of the Settings is not described.

If you have such a need, you can use the examples above, or you can use the ones I wrote

Last but not least, welcome star ~

github