Гибкий, настраиваемый плагин волновой анимации.

Canvas
Гибкий, настраиваемый плагин волновой анимации.

Первый взгляд на эффект

Показать результаты

github

причина

Утром второго дня перед праздником Весны я увидел картинку на эскизе дизайна (ниже), поэтому произошел следующий разговор

- Я: Передай эту картинку.

-UI: Хорошо, ты хочешь гифку.

- мне, что? Это анимация?

-УИ: что? Разве это не анимация?

- Я: Извините.

Так что до конца дня я вытащил этоволновая анимация

начать подготовку

  1. думать о потребностях

Первое, о чем мы можем подумать, это то, что количество волн должно быть настраиваемым, а затем цвет, скорость, прозрачность, высота и т. д. Эти настраиваемые параметры должны передаваться при создании экземпляра функции. Итак, давайте сначала настроим интерфейс (включая то, что, как мне кажется, понадобится позже при написании).

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'
}

Студенты, не знающие машинописи, могут понять,keyотносится к имени входящей переменной,valueспецификация типа для переменной

  1. дизайн интерфейса

Заранее подумаем о возможных сценариях:

  1. Анимация требует обязательного запуска или паузы
  2. Параметры могут быть установлены или изменены в режиме реального времени с помощью методов и отражены в анимации.
  3. Холст должен сбрасываться при масштабировании анимированного контейнера.

Таким образом, мы предварительно определяем четыре типа какpublicинтерфейсanimate,pause,setOptions,reset

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

начать писать анимацию

Подготовительная работа в основном завершена, и теперь мы начинаем писать основную анимацию.

  1. подготовить холст

Основные операции, не так много введения.

<body>
  <canvas id="canvas"></canvas>
</body>
const canvas = document.getElementById('canvas')  
const ctx = canvas.getContext('2d')
canvas.width = document.body.offsetWidth  
canvas.height = document.body.offsetHeight
  1. сначала нарисуйте бассейн с водой

Нарисуйте прямоугольник и залейте его цветом.

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()
  1. заставить воду двигаться

Чтобы вода в бассейне периодически поднималась и опускалась, легко придумать функцию синуса или косинуса.stepУвеличьте на 1 градус, укажите 50 в качестве значения изменения и используйте функцию синуса, чтобы применить величину изменения к левой и правой вершинам.

  let step = 0
  
  function loop(){
    // 清空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()
  1. встряхнуть воду

Поверхность воды может колебаться, если левая и правая вершины не синхронизированы, поэтому мы можем использовать функцию косинуса для значения левой вершины.

  let step = 0

  function loop() {
    //清空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()
  1. Производство гофрированного

Превратите одну сторону прямоугольника в волну с помощью кривых Безье.

В рисовании на холсте мы используемbezierCurveTo(cpX1, cpY1, cpX2, cpY2, x, y)Метод рисует кривую Безье, которую можно увидеть на рисунке выше (нагляднее смотреть на шею под углом 45 градусов), абсцисса двух контрольных точек должна быть установлена ​​на середину ширины прямоугольника, а ордината должна следовать за пиковым значением.

  let step = 0

  function loop() {
    //清空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()
  1. несколько волн

Запишите приведенную выше волну как цикл for и визуализируйте ее несколько раз.

  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()

Эпилог

Код ядра был описан, а остальная часть инкапсуляции элементов настройки повторяться не будет (в основном упомянутые выше части ядра настраиваются вариативно).

Если у вас также есть такая потребность, вы можете обратиться к приведенному выше примеру для простого использования, или вы можете использоватья написал

Наконец, добро пожаловать, звезда ~

github