1
0
mirror of https://github.com/AllanWang/Frost-for-Facebook.git synced 2024-11-09 20:42:34 +01:00

Add shake effect

This commit is contained in:
Allan Wang 2023-06-21 14:53:16 -07:00
parent d0ed236ea6
commit 1dab81ee38
No known key found for this signature in database
GPG Key ID: C93E3F9C679D7A56
2 changed files with 108 additions and 2 deletions

View File

@ -0,0 +1,90 @@
/*
* Copyright 2023 Allan Wang
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.pitchedapps.frost.compose.effects
import androidx.compose.animation.core.animate
import androidx.compose.animation.core.spring
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableFloatStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.rotate
import androidx.compose.ui.platform.debugInspectorInfo
import androidx.compose.ui.platform.inspectable
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.cancelChildren
import kotlinx.coroutines.launch
/**
* State for tracking shaking animation
*
* Note: Used some other material states as reference. This however will recompose the function
* holding the state during shakes.
*/
@Composable
fun rememberShakeState(): ShakeState {
val scope = rememberCoroutineScope()
val state = remember(scope) { ShakeState(scope) }
return state
}
class ShakeState
internal constructor(
private val animationScope: CoroutineScope,
) {
private val job = SupervisorJob()
private var _rotation by mutableFloatStateOf(0f)
internal val rotation
get() = _rotation
fun shake() {
job.cancelChildren()
animationScope.launch(job) {
animate(
_rotation,
0f,
initialVelocity = 200f,
animationSpec =
spring(
dampingRatio = 0.3f,
stiffness = 200f,
),
) { value, _ ->
_rotation = value
}
}
}
}
fun Modifier.shake(state: ShakeState, enabled: Boolean = true) =
inspectable(
inspectorInfo =
debugInspectorInfo {
name = "shake"
properties["enabled"] = enabled
},
) {
Modifier.rotate(state.rotation)
}

View File

@ -16,9 +16,12 @@
*/ */
package com.pitchedapps.frost.tabselector package com.pitchedapps.frost.tabselector
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.statusBarsPadding import androidx.compose.foundation.layout.statusBarsPadding
import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
@ -37,6 +40,8 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.pitchedapps.frost.compose.effects.rememberShakeState
import com.pitchedapps.frost.compose.effects.shake
import com.pitchedapps.frost.facebook.FbItem import com.pitchedapps.frost.facebook.FbItem
import com.pitchedapps.frost.facebook.tab import com.pitchedapps.frost.facebook.tab
@ -63,6 +68,7 @@ fun TabSelectorScreen(modifier: Modifier = Modifier) {
) )
} }
@OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
fun TabSelector( fun TabSelector(
modifier: Modifier, modifier: Modifier,
@ -74,7 +80,17 @@ fun TabSelector(
modifier = modifier, modifier = modifier,
columns = GridCells.Fixed(4), columns = GridCells.Fixed(4),
) { ) {
items(unselected, key = { it.key }) { TabItem(data = it) } items(unselected, key = { it.key }) {
val shakeState = rememberShakeState()
TabItem(
modifier =
Modifier.animateItemPlacement().shake(shakeState).clickable {
shakeState.shake()
// onSelect(listOf(it))
},
data = it,
)
}
} }
} }
@ -88,7 +104,7 @@ fun TabItem(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
) { ) {
Icon( Icon(
modifier = Modifier.padding(4.dp), modifier = Modifier.padding(4.dp).size(24.dp),
imageVector = data.icon, imageVector = data.icon,
contentDescription = data.title, contentDescription = data.title,
) )