首先找模型生成一段红包雨代码,如下
@Composable fun RedPacketRain() { val redPackets = remember { mutableStateListOfRed() } val coroutineScope = rememberCoroutineScope()
scss
LaunchedEffect(Unit) {
coroutineScope.launch {
while (true) {
delay(1000)
redPackets.add(RedPacket())
}
}
}
Box(modifier = Modifier.fillMaxSize()) {
redPackets.forEachIndexed { index, redPacket ->
RedPacketItem(redPacket = redPacket, onRedPacketClicked = {
redPackets.removeAt(index)
// 处理红包点击事件
})
}
}
}
@Composable fun RedPacketItem(redPacket: RedPacket, onRedPacketClicked: () -> Unit) { val position by animateDpAsState( targetValue = redPacket.position, animationSpec = tween(durationMillis = 1000) )
ini
Image(
painter = painterResource(id = R.drawable.red_packet),
contentDescription = "Red Packet",
modifier = Modifier
.offset(y = position.dp)
.clickable { onRedPacketClicked() }
)
}
data class RedPacket(val position: Float = 0f)
遇到问题单个且动画有问题,然后进行改造
-
代码中利用Y轴进行0~1偏移,打印一直是0或者1,关键元素animateDpAsState用法通常以if true 1 else 0来达到插值器ValueAnimator过度动画效果
-
定义一个boolean控制Dp动画
var boolean by remember { mutableStateOf(false) }
-
控制boolean变化,经过各种尝试最终选择在协程中调用
LaunchedEffect(Unit) { boolean = true }
-
设置动画效果为线性
animationSpec = tween(durationMillis = 5000, easing = LinearEasing)
-
Image的Y轴偏移从屏幕起始到结束(屏幕高度*(0.0f~1.0f))
.offset( y = LocalConfiguration.current.screenHeightDp.times(position.value).dp, )
-
要想红包在X轴随机出现还需要补充X轴偏移,定义一个随机区间值,利用屏幕宽度
val width = LocalConfiguration.current.screenWidthDp.toFloat()
fun randomRegion(min: Float, max: Float): Float {return (Math.random() * (max - min + 1) + min).toFloat()}
最终代码呈现
kotlin
@Composable
fun RedPacketRain() {
var isStart by remember {
mutableStateOf(false)
}
var totalSum by remember { mutableStateOf(0) }
Column(
Modifier
.fillMaxSize()
) {
Row(
Modifier
.fillMaxWidth()
.defaultMinSize(minHeight = 40.dp)
.padding(horizontal = 12.dp)
) {
if (isStart.not()) {
Button(onClick = {
isStart = !isStart
}) {
Text(text = "开始")
}
}
Text(
text = "上次点中数量$totalSum",
Modifier
.padding(start = 12.dp)
.align(Alignment.CenterVertically)
)
}
if (isStart) {
val redPackets = remember { mutableStateListOf<RedPacket>() }
val coroutineScope = rememberCoroutineScope()
val context: Context = LocalContext.current
val width = LocalConfiguration.current.screenWidthDp.toFloat()
var total by remember { mutableStateOf(0) }
LaunchedEffect(Unit) {
coroutineScope.launch {
while (total < 100) {
delay(500)
var xx = randomRegion(width / 6, width * 5 / 6)
Log.d("TAGG", "${width},position=$xx")
redPackets.add(
RedPacket(
x = xx
)
)
total++
if (total >= 100) {
delay(5000)
isStart = false
totalSum=0
}
}
}
}
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Gray)
) {
redPackets.forEachIndexed { index, redPacket ->
RedPacketItem(redPacket = redPacket, onRedPacketClicked = {
redPacket.y = 1.0f
redPacket.isRemoved = true
// redPackets.removeAt(index)
// 处理红包点击事件
// Toast.makeText(context, "Red Pack", Toast.LENGTH_SHORT).show()
totalSum++
})
}
}
}
}
}
}
fun randomRegion(min: Float, max: Float): Float {
// Math.random().times(width - 30.dp.value).plus(30.dp.value).toFloat()
return (Math.random() * (max - min + 1) + min).toFloat()
}
@Composable
fun RedPacketItem(redPacket: RedPacket, onRedPacketClicked: () -> Unit) {
if (!redPacket.isRemoved) {
var boolean by remember {
mutableStateOf(false)
}
LaunchedEffect(Unit) {
boolean = true
}
val position by animateDpAsState(
targetValue = if (boolean) 1.dp else redPacket.y.dp,
animationSpec = tween(durationMillis = 5000, easing = LinearEasing)
)
Log.d("TAG", "position=" + position.value)
if (position.value != 0f) {
Image(
painter = painterResource(id = R.drawable.baseline_book_48),
contentDescription = "Red Packet",
modifier = Modifier
.offset(
y = LocalConfiguration.current.screenHeightDp.times(position.value).dp,
x = redPacket.x.dp
)
.clickable { onRedPacketClicked() }
)
}
}
}
data class RedPacket(var y: Float = 0f, var x: Float = 0f, var isRemoved: Boolean = false)
备注:存在问题,点击未选中,跟正常红包雨降落幅度还存在着差异,只在模拟器中测试未检测卡顿