Skip to content

Instantly share code, notes, and snippets.

@dzfranklin
Created May 25, 2021 19:02
Show Gist options
  • Save dzfranklin/4c4a36a5f6eff306b28bd4d6a310bf89 to your computer and use it in GitHub Desktop.
Save dzfranklin/4c4a36a5f6eff306b28bd4d6a310bf89 to your computer and use it in GitHub Desktop.
private class SectionsAnimationState constructor(
initialPosition: PagePosition,
private val renderer: Renderer,
private val onPosition: (PaginatedTextPosition) -> Unit,
) {
private val _positionBacking = mutableStateOf(initialPosition)
private var lastPageReportedToOnPosition = initialPosition.page.roundToInt()
private var _position
get() = _positionBacking.value
set(value) {
_positionBacking.value = value
val page = floor(value.page).toInt()
if (page != lastPageReportedToOnPosition) {
val char = renderer[value.section]!!.pages[page].startChar
onPosition(PaginatedTextPosition(value.section, char))
lastPageReportedToOnPosition = page
}
}
val position: State<PagePosition> = _positionBacking
private val _isAnimating = mutableStateOf(false)
val isAnimating: State<Boolean> = _isAnimating
/** Returns unused delta */
fun jumpBy(delta: Float): Float {
val sectionMax = renderer[position.value.section]!!.lastPage
val start = position.value.page
val sectionDelta = if (_position.section < renderer.maxSection) {
delta.coerceIn(-start, (sectionMax + NEARLY_ONE) - start)
} else {
delta.coerceIn(-start, sectionMax - start)
}
val remainingDelta = delta - sectionDelta
_position = _position.copy(page = start + sectionDelta)
return when {
remainingDelta > 0 -> {
val newSection = _position.section + 1
if (newSection > renderer.maxSection) {
return remainingDelta
}
_position = PagePosition(newSection, 0f)
jumpBy(remainingDelta)
}
remainingDelta < 0 -> {
val newSection = _position.section - 1
if (newSection < 0) {
return remainingDelta
}
_position =
PagePosition(newSection, renderer[newSection]!!.lastPage.toFloat() + NEARLY_ONE)
jumpBy(remainingDelta)
}
else -> 0f
}
}
suspend fun animateBy(
delta: Float,
spec: AnimationSpec<Float> = spring(stiffness = 100f),
settleAfter: Boolean = true
) {
_isAnimating.value = true
var prev = 0f
animate(0f, delta, animationSpec = spec) { value, _ ->
jumpBy(value - prev)
prev = value
}
if (settleAfter) settle()
_isAnimating.value = false
}
suspend fun animateToNearest() {
val delta = round(_position.page) - _position.page
animateBy(delta, spring(stiffness = Spring.StiffnessLow))
}
private suspend fun settle() {
// Round up if we are very close to a page
val gap = _position.page.roundToInt() - _position.page
if (abs(gap) < SETTLE_WITHIN) {
animateBy(gap, settleAfter = false)
_position = _position.copy(page = round(_position.page))
}
}
companion object {
private const val NEARLY_ONE = 0.9999f
private const val SETTLE_WITHIN = 0.05f
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment