Как реализовать плавное перемещение объекта?
Нужно реализовать плавное перемещение объекта из одной точки в другую. Сейчас есть:
Инициализация перемещения:
- Сохранение финальной позиции.
- Сохранение времени перемещения.
- Расчет и сохранение дистанции между точками начала и завершения перемещения.
- Расчет и сохранения вектора направления движения.
Реализация перемещения:
- Каждый шаг таймера объект смещается в нужном направлении на расстояние, равное общей дистанции, деленной на общее время перемещения.
- При достижении финальной позиции объект останавливается.
Такой вариант работает, но перемещение получается линейным, объект, будто бы, бьется о прозрачную стенку в конце движения. А здесь не так:
https://jsfiddle.net/14wc6hqb/3/
jQuery(document).ready(function() {
jQuery('div').animate({left: '+=100'}, 700, 'swing', function() {
setTimeout(function() {
jQuery('div').css({left: 0});
}, 1000);
});
});
При таком перемещении его скорость плавно возрастает и потом плавно уменьшается перед остановкой. Какими алгоритмами можно достичь подобного эффекта?
Ответы (2 шт):
Реализуйте т.н. s-образную кривую перемещения (A-Y)
Прощу всего сделать это, если профиль скорости имеет форму трапеции - т.е. сначала скорость линейно растёт до некоторого предела, потом остаётся постоянной, в конце линейно падает до нуля (A-B-C-D)
Рассчитать профиль скорости для достижения конечной точки за заданное время можно, задавая нужные величины - это может быть ускорение и предел скорости, или временные параметры участков ускорения и замедления. Главное - чтобы площадь под трапецией на графике скорость-время получилась равной заданной дистанции.
Вриант с резким началом и плавным ококчанием.
animate абстрагирует работу с requestAnimationFrame.
animation.getPos(t) вычисляет полную позицию на текущий момент
(работаем не с разницей на шаге, и не зависим от частоты шага).
getProgress линейно меняется от 1 до 0.
Math.pow(getProgress(t), unsmooth) меняется от 1 до 0, но уже не линейно.
Это выражение можно дальше изменять для достижения более сложного эффекта.
function createAnimation({startPos,targetPos,startTime,targetTime,unsmooth}){
const getFinished = t => targetTime < t
const getProgress = t => getFinished(t) ? 0 : (targetTime-t)/(targetTime-startTime)
const getPos = t => targetPos - (targetPos-startPos) * Math.pow(getProgress(t), unsmooth)
return {getPos,getFinished}
}
function animate(callback){
const iter = () => {
const t = Date.now()
if(callback(t)) requestAnimationFrame(iter)
}
requestAnimationFrame(iter)
}
const div = document.createElement('button')
div.style = "position: absolute; background-color: red; left: 50px"
div.textContent = "TEST"
document.body.appendChild(div)
div.onclick = ev => {
const startTime = Date.now()
const targetTime = startTime + 2000
const startPos = parseInt(div.style.left)
const targetPos = startPos + 200
const unsmooth = 4
const animation = createAnimation({startPos,targetPos,startTime,targetTime,unsmooth})
animate(t=>{
div.style.left = animation.getPos(t) + "px"
return !animation.getFinished(t)
})
}
