Created
May 25, 2021 19:02
-
-
Save dzfranklin/4c4a36a5f6eff306b28bd4d6a310bf89 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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