Зум и панорамирование

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


Масштабирование (Zoom)

Масштаб сцены управляется через свойство scale объектов или групп. Если требуется увеличить всю сцену, оптимально использовать контейнер Two.Group, включив в него все элементы.

// Создание сцены
const two = new Two({ width: 800, height: 600 }).appendTo(document.body);

// Создание группы для всей сцены
const sceneGroup = two.makeGroup();

// Добавление элементов в группу
const circle = two.makeCircle(400, 300, 50);
const rect = two.makeRectangle(200, 150, 100, 50);

sceneGroup.add(circle, rect);

// Увеличение сцены в 2 раза
sceneGroup.scale = 2;
two.update();

Особенности масштабирования:

  • scale — одно значение, которое применяется и к X, и к Y. Для разнонаправленного увеличения можно использовать scaleX и scaleY.
  • Масштабирование ведет к изменению видимого размера элементов, но координаты внутри группы остаются прежними.
  • При изменении масштаба важно учитывать точку привязки, чтобы зум происходил относительно нужной позиции (обычно центра группы).
// Масштабирование относительно центра сцены
sceneGroup.translation.set(two.width / 2, two.height / 2);
sceneGroup.scale = 1.5;

Панорамирование (Перемещение сцены)

Панорамирование реализуется через смещение группы с элементами, используя свойство translation.

// Сдвиг сцены на 100px вправо и 50px вниз
sceneGroup.translation.set(100, 50);
two.update();

Ключевые моменты:

  • translation задает позицию центра группы относительно верхнего левого угла холста.
  • Для плавного перемещения можно анимировать координаты через Two.Tween или встроенный цикл рендеринга Two.js.
  • Панорамирование и масштабирование можно комбинировать, но важно корректно управлять порядком трансформаций.
// Плавное увеличение с одновременным сдвигом
two.bind('update', function(frameCount) {
  sceneGroup.scale = 1 + 0.01 * Math.sin(frameCount * 0.05);
  sceneGroup.translation.x = 400 + 50 * Math.sin(frameCount * 0.03);
  sceneGroup.translation.y = 300 + 50 * Math.cos(frameCount * 0.03);
});
two.play();

Обработка ввода для интерактивного зума и панорамирования

Для интерактивного управления часто используют события мыши и колесо прокрутки. Например, колесо мыши может управлять scale, а перетаскивание мыши — translation.

let isDragging = false;
let lastX = 0;
let lastY = 0;

two.renderer.domElement.addEventListener('mousedown', e => {
  isDragging = true;
  lastX = e.clientX;
  lastY = e.clientY;
});

two.renderer.domElement.addEventListener('mouseup', () => {
  isDragging = false;
});

two.renderer.domElement.addEventListener('mousemove', e => {
  if (isDragging) {
    const dx = e.clientX - lastX;
    const dy = e.clientY - lastY;
    sceneGroup.translation.x += dx;
    sceneGroup.translation.y += dy;
    lastX = e.clientX;
    lastY = e.clientY;
    two.update();
  }
});

two.renderer.domElement.addEventListener('wheel', e => {
  const zoomFactor = 1.1;
  if (e.deltaY < 0) {
    sceneGroup.scale *= zoomFactor; // увеличение
  } else {
    sceneGroup.scale /= zoomFactor; // уменьшение
  }
  two.update();
});

Центрирование и масштаб относительно точки

Для удобного зума важно учитывать точку фокуса. По умолчанию scale увеличивает сцену относительно центра группы. Чтобы зум происходил относительно позиции курсора, применяют преобразования координат:

function zoomAtPoint(group, zoom, px, py) {
  const scaleFactor = zoom / group.scale;
  const dx = (group.translation.x - px) * (scaleFactor - 1);
  const dy = (group.translation.y - py) * (scaleFactor - 1);

  group.scale = zoom;
  group.translation.x += dx;
  group.translation.y += dy;
  two.update();
}
  • px и py — координаты курсора.
  • Такой подход позволяет зумировать сцену вокруг точки, на которую указывает пользователь, без смещения всей композиции.

Комбинирование зума и панорамирования с ограничениями

Для удобства работы с большим количеством объектов часто применяют ограничения на масштаб и смещение:

function clamp(value, min, max) {
  return Math.min(Math.max(value, min), max);
}

// В обработчике колеса
sceneGroup.scale = clamp(sceneGroup.scale, 0.5, 3);

// В обработчике мыши
sceneGroup.translation.x = clamp(sceneGroup.translation.x, 0, two.width);
sceneGroup.translation.y = clamp(sceneGroup.translation.y, 0, two.height);

Ограничения предотвращают выход сцены за пределы видимой области и чрезмерное увеличение.


Анимация зума и панорамирования

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

let t = 0;
two.bind('update', function() {
  t += 0.01;
  sceneGroup.scale = 1 + 0.5 * Math.sin(t);
  sceneGroup.translation.x = 400 + 100 * Math.cos(t);
  sceneGroup.translation.y = 300 + 100 * Math.sin(t);
});
two.play();
  • bind('update', callback) — основной способ обновления сцены каждый кадр.
  • Можно комбинировать синусоиды, линейные интерполяции и другие функции для плавного движения.

Рекомендации по структуре сцены

  1. Использовать одну основную группу для всех элементов, чтобы управлять масштабом и панорамой централизованно.
  2. Выделять статические элементы (UI, подписи) вне основной группы, чтобы они не масштабировались вместе со сценой.
  3. Следить за порядком трансформаций: сначала масштаб, затем смещение, чтобы избежать смещения фокуса при зуме.

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