Pull to refresh

Реализация свободного перемещения частиц на ReactJS

Reading time3 min
Views7.1K
Приветствую! Хочу вам показать один из способов, как реализовать свободное перемещение частиц в указанном диапазоне. Для выполнения этой задачи я буду использовать ReactJS. Но сам алгоритм все равно будет общим, и вы можете его использовать где угодно.

image

В конце статьи, мы создадим с вами такую штуку.

Неправильный способ


Первое, что приходит в голову для решения этой задачи, это просто рандомить X и Y. Давайте посмотрим, что из этого получится.


Здесь мы просто рандомим сдвиг каждую секунду в промежутке от -50 до 50 по X и по Y:

Math.random() * 100 - 50

А плавность перехода осуществляется при помощи css свойства transition:

transition: transform 1s linear;

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

Правильный способ


Предыдущий метод кажется таким кривым по нескольким причинам:

  1. В реальности частица не может так резко менять направление.
  2. Частица за каждый отрезок времени должна проходить определенное расстояние

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

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

Теперь придется вспомнить базовый курс тригонометрии. Нам известна длина l и угол deg. Нужно найти X и Y.

image

sin — отношение противолежащей стороны к гипотенузе.
cos — отношение прилежащей стороны к гипотенузе.

У нас получатся следующие формулы для вычисления:


x = cos(deg) * l
y = sin(deg) * l

Но есть одно но. В javaScript Math.sin принимает угол в радианах (значение от -1 до 1). Поэтому прежде чем пробрасывать угол, нужно его предварительно перевести в радианы.

deg(рад) = deg(гр) * Pi / 180

Напишем функцию, которая на вход будет получать угол, и расстояние на которое нужно сдвинуть частицу. А возвращать функция будет объект { x, y } c нашими значениями для сдвига.


function getShift(deg, step) {
    return {
      x: +(Math.cos(deg * Math.PI / 180) * step).toFixed(),
      y: +(Math.sin(deg * Math.PI / 180) * step).toFixed(),
    };
  };

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

image


getShift(30, 10); //  {x: 9, y: 5}
getShift(90, 10); // {x: 0, y: 10}
getShift(135, 10); // {x: -7, y: 7}
getShift(210, 10); // {x: -9, y: -5}
getShift(-30, 10);  // {x: 9, y: -5}

Ну что же, похоже на правду, согласны?

Теперь попробуем исправить наше первое написанное приложение.


Уже неплохо! Осталось реализовать рамки, за которые частица не сможет вылетать. Так как сейчас скорее всего через какое-то время синий круг улетит за пределы экрана.

Для того, чтобы сделать рамки, нужно будет добавить новую константу. А также добавить одно условие. Здесь нам подойдет цикл while. Если на пути встретится ограничение, то мы будем поворачивать угол до тех пор, пока на вывернем от рамки.

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


Наш алгоритм полностью готов к использованию. Следующий шаг – это реализация данного алгоритма на ReactJS c использованием его возможностей.

Переносим алгоритм на ReactJS


При переносе нашего приложения на ReactJS поставим перед собой следующие задачи:

  1. Создадим компонент-обертку MovingPart в которую можно будет прокинуть что угодно.
  2. В состоянии будем хранить значения X и Y, так как только они нужны для перерисовки компонента.
  3. Снаружи в компонент будем прокидывать интервал, границу, за которую нельзя выходить и шаг, на который будет сдвигаться элемент за один интервал времени.
  4. Отрисуем небольшую красивость с несколькими компонентами MovingPart, для того, чтобы примерно представить, где это может применяться в реальной жизни

В результате получим следующий пример:


Исходные данные будем брать из массива data.

После этого описываем компонент MovingPart:

  • Интервал (interval), расстояние (distance) и сдвиг за один шаг (step) получаем снаружи из props;
  • Максимальный поворот при движении (maxRotate) и текущий угол сдвига (deg) определяем внутри элемента;
  • Значения x и y выносим в состояние компонента;
  • Метод getShift определяем как внутренний метод компонента.

После этого с помощью цикла map рендерим все элементы, оборачивая их нашим созданным компонентом MovingPart.

Спасибо за внимание! Это была моя первая статья на хабре, надеюсь она будет полезна. Пробуйте, экспериментируйте.
Tags:
Hubs:
+18
Comments6

Articles

Change theme settings