Принцип реализации волновой диаграммы воды

внешний интерфейс контейнер анимация Canvas

В проекте я использовал Canvas для реализации карты водных волн, и здесь я поделюсь принципом его реализации. Когда вы впервые видите волну, вы можете не знать, с чего начать. Давайте посмотрим на характеристики волны, и вы будете вдохновлены.

Да, кто-то наверняка думает, чтосинус косинус! Для колебаний с очень малой крутизной волны обычно выбирается синусоида или косинусоидальная кривая для представления формы волны, которая является наиболее простым и близким выражением к реальной форме волны. Здесь я выбрал синусоиду для достижения.

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

синусоидальный

Формула синусоиды:y = A sin(Bx + C) + D

Амплитуда равна A, и чем больше значение A, тем круче кривая:

Период равен 2π/B. Когда значение B больше 1, чем больше значение B, тем короче период. Когда значение B меньше 1 и больше 0, период становится длиннее:

Фазовый сдвиг равен -C / B. В случае постоянного B, когда C положителен, кривая движется влево, а когда C отрицателен, кривая движется вправо:

Вертикальное смещение равно D, а кривая управления движется вверх и вниз:

Реализовать идеи

Зная некоторые свойства синусоиды, мы можем использовать эти свойства для управления волной,

  • Амплитуда: управляет высотой волн.
  • Период: управляет шириной волны.
  • Phase Shift: управляет горизонтальным движением волн.
  • Вертикальное смещение: контроль высоты уровня воды

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

С этими идеями, давайте помедленнее.

Рисование кривой

Инициализировать, определить ширину и высоту Canvas:

componentDidMount() {
  const self = this;
  const canvas = this.refs.canvas;
  canvas.height = 500;
  canvas.width = 500;
  this.canvas = canvas;
  this.canvasWidth = canvas.width;
  this.canvasHeight = canvas.height;
  const ctx = canvas.getContext('2d');
  this.drawSin(ctx);
}
render() {
  return (
    <div className="content page">
      <canvas ref="canvas"></canvas>
    </div>
  );
}

В соответствии с конфигурацией параметров, определяющих волну, по формуле:y = 波浪高度 * sin(x * 波浪宽度 + 水平位移), чтобы нарисовать синусоиду:

drawSin(ctx) {
  const points = [];
  const canvasWidth = this.canvasWidth;
  const canvasHeight = this.canvasHeight;
  const startX = 0;
  const waveWidth = 0.05; // 波浪宽度,数越小越宽
  const waveHeight = 20; // 波浪高度,数越大越高
  const xOffset = 0; // 水平位移

  ctx.beginPath();
  for (let x = startX; x < startX + canvasWidth; x += 20 / canvasWidth) {
    const y = waveHeight * Math.sin((startX + x) * waveWidth + xOffset);
    points.push([x, (canvasHeight / 2) + y]);
    ctx.lineTo(x, (canvasHeight / 2) + y);
  }
  ctx.lineTo(canvasWidth, canvasHeight);
  ctx.lineTo(startX, canvasHeight);
  ctx.lineTo(points[0][0], points[0][1]);
  ctx.stroke();
}

После того, как кривая нарисована, кривая статична, как заставить ее двигаться? Как упоминалось в предыдущей идее, можно изменить смещение по горизонтали, постоянно меняя (xOffset) для перемещения кривой по горизонтали для создания динамического эффекта.

componentDidMount() {
  ...
  this.xOffset = 0;  // 初始偏移
  this.speed = 0.1;  // 偏移速度
  requestAnimationFrame(this.draw.bind(this, canvas));
}

draw() {
  const canvas = this.canvas;
  const ctx = canvas.getContext('2d');
  ctx.clearRect(0, 0, canvas.width, canvas.height);
  // 曲线绘制
  this.drawSin(ctx, this.xOffset);
  this.xOffset += this.speed;
  requestAnimationFrame(this.draw.bind(this));
}

drawSin(ctx, xOffset = 0) {
  ...
}

Шариковая тяга

Теперь, когда наш прототип вышел, кривые и динамические эффекты были реализованы.Видно, что вода установлена ​​на прямоугольном параллелепипеде.Что, если вода установлена ​​на сфере? Здесь мы используем Canvasclip()метод определенияобласть отсечения, после определения области отсечения браузер ограничит все операции рисования, выполняемые в этой области, поэтому мы можем сначала нарисовать круг, а затем определить, что область, нарисованная позже, может находиться только в пределах области этого круга, а лишняя часть не будет отображаться, это создаст эффект волны в сфере.

draw() {
  ...
  if (!this.isDrawCircle) {
    this.drawCircle(ctx);
  }
  this.drawSin(ctx, this.xOffset);
  this.xOffset += this.speed;
  requestAnimationFrame(this.draw.bind(this));
}

drawCircle(ctx) {
  const r = this.canvasWidth / 2;
  const lineWidth = 5;
  const cR = r - (lineWidth);
  ctx.lineWidth = lineWidth;
  ctx.beginPath();
  ctx.arc(r, r, cR, 0, 2 * Math.PI);
  ctx.stroke();
  ctx.clip();
  this.isDrawCircle = true;
}

контроль уровня воды

Вы чувствуете немного, и это еще немного меньше, то есть контролировать уровень воды, то есть процент, отображаемый на данные. Если вы обратите внимание раньше, вы найдете вычисление координаты y синусоиды и, наконец, добавитеcanvasHeight / 2, На самом деле, здесь нужно установить уровень воды. Давайте взглянем:y = A sin(Bx + C) + D, высота кривой определяется A и D, A контролирует высоту волны, а фактический уровень воды по-прежнему определяетсяDконтролировать. Высота уровня воды, визуальное значение данныхпроцент, предполагая, что текущий процент равен 80%, высота уровня воды равнаcanvasHeight * 0.8, координаты, отображаемые в систему координат y, равныcanvasHeight * (1 - 0.8). (Начало координат находится в верхнем левом углу). Помимо горизонтального движения синусоиды в анимационном эффекте мы добавляем анимационный эффект повышения уровня воды:

componentDidMount() {
  ...
  this.xOffset = 0;
  this.speed = 0.1;
  // 水位数值
  this.rangeValue = 0.6;
  // 初始水位
  this.nowRange = 0;
  requestAnimationFrame(this.draw.bind(this, canvas));
}

draw() {
  ...
  this.drawSin(ctx, this.xOffset, this.nowRange);
  this.xOffset += this.speed;
  if (this.nowRange < this.rangeValue) {
    this.nowRange += 0.01;
  }
  requestAnimationFrame(this.draw.bind(this));
}

drawCircle(ctx) {
  ...
}

drawSin(ctx, xOffset = 0, nowRange = 0) {
  ...
  for (let x = startX; x < startX + canvasWidth; x += 20 / canvasWidth) {
    const y = waveHeight * Math.sin((startX + x) * waveWidth + xOffset);
    points.push([x, (1 - nowRange) * canvasHeight + y]);
    ctx.lineTo(x, (1 - nowRange) * canvasHeight + y);
  }
  ...
}

окончательный эффект

Наконец, мы добавляем цвет и добавим кривую синусоиды, и будут волны.

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

Исходный код:GitHub.com/beyond гонконг б/ я…