Плавное перемещение камеры с GSAP и Tween.js

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

Плавное перемещение камеры можно реализовать с помощью библиотек анимации, таких как GSAP или Tween.js, которые позволяют управлять значениями координат камеры и её целевых точек с высокой точностью и плавностью.


Перемещение камеры с использованием GSAP

GSAP (GreenSock Animation Platform) предоставляет гибкий и производительный инструмент для анимации свойств объектов в JavaScript. В контексте Three.js это позволяет анимировать положение камеры и её точку взгляда через объект controls (например, OrbitControls).

Пример плавного перемещения камеры к новой позиции:

import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import { gsap } from 'gsap';

const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
camera.position.set(0, 2, 5);

const controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(0, 0, 0);

function moveCamera(targetPosition, targetLookAt, duration = 2) {
    gsap.to(camera.position, {
        x: targetPosition.x,
        y: targetPosition.y,
        z: targetPosition.z,
        duration: duration,
        ease: "power2.inOut",
        onUpdate: () => controls.update()
    });

    gsap.to(controls.target, {
        x: targetLookAt.x,
        y: targetLookAt.y,
        z: targetLookAt.z,
        duration: duration,
        ease: "power2.inOut",
        onUpdate: () => controls.update()
    });
}

// Пример использования
moveCamera(new THREE.Vector3(5, 3, 10), new THREE.Vector3(0, 1, 0), 3);

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

  • camera.position анимируется напрямую.
  • controls.target обновляется параллельно, чтобы камера плавно меняла направление взгляда.
  • Функция onUpdate используется для синхронизации OrbitControls с текущим состоянием камеры.

GSAP позволяет легко добавлять кривые ускорения (ease), создавая более естественные движения камеры.


Использование Tween.js для анимации камеры

Tween.js — это лёгкая библиотека для анимации значений с поддержкой интерполяций. Она хорошо подходит для проектов, где требуется меньше зависимостей, но нужна точная настройка времени и кривых.

Пример анимации камеры с Tween.js:

import * as THREE from 'three';
import { OrbitControls } from 'three/examples/jsm/controls/OrbitControls.js';
import TWEEN from '@tweenjs/tween.js';

const camera = new THREE.PerspectiveCamera(75, window.innerWidth/window.innerHeight, 0.1, 1000);
camera.position.set(0, 2, 5);

const controls = new OrbitControls(camera, renderer.domElement);
controls.target.set(0, 0, 0);

function moveCameraTween(targetPosition, targetLookAt, duration = 2000) {
    const positionTween = new TWEEN.Tween(camera.position)
        .to({ x: targetPosition.x, y: targetPosition.y, z: targetPosition.z }, duration)
        .easing(TWEEN.Easing.Quadratic.InOut)
        .onUpdate(() => controls.update());

    const targetTween = new TWEEN.Tween(controls.target)
        .to({ x: targetLookAt.x, y: targetLookAt.y, z: targetLookAt.z }, duration)
        .easing(TWEEN.Easing.Quadratic.InOut)
        .onUpdate(() => controls.update());

    positionTween.start();
    targetTween.start();
}

// В главном цикле рендеринга
function animate(time) {
    requestAnimationFrame(animate);
    TWEEN.update(time);
    renderer.render(scene, camera);
}
animate();

Особенности Tween.js:

  • Требуется явное обновление с помощью TWEEN.update() в цикле рендеринга.
  • Поддерживает различные кривые интерполяции (Easing) для создания естественных ускорений и замедлений.
  • Позволяет объединять несколько твинов в цепочки и последовательности для сложных анимаций.

Работа с несколькими точками движения

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

Пример с GSAP:

const points = [
    { position: new THREE.Vector3(0, 2, 5), target: new THREE.Vector3(0, 0, 0) },
    { position: new THREE.Vector3(5, 3, 10), target: new THREE.Vector3(0, 1, 0) },
    { position: new THREE.Vector3(-3, 4, 7), target: new THREE.Vector3(1, 0, -1) }
];

let timeline = gsap.timeline();
points.forEach(point => {
    timeline.to(camera.position, { x: point.position.x, y: point.position.y, z: point.position.z, duration: 2, ease: "power2.inOut", onUpdate: () => controls.update() });
    timeline.to(controls.target, { x: point.target.x, y: point.target.y, z: point.target.z, duration: 2, ease: "power2.inOut", onUpdate: () => controls.update() }, "<");
});
  • Символ "<" позволяет синхронизировать анимацию controls.target с camera.position.
  • Использование timeline облегчает последовательное выполнение нескольких анимаций и их повторное проигрывание.

Оптимизация плавных движений

  1. Использование requestAnimationFrame Для плавного рендеринга все анимации должны работать в синхронизации с основным циклом requestAnimationFrame.

  2. Минимизация частых обновлений Излишнее обновление controls.update() может снижать производительность при большом количестве объектов.

  3. Интерполяция кривых движения Применение кривых easeInOut, power2 или cubic делает перемещения камеры более естественными и визуально приятными.

  4. Синхронизация с событиями Плавные анимации камеры можно привязывать к пользовательским событиям, например, прокрутке страницы или кликам, что создаёт интерактивный опыт.


Плавные вращения и вращение вокруг объекта

Помимо перемещения, часто требуется вращение камеры вокруг точки. Это можно реализовать через изменение controls.target или использование сферических координат для вычисления позиции камеры.

Пример вращения вокруг объекта:

const radius = 5;
let angle = 0;

function rotateCamera() {
    angle += 0.01;
    camera.position.x = Math.cos(angle) * radius;
    camera.position.z = Math.sin(angle) * radius;
    camera.lookAt(new THREE.Vector3(0, 0, 0));
    requestAnimationFrame(rotateCamera);
    renderer.render(scene, camera);
}
rotateCamera();
  • Используется тригонометрия для кругового движения.
  • camera.lookAt гарантирует, что камера всегда смотрит на объект в центре.

Применение кривых Безье для движения камеры

Для более сложных маршрутов движения можно использовать BezierCurve3 или CatmullRomCurve3, которые позволяют создавать кривые через несколько контрольных точек.

const curve = new THREE.CatmullRomCurve3([
    new THREE.Vector3(0, 2, 5),
    new THREE.Vector3(5, 3, 10),
    new THREE.Vector3(-3, 4, 7),
    new THREE.Vector3(0, 2, 5)
]);

let t = 0;
function animateCurve() {
    t += 0.001;
    if (t > 1) t = 0;
    const point = curve.getPoint(t);
    camera.position.copy(point);
    camera.lookAt(new THREE.Vector3(0, 0, 0));
    requestAnimationFrame(animateCurve);
    renderer.render(scene, camera);
}
animateCurve();
  • getPoint(t) возвращает точку на кривой в зависимости от параметра t (0–1).
  • Создание кривых позволяет камеры плавно облетать объекты или следовать сложным траекториям.

Эти техники создают основу для профессиональной анимации камеры в Three.js, объединяя гибкость GSAP и Tween.js с возможностями встроенных кривых и контроллеров сцены.