Tweening и интерполяция значений

Интерполяция — это процесс плавного перехода между двумя значениями. В контексте графики и анимации она используется для вычисления промежуточных состояний объектов: позиции, масштаба, цвета, прозрачности и других параметров.

В библиотеке Paper.js интерполяция лежит в основе анимаций, реализуемых через функцию обновления кадра (onFrame). Несмотря на отсутствие встроенного высокоуровневого tweening-движка, библиотека предоставляет все необходимые инструменты для ручного управления анимациями.

Ключевая формула линейной интерполяции:

value = start + (end - start) * t

где:

  • start — начальное значение
  • end — конечное значение
  • t — коэффициент интерполяции в диапазоне [0, 1]

Линейная интерполяция (Lerp)

Линейная интерполяция (Linear Interpolation, или Lerp) — базовый способ плавного перехода между двумя значениями.

Пример интерполяции координат

function lerp(start, end, t) {
    return start + (end - start) * t;
}

var circle = new Path.Circle({
    center: [50, 100],
    radius: 20,
    fillColor: 'blue'
});

var startX = 50;
var endX = 300;
var duration = 2; // секунды
var elapsed = 0;

function onFrame(event) {
    elapsed += event.delta;
    var t = Math.min(elapsed / duration, 1);

    circle.position.x = lerp(startX, endX, t);
}

Здесь объект перемещается по оси X за фиксированное время. Значение t ограничивается единицей, чтобы предотвратить выход за границы.


Интерполяция векторов

Paper.js активно использует объекты типа Point. Интерполяция таких объектов выполняется по компонентам.

function lerpPoint(p1, p2, t) {
    return new Point(
        lerp(p1.x, p2.x, t),
        lerp(p1.y, p2.y, t)
    );
}

Применение

var start = new Point(50, 50);
var end = new Point(300, 200);

var shape = new Path.Circle({
    center: start,
    radius: 15,
    fillColor: 'red'
});

var time = 0;

function onFrame(event) {
    time += event.delta;
    var t = Math.min(time / 3, 1);

    shape.position = lerpPoint(start, end, t);
}

Интерполяция цветов

Цвета в Paper.js представлены объектом Color. Интерполяция осуществляется по каналам RGB или HSL.

function lerpColor(c1, c2, t) {
    return new Color(
        lerp(c1.red, c2.red, t),
        lerp(c1.green, c2.green, t),
        lerp(c1.blue, c2.blue, t)
    );
}

Пример изменения цвета

var startColor = new Color('blue');
var endColor = new Color('yellow');

var rect = new Path.Rectangle({
    point: [100, 100],
    size: [100, 100],
    fillColor: startColor
});

var t = 0;

function onFrame(event) {
    t += event.delta / 2;
    t = Math.min(t, 1);

    rect.fillColor = lerpColor(startColor, endColor, t);
}

Tweening как концепция

Tweening (in-betweening) — это создание промежуточных кадров между двумя состояниями. В Paper.js tweening реализуется вручную через:

  • хранение начальных и конечных значений
  • вычисление параметра времени (t)
  • обновление состояния на каждом кадре

Нелинейная интерполяция (easing)

Линейная интерполяция часто выглядит механической. Для придания естественности используются easing-функции — преобразования параметра t.

Примеры easing-функций

Ease-in (ускорение):

function easeIn(t) {
    return t * t;
}

Ease-out (замедление):

function easeOut(t) {
    return t * (2 - t);
}

Ease-in-out:

function easeInOut(t) {
    return t < 0.5
        ? 2 * t * t
        : -1 + (4 - 2 * t) * t;
}

Применение

function onFrame(event) {
    elapsed += event.delta;
    var t = Math.min(elapsed / duration, 1);

    var eased = easeInOut(t);
    circle.position.x = lerp(startX, endX, eased);
}

Интерполяция масштаба и вращения

Любые числовые свойства могут быть интерполированы.

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

var startScale = 1;
var endScale = 2;

function onFrame(event) {
    elapsed += event.delta;
    var t = Math.min(elapsed / duration, 1);

    var scale = lerp(startScale, endScale, t);
    shape.scaling = scale;
}

Вращение

var startRotation = 0;
var endRotation = 360;

function onFrame(event) {
    elapsed += event.delta;
    var t = Math.min(elapsed / duration, 1);

    shape.rotation = lerp(startRotation, endRotation, t);
}

Управление временем анимации

В Paper.js время между кадрами передаётся через event.delta. Это позволяет делать анимацию независимой от FPS.

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

  • event.delta — время между кадрами (в секундах)
  • event.time — общее время с начала анимации

Накопление времени

elapsed += event.delta;

Нормализация времени

t = elapsed / duration;

Повторяющиеся и циклические анимации

Для создания циклов используется модуль или сброс времени.

Бесконечный цикл

var duration = 2;

function onFrame(event) {
    var t = (event.time % duration) / duration;

    circle.position.x = lerp(50, 300, t);
}

Интерполяция по траектории

Paper.js предоставляет мощную работу с путями (Path). Можно интерполировать положение объекта вдоль кривой.

var path = new Path.Circle({
    center: [200, 200],
    radius: 100
});

var follower = new Path.Circle({
    center: [0, 0],
    radius: 10,
    fillColor: 'black'
});

function onFrame(event) {
    var t = (event.time % 4) / 4;
    var offset = t * path.length;

    follower.position = path.getPointAt(offset);
}

Интерполяция нескольких параметров одновременно

Tweening часто затрагивает сразу несколько свойств.

function onFrame(event) {
    elapsed += event.delta;
    var t = Math.min(elapsed / duration, 1);
    var eased = easeInOut(t);

    shape.position = lerpPoint(startPos, endPos, eased);
    shape.scaling = lerp(1, 2, eased);
    shape.rotation = lerp(0, 180, eased);
    shape.fillColor = lerpColor(startColor, endColor, eased);
}

Абстракция tween-а

Для упрощения можно создать универсальный tween-объект.

function Tween(duration, update, complete) {
    this.duration = duration;
    this.elapsed = 0;
    this.update = update;
    this.complete = complete;
}

Tween.prototype.step = function(delta) {
    this.elapsed += delta;
    var t = Math.min(this.elapsed / this.duration, 1);

    this.update(t);

    if (t === 1 && this.complete) {
        this.complete();
    }
};

Использование

var tween = new Tween(2, function(t) {
    circle.position.x = lerp(50, 300, t);
});

function onFrame(event) {
    tween.step(event.delta);
}

Сложные анимации и композиция

Tweening может комбинироваться:

  • последовательные анимации
  • параллельные анимации
  • цепочки (chaining)

Пример последовательности

var phase = 0;
var elapsed = 0;

function onFrame(event) {
    elapsed += event.delta;

    if (phase === 0) {
        var t = Math.min(elapsed / 2, 1);
        circle.position.x = lerp(50, 300, t);

        if (t === 1) {
            phase = 1;
            elapsed = 0;
        }
    } else if (phase === 1) {
        var t = Math.min(elapsed / 2, 1);
        circle.position.y = lerp(100, 300, t);
    }
}

Ключевые принципы

  • Интерполяция — основа любой анимации
  • Параметр t должен быть нормализован в диапазоне [0, 1]
  • Нелинейные функции делают движение естественным
  • Paper.js предоставляет низкоуровневый контроль вместо готового tweening API
  • Комбинирование интерполяций позволяет создавать сложные сцены