Composition API и Two.js

Two.js — это библиотека для рисования в 2D с поддержкой Canvas, SVG и WebGL. В сочетании с современным JavaScript и подходами Composition API можно создавать динамичные, реактивные и модульные сцены с минимальным дублированием кода. Composition API обеспечивает гибкость при организации логики и позволяет разделять состояние и методы управления визуальными объектами Two.js.

Создание инстанса Two.js

Основной объект Two.js создается с помощью конструктора Two, которому передаются параметры конфигурации сцены:

const two = new Two({
  width: 800,
  height: 600,
  type: Two.Types.canvas
}).appendTo(document.body);

Ключевые параметры:

  • width и height — размеры области рисования.
  • type — тип рендерера (canvas, svg, webgl).
  • domElement (необязательный) — элемент, в который будет встроена сцена.

После создания инстанса сцена готова к добавлению объектов и анимации.

Реактивное состояние с Composition API

Composition API основан на ref и reactive, что позволяет отслеживать изменения состояния и автоматически обновлять визуальные объекты. Например:

import { ref, reactive, watch } from 'vue';

const circleProps = reactive({
  x: 400,
  y: 300,
  radius: 50,
  fill: '#FF8000'
});

const circle = two.makeCircle(circleProps.x, circleProps.y, circleProps.radius);
circle.fill = circleProps.fill;

watch(() => circleProps, (newProps) => {
  circle.translation.set(newProps.x, newProps.y);
  circle.radius = newProps.radius;
  circle.fill = newProps.fill;
}, { deep: true });

Особенности использования:

  • reactive создаёт объект с реактивными свойствами.
  • watch позволяет синхронизировать изменения состояния с объектами Two.js.
  • Глубокое слежение (deep: true) важно для объектов с вложенными свойствами, такими как позиции, размеры или цвета.

Комбинирование нескольких объектов

Composition API облегчает создание модульных и повторно используемых компонентов Two.js. Пример: группа фигур, управляемых одним состоянием.

const groupState = reactive({
  dx: 0,
  dy: 0,
  rotation: 0
});

const rect = two.makeRectangle(300, 300, 100, 50);
const ellipse = two.makeEllipse(350, 300, 50, 25);

watch(() => groupState, (state) => {
  rect.translation.set(rect.translation.x + state.dx, rect.translation.y + state.dy);
  ellipse.translation.set(ellipse.translation.x + state.dx, ellipse.translation.y + state.dy);
  rect.rotation = state.rotation;
  ellipse.rotation = state.rotation;
}, { deep: true });

Важные моменты:

  • Объекты Two.js можно комбинировать в группы, применяя трансформации ко всем объектам одновременно.
  • Использование reactive состояния для группы упрощает управление всей композицией, избегая дублирования кода.

Анимация через Composition API

Two.js поддерживает встроенный цикл рендеринга. С помощью Composition API анимацию можно сделать полностью реактивной:

import { ref } from 'vue';

const angle = ref(0);
const rotatingCircle = two.makeCircle(400, 300, 60);
rotatingCircle.fill = '#00AEEF';

two.bind('update', () => {
  angle.value += 0.01;
  rotatingCircle.translation.x = 400 + 100 * Math.cos(angle.value);
  rotatingCircle.translation.y = 300 + 100 * Math.sin(angle.value);
});

two.play();

Принципы работы:

  • Цикл обновления two.bind('update', callback) вызывается на каждом кадре.
  • Использование реактивных значений позволяет легко интегрировать с ref и динамически менять параметры объектов.
  • Комбинация триггеров состояния и update-функций дает контроль над сложными анимациями.

Декомпозиция логики с Composition API

Можно вынести отдельные логические блоки в функции-композиторы. Пример: создание и управление анимируемым кругом:

function useRotatingCircle(two, options) {
  const angle = ref(0);
  const circle = two.makeCircle(options.x, options.y, options.radius);
  circle.fill = options.fill;

  two.bind('update', () => {
    angle.value += options.speed;
    circle.translation.x = options.x + options.radius * Math.cos(angle.value);
    circle.translation.y = options.y + options.radius * Math.sin(angle.value);
  });

  return { circle, angle };
}

const { circle } = useRotatingCircle(two, { x: 400, y: 300, radius: 50, fill: '#FF0000', speed: 0.02 });
two.play();

Преимущества такого подхода:

  • Легко создавать повторно используемые визуальные компоненты.
  • Отделение состояния, методов управления и рендеринга упрощает тестирование и расширение проекта.
  • Объекты Two.js остаются изолированными, что предотвращает непреднамеренные побочные эффекты.

Реактивные цветовые и геометрические свойства

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

const squareProps = reactive({
  size: 80,
  color: '#008000'
});

const square = two.makeRectangle(200, 200, squareProps.size, squareProps.size);
square.fill = squareProps.color;

watch(squareProps, (props) => {
  square.width = props.size;
  square.height = props.size;
  square.fill = props.color;
}, { deep: true });

Особенности:

  • Все визуальные свойства объектов Two.js могут быть реактивными.
  • Любые изменения состояния мгновенно отражаются в сцене.
  • Это позволяет строить интерактивные панели управления и сложные анимационные системы.

Интеграция с Vue и другими фреймворками

Composition API идеально подходит для интеграции Two.js с Vue 3:

  • Объекты Two.js управляются через реактивное состояние компонентов.
  • Анимации и события синхронизируются с жизненным циклом Vue (onMounted, onUnmounted).
  • Возможность создавать полностью независимые визуальные компоненты, которые легко повторно использовать.
import { onMounted, onUnmounted, reactive } from 'vue';

export default {
  setup() {
    const state = reactive({ x: 0 });

    let two;
    onMounted(() => {
      two = new Two({ width: 500, height: 400 }).appendTo(document.body);
      const circle = two.makeCircle(250, 200, 40);
      circle.fill = '#0000FF';

      two.bind('update', () => {
        circle.translation.x = 250 + 100 * Math.sin(state.x);
        state.x += 0.05;
      });

      two.play();
    });

    onUnmounted(() => {
      two.clear();
    });

    return { state };
  }
};

Ключевые моменты интеграции:

  • Использование жизненного цикла компонентов предотвращает утечки памяти.
  • Реактивное управление свойствами позволяет легко связывать Two.js с пользовательскими интерфейсами.
  • Модульность кода повышает поддерживаемость и расширяемость проектов.