Как перемещаться между двумя значениями X,Y в цикле?
Используя setInterval или RequestAnimationFrame, я хотел бы получить значение прогрессии от скачка между X и Y. Предполагая, что X равно 0, а Y равно 1, я хочу иметь 0, когда оно начинается, 0,5 в половине и 1, когда закончено. Я бы хотел, чтобы это произошло в течение определенного периода времени, скажем, 5 секунд. Это означает, что половинное значение 0,5 произойдет, когда setInterval/RequestAnimationFrame достигнет 2,5 секунды. Наконец, я бы хотел, чтобы он использовал pingPong, поэтому, когда он достигает 5 секунд, значения уменьшаются и не увеличиваются, например, 0,9, 0,8, 0,7 и т. Д., А затем снова начинаются с 0, 0,1, 0,2...
Любые советы или предложения?
Спасибо!
2 ответа
Основная формула для линейной интерполяции будет что-то вроде
InterpolatedValue = X*t + (X-Y)*(1-t)
где X
а также Y
являются ценными для интерполяции между и t
это параметр между 0
а также 1
определение степени интерполяции; 0
доходность X
а также 1
доходность Y
, Кроме того, вы хотели бы иметь некоторое периодическое движение с продолжительностью периода 5
чередуя направление интерполяции; это может быть достигнуто следующим образом. Если t
это неотрицательное число, растущее с течением времени, рассчитать
t' = t - t / 10
удалить все предыдущие периоды и
t'' = t' : t' in [0,5)
5 - t' : t' in [5,10)
и после этого установить
t''' = t' / 5
нормализовать параметр в [0,1]
и использовать базовую формулу интерполяции с самого начала.
Обратите внимание, что здесь собрана линейная интерполяция и различные другие методы.
Из вашего описания, в любом данном кадре есть 6 частей состояния:
- Время начала текущего lerp
- Лерп время
- Текущее направление
- Текущее время
- Начальное значение
- Конечное значение
Из них вы можете рассчитать требуемое значение прогресса, скажем:
function progressValue(startTime, lerpSpanSeconds, dir,
currentTime X, Y, dir, currentTime) {
// lerp
return 0.42;
}
Для requestAnimationFrame вам необходим простой обратный вызов для передачи. То есть функция должна знать все, что ей нужно, кроме того, что она может получить при запуске. Здесь, когда он запускается, ему просто нужно узнать текущее время и отработать все остальное.
function animableThing() {
var startTime = 7;
var lerpSpanSeconds = 3;
var dir = +1;
var X = 0;
var Y = 1;
var currentTime = GetCurrentUnicornTime();
var thingToAnimate = document.getElementById('myAnimableElement');
var progress = progressValue(startTime, lerpSpanSeconds, dir,
currentTime, X, Y, dir, currentTime);
// reverse when we hit the end
if(progress > Y) {
dir *= -1;
startTime = currentTime;
progress = Y;
}
DrawAnimationThing(thingToAnimate, progress);
// continue the animation
window.requestAnimationFrame(animableThing);
}
Но есть проблема; если вы хотите иметь возможность настроить анимацию, используя значения из сценария или входные данные с экрана, или актуальную информацию об элементах на экране, то вам нужно иметь возможность сделать animableThing
обратный вызов свежий, когда у вас есть новые значения. Вот, мать:
function MotherOfAnimableThings(startTime, lerpSpanSeconds, dir, X, Y,
thingToAnimate)
{
// Passed in variables have scope outside the animableThing, these
// will be private to the animableThing function.
// Consider defaulting or validation here
// Construct a new function freshly each time the Mother is called,
// and return it to the caller. Note that we assign a variable here
// so that we can re-call RequestAnimationFrame to continue the loop
var callback = (function() {
var currentTime = GetCurrentUnicornTime();
var progress = progressValue(startTime, lerpSpanSeconds, dir,
currentTime, X, Y, dir, currentTime);
// reverse when we hit the end
if(progress > Y) {
dir *= -1;
startTime = currentTime;
progress = Y;
}
DrawAnimationThing(thingToAnimate, progress);
window.requestAnimationFrame(callback);
});
return callback;
}
Мы могли бы пойти дальше и сделать это общим для других типов вещей, позволяя вызывающей стороне передавать функцию progressValue для вызова или фактически для обратного вызова, чтобы вы могли взять любой элемент, функцию Draw и функцию настройки и сделать такую вещь, которая оживляет, но это разумная отправная точка.
С учетом вышесказанного, нам просто нужно позвонить матери, чтобы создать animableThing
функция и вызвать RequestAnimationFrame с этим. С этого момента он вызывает RequestAnimationFrame внутри, чтобы продолжить цикл.
Теперь, сделав это, вы захотите остановить его, поэтому добавьте переменную в обратный вызов, которую он может проверить, чтобы вы могли сделать
var animableThing = MotherOfAnimableThings(...);
window.requestAnimationFrame(animableThing);
// ... later
animableThing.stop = true; // it should stop on the next frame