Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Avoid MotionController recreation #571

Merged
merged 12 commits into from
Sep 9, 2023
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
import com.bumble.appyx.components.experimental.promoter.PromoterModel
import com.bumble.appyx.components.experimental.promoter.PromoterModel.State.ElementState
import com.bumble.appyx.interactions.core.ui.context.TransitionBounds
import com.bumble.appyx.interactions.core.ui.context.UiContext
import com.bumble.appyx.interactions.core.ui.helper.DefaultAnimationSpec
import com.bumble.appyx.interactions.core.ui.property.impl.AngularPosition
Expand All @@ -28,8 +29,11 @@ class PromoterMotionController<InteractionTarget : Any>(
uiContext = uiContext,
defaultAnimationSpec = uiAnimationSpec,
) {
override fun PromoterModel.State<InteractionTarget>.toUiTargets(
): List<MatchedTargetUiState<InteractionTarget, TargetUiState>> =
init {
createTargetUiStates(0f)
}

override fun PromoterModel.State<InteractionTarget>.toUiTargets(): List<MatchedTargetUiState<InteractionTarget, TargetUiState>> =
Fixed Show fixed Hide fixed
elements.map {
MatchedTargetUiState(
it.first, when (it.second) {
Expand All @@ -50,101 +54,116 @@ class PromoterMotionController<InteractionTarget : Any>(
): MutableUiState =
targetUiState.toMutableState(uiContext)

private val halfWidthDp = uiContext.transitionBounds.widthDp.value / 2
private val halfHeightDp = uiContext.transitionBounds.heightDp.value / 2
@Suppress("MaxLineLength", "UnusedPrivateMember")
private val center = DpOffset(halfWidthDp.dp, halfHeightDp.dp) - DpOffset((childSize.value / 2).dp, (childSize.value / 2).dp)
private val radius = min(halfWidthDp, halfHeightDp) * 0.8f
override fun updateBounds(transitionBounds: TransitionBounds) {
super.updateBounds(transitionBounds)
val halfWidthDp = transitionBounds.widthDp.value / 2
val halfHeightDp = transitionBounds.heightDp.value / 2
val radius = min(halfWidthDp, halfHeightDp) * 0.8f
createTargetUiStates(radius)
}

private val created = TargetUiState(
position = PositionInside.Target(alignment = Center),
angularPosition = AngularPosition.Target(
AngularPosition.Value(
radius = radius,
angleDegrees = 0f
)
),
scale = Scale.Target(0f),
rotationY = RotationY.Target(0f),
rotationZ = RotationZ.Target(0f),
)
private lateinit var created: TargetUiState
private lateinit var stage1: TargetUiState
private lateinit var stage2: TargetUiState
private lateinit var stage3: TargetUiState
private lateinit var stage4: TargetUiState
private lateinit var stage5: TargetUiState
private lateinit var destroyed: TargetUiState

private val stage1 = TargetUiState(
position = PositionInside.Target(alignment = Center),
angularPosition = AngularPosition.Target(
AngularPosition.Value(
radius = radius,
angleDegrees = 0f
)
),
scale = Scale.Target(0.25f),
rotationY = RotationY.Target(0f),
rotationZ = RotationZ.Target(0f),
fun createTargetUiStates(radius: Float) {
Fixed Show fixed Hide fixed
created = TargetUiState(
position = PositionInside.Target(alignment = Center),
angularPosition = AngularPosition.Target(
AngularPosition.Value(
radius = radius,
angleDegrees = 0f
)
),
scale = Scale.Target(0f),
rotationY = RotationY.Target(0f),
rotationZ = RotationZ.Target(0f),
)

stage1 = TargetUiState(
position = PositionInside.Target(alignment = Center),
angularPosition = AngularPosition.Target(
AngularPosition.Value(
radius = radius,
angleDegrees = 0f
)
),
scale = Scale.Target(0.25f),
rotationY = RotationY.Target(0f),
rotationZ = RotationZ.Target(0f)
)

private val stage2 = TargetUiState(
position = PositionInside.Target(alignment = Center),
angularPosition = AngularPosition.Target(
AngularPosition.Value(
radius = radius,
angleDegrees = 90f
)
),
scale = Scale.Target(0.45f),
rotationY = RotationY.Target(0f),
rotationZ = RotationZ.Target(0f),
)
stage2 = TargetUiState(
position = PositionInside.Target(alignment = Center),
angularPosition = AngularPosition.Target(
AngularPosition.Value(
radius = radius,
angleDegrees = 90f
)
),
scale = Scale.Target(0.45f),
rotationY = RotationY.Target(0f),
rotationZ = RotationZ.Target(0f),
)

private val stage3 = TargetUiState(
position = PositionInside.Target(alignment = Center),
angularPosition = AngularPosition.Target(
AngularPosition.Value(
radius = radius,
angleDegrees = 180f
)
),
scale = Scale.Target(0.65f),
rotationY = RotationY.Target(0f),
rotationZ = RotationZ.Target(0f),
)
stage3 = TargetUiState(
position = PositionInside.Target(alignment = Center),
angularPosition = AngularPosition.Target(
AngularPosition.Value(
radius = radius,
angleDegrees = 180f
)
),
scale = Scale.Target(0.65f),
rotationY = RotationY.Target(0f),
rotationZ = RotationZ.Target(0f),
)

private val stage4 = TargetUiState(
position = PositionInside.Target(alignment = Center),
angularPosition = AngularPosition.Target(
AngularPosition.Value(
radius = radius,
angleDegrees = 270f
)
),
scale = Scale.Target(0.85f),
rotationY = RotationY.Target(0f),
rotationZ = RotationZ.Target(0f),
)
stage4 = TargetUiState(
position = PositionInside.Target(alignment = Center),
angularPosition = AngularPosition.Target(
AngularPosition.Value(
radius = radius,
angleDegrees = 270f
)
),
scale = Scale.Target(0.85f),
rotationY = RotationY.Target(0f),
rotationZ = RotationZ.Target(0f),
)

private val stage5 = TargetUiState(
position = PositionInside.Target(alignment = Center),
angularPosition = AngularPosition.Target(
AngularPosition.Value(
radius = 0f,
angleDegrees = 270f
)
),
scale = Scale.Target(1f),
rotationY = RotationY.Target(360f),
rotationZ = RotationZ.Target(0f),
)
stage5 = TargetUiState(
position = PositionInside.Target(alignment = Center),
angularPosition = AngularPosition.Target(
AngularPosition.Value(
radius = 0f,
angleDegrees = 270f
)
),
scale = Scale.Target(1f),
rotationY = RotationY.Target(360f),
rotationZ = RotationZ.Target(0f),
)

private val destroyed = TargetUiState(
position = PositionInside.Target(alignment = Center, offset = DpOffset(500.dp, (-200).dp)),
angularPosition = AngularPosition.Target(
AngularPosition.Value(
radius = radius,
angleDegrees = 0f
)
),
scale = Scale.Target(0f),
rotationY = RotationY.Target(360f),
rotationZ = RotationZ.Target(540f),
)
destroyed = TargetUiState(
position = PositionInside.Target(
alignment = Center,
offset = DpOffset(500.dp, (-200).dp)
),
angularPosition = AngularPosition.Target(
AngularPosition.Value(
radius = radius,
angleDegrees = 0f
)
),
scale = Scale.Target(0f),
rotationY = RotationY.Target(360f),
rotationZ = RotationZ.Target(540f),
)
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -27,28 +27,23 @@ class BackStackParallax<InteractionTarget : Any>(
uiContext = uiContext,
defaultAnimationSpec = defaultAnimationSpec,
) {
private val width = uiContext.transitionBounds.widthDp.value
private val slowOutFastInEasing = CubicBezierEasing(1f, 0f, 1f, 0f)

private val left = TargetUiState(
elementWidth = width,
offsetMultiplier = -1f,
alpha = Alpha.Target(0f),
)
private val right = TargetUiState(
elementWidth = width,
offsetMultiplier = 1f,
shadow = Shadow.Target(value = 0f, easing = slowOutFastInEasing),
)

private val bottom = TargetUiState(
elementWidth = width,
offsetMultiplier = -0.2f,
colorOverlay = ColorOverlay.Target(0.7f),
)

private val top = TargetUiState(
elementWidth = width,
offsetMultiplier = 0f,
shadow = Shadow.Target(25f),
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ class TargetUiState(
) {

constructor(
elementWidth: Float,
offsetMultiplier: Float,
colorOverlay: ColorOverlay.Target = ColorOverlay.Target(0f),
shadow: Shadow.Target = Shadow.Target(0f),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,23 @@ class BackStackSlider<InteractionTarget : Any>(
override fun BackStackModel.State<InteractionTarget>.toUiTargets(
): List<MatchedTargetUiState<InteractionTarget, TargetUiState>> =
created.map { MatchedTargetUiState(it, visible.toOutsideRight()) } +
listOf(active).map { MatchedTargetUiState(it, visible.toNoOffset() ) } +
stashed.mapIndexed { index, element ->
MatchedTargetUiState(
element,
visible.toOutsideLeft()
)
} +
destroyed.mapIndexed { index, element ->
MatchedTargetUiState(
element,
fadeOut.toOutsideRight()
)
}
listOf(active).map { MatchedTargetUiState(it, visible.toNoOffset()) } +
stashed.mapIndexed { index, element ->
MatchedTargetUiState(
element,
visible.toOutsideLeft((index - stashed.size).toFloat())
)
} +
destroyed.mapIndexed { index, element ->
MatchedTargetUiState(
element,
fadeOut.toOutsideRight()
)
}

override fun mutableUiStateFor(uiContext: UiContext, targetUiState: TargetUiState): MutableUiState =
override fun mutableUiStateFor(
uiContext: UiContext,
targetUiState: TargetUiState
): MutableUiState =
targetUiState.toMutableState(uiContext)
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,13 @@ class TargetUiState(
val position: PositionOutside.Target,
val alpha: Alpha.Target,
) {
fun toOutsideLeft() = TargetUiState(
position = PositionOutside.Target(alignment = BiasAlignment.OutsideAlignment.OutsideLeft),
fun toOutsideLeft(multiplier: Float) = TargetUiState(
position = PositionOutside.Target(
alignment = BiasAlignment.OutsideAlignment(
horizontalBias = multiplier,
verticalBias = 0f
)
),
alpha = alpha
)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.bumble.appyx.components.backstack.ui.stack3d
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.TransformOrigin
import androidx.compose.ui.unit.Density
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.times
Expand All @@ -29,7 +30,14 @@ class BackStack3D<InteractionTarget : Any>(
) : BaseMotionController<InteractionTarget, State<InteractionTarget>, MutableUiState, TargetUiState>(
uiContext = uiContext,
) {
private val height = uiContext.transitionBounds.heightDp
private var width: Dp = 0.dp
private var height: Dp = 0.dp

override fun updateBounds(transitionBounds: TransitionBounds) {
super.updateBounds(transitionBounds)
width = transitionBounds.widthDp
height = transitionBounds.heightDp
}

private val topMost: TargetUiState =
TargetUiState(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package com.bumble.appyx.components.spotlight.ui.stack3d

import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import com.bumble.appyx.components.spotlight.SpotlightModel.State
import com.bumble.appyx.components.spotlight.SpotlightModel.State.ElementState.CREATED
import com.bumble.appyx.components.spotlight.SpotlightModel.State.ElementState.DESTROYED
import com.bumble.appyx.components.spotlight.SpotlightModel.State.ElementState.STANDARD
import com.bumble.appyx.interactions.core.ui.context.TransitionBounds
import com.bumble.appyx.interactions.core.ui.context.UiContext
import com.bumble.appyx.interactions.core.ui.property.impl.Alpha
import com.bumble.appyx.interactions.core.ui.property.impl.GenericFloatProperty
Expand All @@ -25,7 +27,14 @@ class SpotlightStack3D<InteractionTarget : Any>(
) : BaseMotionController<InteractionTarget, State<InteractionTarget>, MutableUiState, TargetUiState>(
uiContext = uiContext,
) {
private val height: Dp = uiContext.transitionBounds.heightDp
private var width: Dp = 0.dp
private var height: Dp = 0.dp

override fun updateBounds(transitionBounds: TransitionBounds) {
super.updateBounds(transitionBounds)
width = transitionBounds.widthDp
height = transitionBounds.heightDp
}

private val scrollY = GenericFloatProperty(uiContext.coroutineScope, GenericFloatProperty.Target(0f))
override val viewpointDimensions: List<Pair<(State<InteractionTarget>) -> Float, GenericFloatProperty>> =
Expand Down
Loading