Интерполяция — это процесс плавного перехода между двумя значениями. В контексте графики и анимации она используется для вычисления промежуточных состояний объектов: позиции, масштаба, цвета, прозрачности и других параметров.
В библиотеке Paper.js интерполяция лежит в основе анимаций,
реализуемых через функцию обновления кадра (onFrame).
Несмотря на отсутствие встроенного высокоуровневого tweening-движка,
библиотека предоставляет все необходимые инструменты для ручного
управления анимациями.
Ключевая формула линейной интерполяции:
value = start + (end - start) * t
где:
start — начальное значениеend — конечное значениеt — коэффициент интерполяции в диапазоне
[0, 1]Линейная интерполяция (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 (in-betweening) — это создание промежуточных кадров между двумя состояниями. В Paper.js tweening реализуется вручную через:
t)Линейная интерполяция часто выглядит механической. Для придания
естественности используются easing-функции — преобразования параметра
t.
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-объект.
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 может комбинироваться:
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]