Olivier Patry
04/18/2021, 11:31 AMOlivier Patry
04/18/2021, 11:33 AMOlivier Patry
04/18/2021, 11:35 AMval anim = remember {
TargetBasedAnimation(
animationSpec = tween(200),
typeConverter = Float.VectorConverter,
initialValue = 0f,
targetValue = 360f
)
}
var playTime by remember { mutableStateOf(0L) }
// FIXME only if both previous and current tile were non empty tiles
LaunchedEffect(tile) {
val startTime = withFrameNanos { it }
do {
playTime = withFrameNanos { it } - startTime
val animationValue = anim.getValueFromNanos(playTime)
} while (!anim.isFinishedFromNanos(playTime))
}
...
Modifier.rotate(anim.getValueFromNanos(playTime))
It "kinda" work but isn't really accurate.
Despite using tile.id
(a unique ID associated to each tile), too many tiles are animated.Olivier Patry
04/18/2021, 11:35 AMDoris Liu
04/19/2021, 6:35 PMand I still don't understand how to apply a tile offset transition from previous state to currentGenerally speaking, this can be achieved by creating a stateful animation (e.g. animate*AsState, Animatable) to store the value calculated from the previous state, so that when state changes the new value can be used as the new target for the stateful animation.
Olivier Patry
04/20/2021, 7:46 AManimate*AsState
, my understanding is that a link is made between the animation spec and the animated entity, thus automatically managing the "from previous to current state" logic.
Here, I'm stuck because the animation logic doesn't seem that easy to express.Olivier Patry
04/20/2021, 7:49 AM@Composable
fun TilesGrid(
tiles: List<Tile>,
size: IntSize) {
LazyVerticalGrid(GridCells.Fixed(size.width)) {
items(tiles) { tile ->
TileView(tile)
}
}
}
The tile:
@Composable
fun TileView(tile: Tile) {
Box {
// only draw tile slot for empty tiles
if (tile != Tile.EMPTY_TILE) {
val color by animateColorAsState(tile.color)
Box(
Modifier
.padding(bottom = 8.dp, start = 2.dp, end = 2.dp)
.background(color, RoundedCornerShape(4.dp))
.fillMaxSize()
.padding(bottom = 4.dp),
contentAlignment = Alignment.BottomCenter
) {
Text(tile.value.toString())
}
}
}
}
Olivier Patry
04/20/2021, 7:51 AMTile
class is
data class Tile(val rank: Int) : Comparable<Tile> {
val id: Long = ID++
val value: Int = 2.0.pow(rank).toInt()
override fun compareTo(other: Tile): Int {
return rank - other.rank
}
companion object {
private var ID: Long = 0L
val EMPTY_TILE = Tile(0)
}
}
Olivier Patry
04/20/2021, 12:21 PMval tiles1 = listOf(
Tile.EMPTY_TILE, Tile(3), Tile.EMPTY_TILE, Tile.EMPTY_TILE,
Tile.EMPTY_TILE, Tile(2), Tile.EMPTY_TILE, Tile(2),
Tile.EMPTY_TILE, Tile(2), Tile(3), Tile.EMPTY_TILE,
Tile.EMPTY_TILE, Tile.EMPTY_TILE, Tile(2), Tile(1),
)
val tiles2 = listOf(
Tile.EMPTY_TILE, tiles1[1], Tile.EMPTY_TILE, tiles1[7],
Tile.EMPTY_TILE, Tile(3), tiles1[10], Tile.EMPTY_TILE,
Tile.EMPTY_TILE, Tile.EMPTY_TILE, tiles1[14], tiles1[15],
Tile.EMPTY_TILE, Tile(1), Tile.EMPTY_TILE, Tile.EMPTY_TILE,
)
@ExperimentalFoundationApi
@Composable
fun Playground() {
var state by remember { mutableStateOf(true) }
val tiles = if (state) tiles1 else tiles2
TilesGrid2(tiles, IntSize(4, 4)) {
state = !state
}
}
Olivier Patry
04/20/2021, 12:22 PMOlivier Patry
04/20/2021, 12:23 PMval scale = remember(tile.id) { Animatable(0f) }
LaunchedEffect(tile.id) {
scale.animateTo(
targetValue = 1f,
animationSpec = spring(
dampingRatio = Spring.DampingRatioMediumBouncy,
stiffness = Spring.StiffnessMedium,
visibilityThreshold = .3f
)
)
}
And applying such scale to tileOlivier Patry
04/20/2021, 12:27 PMOlivier Patry
04/20/2021, 12:27 PMOlivier Patry
04/20/2021, 1:05 PMtile.id
same position) do not animate (which is expected).
New ones (new or result of a merge) are animated (which is expected, new tile.id
).
But tiles that are simply moving from one cell to another are animated which isn't expected. The tile.id
being the same, I would have expected stable state.
I guess this comes from the fact that such TileView
composable is managed by LazyVerticalGrid
?Doris Liu
04/21/2021, 2:15 AMModifier.offset
or place(...)
.Olivier Patry
04/21/2021, 6:32 AMmap
my tiles 2D board to a list of TileState
with the board position in a mutable state.
I'm not sure about the details yet but you gave me a good lead on where to dig!
ThanksOlivier Patry
04/22/2021, 8:22 PMTile
.
I store the last update made on each tile (new, none, combination, top, bottom, left, right). When I update my board, I can determine such new state for each tile.
This way, I can start playing with animation depending on the update state and distinguish the different cases.
I have remaining issues related to origin of animations (when dealing with rotate + offset) but I'll keep going. I think I have stronger foundation to complete my goal now π
Thanks πDoris Liu
04/22/2021, 8:25 PMOlivier Patry
04/22/2021, 8:28 PMLazyVerticalGrid
.
Maybe I'll have to define a TileState
containing my tile data, animation state and expected position in grid.
Then draw list of TileState
given such position and defined animation states.
I could address the issue I currently have with animation combination.Doris Liu
04/22/2021, 8:31 PMOlivier Patry
04/22/2021, 8:35 PMLazyRow
, for vertical list LazyColumn
, for grid, LazyVerticalGrid
πOlivier Patry
04/22/2021, 9:30 PMOlivier Patry
04/22/2021, 9:31 PMDoris Liu
04/22/2021, 10:06 PM