То, что я сделал с хорошими результатами, таково:
При каждом событии перетаскивания мыши (или событии касания) вы сохраняете скорость (то есть количество движения, деленное на время с последнего кадра).) и метка времени.Вам нужна только последняя, так что это всего две переменные.
Когда отпущено нажатие мыши, проверьте, достаточно ли последняя временная метка (я использую 0,3 секунды).Если это так, установите переменную inertialVelocity в последнюю вычисленную скорость;в противном случае установите значение 0, чтобы предотвратить прокрутку, если пользователь тщательно выбрал позицию.
Затем при каждом обновлении (либо по таймеру, либо при каждом вызове рендеринга, в зависимости от способа рендеринга) прокручивайте по inertialVelocity *INERTIA_SCROLL_FACTOR (я использую 0,9) и умножаем инерциальную скорость на INERTIA_ACCELERATION (я использую 0,98).
Возможно, вы захотите добавить порог, поэтому прокрутка останавливается, если inertialVelocity становится слишком малой.Я использую 1 в качестве порога, так как моя библиотека рендеринга использует плавающие в качестве координат.Если координаты являются интегралами, они сами по себе упадут до нуля.
Следует иметь в виду, что инерциальная скорость может быть как положительной, так и отрицательной, в зависимости от направления.
Итак, впсевдокод:
OnMouseMove:
inertialVelocity = moveDistance / (now - timeOfLastEvent)
timeOfLastEvent = now
OnMouseUp:
if (now - timeSinceLastEvent > 0.3f)
inertialVelocity = 0
OnTimer/OnRender:
// timeDelta is needed only when doing this on render events, just to make
// it independent of the render speed. It is the time since the previous render
scrollPosition += inertialVelocity * INERTIA_SCROLL_FACTOR * timeDelta
inertialVelocity *= INERTIA_ACCELERATION * timeDelta
// Keep in mind that velocity can be negative as well, hence the abs
if (abs(inertialVelocity) < INERTIA_THRESHOLD)
inertialVelocity = 0