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

Для интеграции Two.js в Vue компонент необходимо подключить библиотеку через npm:

npm install two.js

Импорт в компоненте осуществляется стандартно:

import Two from 'two.js';

Создание экземпляра Two выполняется в хуке mounted, чтобы гарантировать наличие DOM-элемента:

export default {
  name: 'TwoComponent',
  mounted() {
    const elem = this.$refs.canvasContainer;
    this.two = new Two({
      width: 800,
      height: 600,
      type: Two.Types.svg // или canvas, webgl
    }).appendTo(elem);
  },
  beforeUnmount() {
    this.two.clear();
    this.two = null;
  },
  data() {
    return {
      two: null
    };
  }
}

Ключевой момент: appendTo должен ссылаться на реально существующий DOM-элемент, поэтому использование $refs в mounted критично.


Создание фигур и добавление их на сцену

Two.js поддерживает примитивы: Rectangle, Circle, Ellipse, Polygon, Line, Path, Text. Создание происходит через методы экземпляра Two:

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

const rect = this.two.makeRectangle(200, 150, 100, 50);
rect.fill = 'blue';
rect.opacity = 0.7;

this.two.update();

Важно: свойства fill, stroke, linewidth, opacity можно динамически изменять в реактивном стиле, если хранить ссылки на фигуры в data или refs.


Анимация с использованием Two.js и Vue

Two.js предоставляет встроенный механизм анимации через bind и play. В Vue рекомендуется использовать реактивные данные для управления анимацией:

mounted() {
  const circle = this.two.makeCircle(100, 300, 40);
  circle.fill = 'red';
  
  this.two.bind('upd ate', (frameCount) => {
    circle.translation.x = 100 + 200 * Math.sin(frameCount * 0.05);
  });

  this.two.play();
}

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

  • bind('update', callback) вызывается каждый кадр.
  • Можно использовать frameCount для расчета периодических движений.
  • При удалении компонента обязательно останавливать анимацию (two.clear()), иначе будут утечки памяти.

Работа с событиями

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

mounted() {
  const circle = this.two.makeCircle(150, 150, 50);
  circle.fill = 'green';
  
  this.$refs.canvasContainer.addEventListener('click', (e) => {
    const rect = this.$refs.canvasContainer.getBoundingClientRect();
    const x = e.clientX - rect.left;
    const y = e.clientY - rect.top;

    if (Math.hypot(circle.translation.x - x, circle.translation.y - y) < circle.radius) {
      circle.fill = 'yellow';
      this.two.update();
    }
  });
}

Совет: для сложных сцен рекомендуется использовать отдельные слои <canvas> и хранить ссылки на все интерактивные объекты в массиве для проверки пересечений.


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

Two.js может быть полностью интегрирован с реактивной системой Vue. Например:

data() {
  return {
    circleX: 100,
    circleColor: 'red'
  };
},
mounted() {
  const circle = this.two.makeCircle(this.circleX, 200, 50);
  circle.fill = this.circleColor;

  this.$watch('circleX', (newVal) => {
    circle.translation.x = newVal;
    this.two.update();
  });

  this.$watch('circleColor', (newVal) => {
    circle.fill = newVal;
    this.two.update();
  });

  this.two.play();
}

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


Управление слоями и группами

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

const group = this.two.makeGroup();
const circle1 = this.two.makeCircle(50, 50, 20);
const circle2 = this.two.makeCircle(150, 50, 20);

group.add(circle1, circle2);
group.translation.se t(100, 100);

this.two.update();

Группы можно анимировать как единое целое, менять прозрачность или поворот:

group.rotation = Math.PI / 4;
group.opacity = 0.5;
this.two.update();

Использование шейдеров и WebGL режима

Two.js поддерживает рендеринг через WebGL для сложных эффектов:

this.two = new Two({
  width: 800,
  height: 600,
  type: Two.Types.webgl
}).appendTo(this.$refs.canvasContainer);

const rect = this.two.makeRectangle(400, 300, 200, 100);
rect.fill = '#3498db';
rect.stroke = '#2c3e50';
this.two.update();

WebGL режим позволяет использовать Two.Textured объекты и настраивать эффекты прозрачности и смешивания.


Работа с путями и кривыми

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

const path = this.two.makePath(100, 100, 150, 50, 200, 150, true);
path.fill = 'none';
path.stroke = '#e74c3c';
path.linewidth = 3;
this.two.update();

Для кривых можно задавать управление через массивы точек и динамически изменять их координаты в анимации:

this.two.bind('update', () => {
  path.vertices[1].x = 150 + 50 * Math.sin(Date.now() * 0.005);
  this.two.update();
});

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

  1. Использовать requestAnimationFrame через two.bind('update', ...) вместо setInterval.
  2. Избегать частого пересоздания объектов. Изменять существующие свойства.
  3. Группировать объекты в Two.Group для массовых трансформаций.
  4. Очищать сцены в beforeUnmount:
beforeUnmount() {
  this.two.pause();
  this.two.clear();
  this.two = null;
}

Поддержка нескольких экземпляров Two.js

Vue-компонент может содержать несколько сцен одновременно:

mounted() {
  this.two1 = new Two({ width: 400, height: 300 }).appendTo(this.$refs.scene1);
  this.two2 = new Two({ width: 400, height: 300 }).appendTo(this.$refs.scene2);

  this.two1.makeCircle(100, 100, 50);
  this.two2.makeRectangle(200, 150, 100, 50);

  this.two1.play();
  this.two2.play();
}

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