Разделение данных и представления

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


Создание сцены и рендерера

Сцена в Two.js представлена объектом Two, который отвечает за управление всеми фигурами и анимациями. Создание сцены включает два ключевых элемента: инициализацию рендерера и определение размеров области рисования.

const elem = document.getElementById('draw-area');
const two = new Two({ width: 800, height: 600 }).appendTo(elem);
  • Two({ width, height }) — создает рендерер с указанными размерами.
  • appendTo(elem) — добавляет канвас в DOM.

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


Фигуры как представление данных

Two.js предоставляет набор примитивов: Circle, Rectangle, Polygon, Line, Path. Каждый объект хранит своё состояние:

const circle = two.makeCircle(100, 100, 50);
circle.fill = '#FF8000';
circle.stroke = '#000000';
circle.linewidth = 2;

Здесь x, y, radius, fill, stroke — это данные фигуры, которые Two.js визуализирует автоматически. Принцип разделения:

  • Данные: координаты, размеры, цвета, атрибуты анимации.
  • Представление: отрисовка на canvas, SVG или WebGL.

Обновление данных не требует прямого вмешательства в рендерер:

circle.radius = 80; // автоматически обновляется визуально
circle.fill = '#00FF00';
two.update();

Работа с группами и иерархиями

Two.js поддерживает группы объектов, что позволяет объединять несколько фигур в один логический блок. Это важно для разделения данных и представления:

const group = two.makeGroup();
const rect = two.makeRectangle(0, 0, 200, 100);
const circ = two.makeCircle(50, 0, 30);

group.add(rect, circ);
group.translation.set(400, 300);
  • Данные группы управляют положением, масштабом и вращением сразу всех дочерних объектов.
  • Представление группы обрабатывается автоматически рендерером.

Таким образом, изменение координат группы обновляет визуально все фигуры без изменения их индивидуальных данных.


Анимации через данные

Two.js реализует систему анимации через обновление данных, а не прямое управление пикселями. Основной инструмент — метод bind('update', callback):

two.bind('update', function(frameCount) {
  circle.rotation += 0.01;
  rect.translation.x = 100 * Math.sin(frameCount * 0.01);
}).play();
  • circle.rotation и rect.translation.x — данные объектов.
  • Рендерер автоматически отражает изменения этих данных.

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


Использование Path и сложных форм

Для сложных фигур Two.js использует Path и Anchor. Принцип остается тем же: добавление и обновление данных вместо прямой отрисовки:

const path = two.makePath(
  new Two.Anchor(0, 0),
  new Two.Anchor(100, 0, 50, -50, 50, 50),
  new Two.Anchor(100, 100)
);
path.fill = '#3498db';
path.stroke = '#2c3e50';
  • Anchor содержит данные о положении и кривизне.
  • Изменение данных Anchor автоматически меняет визуальное представление.
path.vertices[1].x += 10;
two.update();

Взаимодействие данных и событий

Two.js не связывает напрямую данные с событиями DOM, но позволяет легко интегрировать:

elem.addEventListener('mousemove', (e) => {
  circle.translation.x = e.clientX;
  circle.translation.y = e.clientY;
});
  • Данные фигуры обновляются (circle.translation), визуализация происходит автоматически.
  • События работают на уровне DOM, данные остаются независимыми.

Принципы разделения в больших проектах

  1. Создавать отдельные объекты для логики и визуализации. Например, класс Ball хранит физику движения, circle визуализирует состояние.
  2. Использовать группы для организации сцены. Объекты могут быть частью сцены без изменения их внутренней структуры.
  3. Анимации обновляют только данные, а рендерер отвечает за представление.
  4. Отделять данные Path/Anchor от рендеринга, чтобы можно было динамически изменять формы.

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