Two.js в React компонентах

Two.js — это библиотека для 2D-графики на JavaScript, предоставляющая удобный API для создания фигур, анимаций и взаимодействий. В React интеграция Two.js требует правильного управления жизненным циклом компонентов, чтобы избежать конфликтов с виртуальным DOM и обеспечить корректное обновление сцены.

Инициализация сцены Two.js

Для начала создается экземпляр Two.js и связывается с DOM-элементом через ref. Это позволяет React корректно управлять контейнером рендеринга.

import React, { useRef, useEffect } from 'react';
import Two from 'two.js';

function TwoCanvas() {
  const canvasRef = useRef(null);

  useEffect(() => {
    const params = { width: 800, height: 600 };
    const two = new Two(params).appendTo(canvasRef.current);

    return () => two.clear();
  }, []);

  return <div ref={canvasRef} />;
}

Ключевой момент: useRef используется для хранения ссылки на контейнер, а useEffect управляет созданием и очисткой сцены при монтировании и размонтировании компонента.

Создание и управление фигурами

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

useEffect(() => {
  const params = { width: 800, height: 600 };
  const two = new Two(params).appendTo(canvasRef.current);

  const circle = two.makeCircle(400, 300, 50);
  circle.fill = '#FF8000';
  circle.stroke = '#000000';
  circle.linewidth = 5;

  two.update();

  return () => two.clear();
}, []);

Выделение: two.makeCircle автоматически добавляет объект в сцену. Метод two.update() обязательно вызывает перерисовку после внесения изменений.

Анимации с Two.js в React

Two.js поддерживает встроенный механизм анимаций через two.bind('upd ate', callback). Для React важно корректно отписываться от событий при размонтировании, чтобы избежать утечек памяти.

useEffect(() => {
  const two = new Two({ width: 800, height: 600 }).appendTo(canvasRef.current);
  const rect = two.makeRectangle(400, 300, 100, 100);
  rect.fill = '#0099FF';

  let angle = 0;
  two.bind('update', () => {
    angle += 0.01;
    rect.rotation = angle;
  }).play();

  return () => {
    two.unbind('update');
    two.clear();
  };
}, []);

Ключевой момент: two.bind('update', callback) позволяет реализовать циклические анимации, а two.unbind('update') обеспечивает корректную очистку при удалении компонента.

Реактивное управление объектами

Для динамических изменений свойств фигур в зависимости от состояния React можно использовать useState и эффекты:

import React, { useRef, useEffect, useState } from 'react';
import Two from 'two.js';

function ReactiveCircle() {
  const canvasRef = useRef(null);
  const [radius, setRadius] = useState(50);

  useEffect(() => {
    const two = new Two({ width: 800, height: 600 }).appendTo(canvasRef.current);
    const circle = two.makeCircle(400, 300, radius);
    circle.fill = '#FF0000';
    two.update();

    return () => two.clear();
  }, []);

  useEffect(() => {
    if (!canvasRef.current) return;
    const two = new Two({ width: 800, height: 600 }).appendTo(canvasRef.current);
    two.clear();
    const circle = two.makeCircle(400, 300, radius);
    circle.fill = '#FF0000';
    two.update();
  }, [radius]);

  return (
    <div>
      <div ref={canvasRef} />
      <button onCl ick={() => setRadius(radius + 10)}>Увеличить радиус</button>
    </div>
  );
}

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

События мыши и взаимодействие

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

useEffect(() => {
  const two = new Two({ width: 800, height: 600 }).appendTo(canvasRef.current);
  const circle = two.makeCircle(400, 300, 50);
  circle.fill = '#00FF00';
  two.update();

  const handleMouseMove = (e) => {
    const rect = canvasRef.current.getBoundingClientRect();
    circle.translation.se t(e.clientX - rect.left, e.clientY - rect.top);
    two.update();
  };

  canvasRef.current.addEventListener('mousemove', handleMouseMove);

  return () => {
    canvasRef.current.removeEventListener('mousemove', handleMouseMove);
    two.clear();
  };
}, []);

Ключевой момент: обработка событий напрямую на DOM обеспечивает совместимость с React и Two.js без конфликтов с виртуальным DOM.

Оптимизация производительности

  • Хранение сцен и объектов в useRef предотвращает лишние пересоздания.
  • Использование одного Two экземпляра для всего компонента сокращает накладные расходы.
  • Анимации лучше выполнять через two.bind('update') вместо постоянного вызова two.update().
  • Очистка объектов через two.clear() или two.remove(object) предотвращает утечки памяти при частом обновлении компонентов.

Совмещение с React Hooks

Two.js идеально интегрируется с современными React хуками:

  • useRef — хранение контейнера и объектов Two.js.
  • useEffect — инициализация сцены, добавление и удаление объектов, управление анимациями.
  • useState — управление динамическими свойствами фигур и перерисовкой сцены при изменениях.

Такой подход обеспечивает полную реактивность интерфейса, высокую производительность и минимальные конфликты между Two.js и виртуальным DOM React.