You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetpack Compose实现类似Pulsator4Droid的流动脉冲动画需求

Jetpack Compose实现Pulsator4Droid风格流动脉冲动画

现有代码

当前实现仅让圆形小幅缩放,无法达到向外扩散淡出的脉冲效果:

@Composable
fun PulseView() {
    val infiniteTransition = rememberInfiniteTransition()
    val smallCircleSizeInPx = LocalDensity.current.run {
        6.dp.toPx()
    }
    val mediumCircleSize by infiniteTransition.animateValue(
        initialValue = 13.dp,
        targetValue = 11.dp,
        Dp.VectorConverter,
        animationSpec = infiniteRepeatable(
            animation = tween(500),
            repeatMode = RepeatMode.Reverse
        )
    )
    val largeCircleSize by infiniteTransition.animateValue(
        initialValue = 18.dp,
        targetValue = 16.dp,
        Dp.VectorConverter,
        animationSpec = infiniteRepeatable(
            animation = tween(500),
            repeatMode = RepeatMode.Reverse
        )
    )
    val middleCircleSizeInPx = LocalDensity.current.run { mediumCircleSize.toPx() }
    val largeCircleSizeInPx = LocalDensity.current.run { largeCircleSize.toPx() }
    val strokeWidth = LocalDensity.current.run { 2.dp.toPx() }
    val brush = Brush.linearGradient(listOf(NeutralsCloudy, NeutralsCloudy))
    PulseViewContent(
        smallCircleSizeInPx,
        middleCircleSizeInPx,
        largeCircleSizeInPx,
        strokeWidth,
        brush,
    )
}

@Composable
fun PulseViewContent(smallCircleSizeInPx: Float, middleCircleSizeInPx: Float, largeCircleSizeInPx: Float, strokeWidth: Float, brush: Brush) {
    
    Canvas(modifier = Modifier.fillMaxHeight()) {
        drawIntoCanvas {
            drawLine(
                brush = brush,
                start = Offset(x = size.width / 2, y = 0F),
                end = Offset(x = size.width / 2, y = size.height / 2),
                strokeWidth = strokeWidth,
            )
            drawLine(
                brush = brush,
                start = Offset(x = size.width / 2, y = size.height / 2),
                end = Offset(x = size.width / 2, y = size.height),
                strokeWidth = strokeWidth,
            )
            drawCircle(
                color = SeaGreen.copy(alpha = 0.32f),
                radius = largeCircleSizeInPx,
                center = Offset(size.width, size.height / 2)
            )
            drawCircle(
                color = SeaGreen.copy(alpha = 0.32f),
                radius = middleCircleSizeInPx,
                center = Offset(size.width, size.height / 2)
            )
            drawCircle(
                color = SeaGreen,
                radius = smallCircleSizeInPx,
                center = Offset(size.width, size.height / 2)
            )
        }
    }
}

效果说明

  • 实际效果:几个圆形在固定大小范围内小幅往复缩放,无流动扩散感
  • 预期效果:多个圆形从中心向外逐渐扩散,同时透明度递减,循环产生连续的脉冲流动效果

修改后的实现方案

以下代码实现了类似Pulsator4Droid的流动脉冲效果,核心是让多个脉冲层按不同延迟启动,同时进行缩放和透明度动画:

import androidx.compose.animation.core.*
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp

// 自定义颜色,替换为你的颜色值
val SeaGreen = Color(0xFF2ECC71)
val NeutralsCloudy = Color(0xFFBDC3C7)

@Composable
fun PulsatorView() {
    val pulseCount = 3 // 脉冲层数
    val pulseDuration = 1500 // 单个脉冲动画时长(毫秒)
    val maxPulseRadius = 30.dp // 脉冲最大扩散半径

    Canvas(modifier = Modifier.fillMaxSize()) {
        val center = Offset(size.width, size.height / 2)
        val maxRadiusPx = maxPulseRadius.toPx()
        val smallCircleRadius = 6.dp.toPx()

        // 绘制中心固定小圆圈
        drawCircle(
            color = SeaGreen,
            radius = smallCircleRadius,
            center = center
        )

        // 绘制竖线
        val strokeWidth = 2.dp.toPx()
        drawLine(
            color = NeutralsCloudy,
            start = Offset(x = size.width / 2, y = 0f),
            end = Offset(x = size.width / 2, y = size.height),
            strokeWidth = strokeWidth
        )

        // 绘制多个脉冲层
        repeat(pulseCount) { index ->
            val delay = (index * pulseDuration / pulseCount).toInt()
            val infiniteTransition = rememberInfiniteTransition(label = "pulse_$index")

            // 缩放动画:从0到1(对应半径从0到maxRadiusPx)
            val scale by infiniteTransition.animateFloat(
                initialValue = 0f,
                targetValue = 1f,
                animationSpec = infiniteRepeatable(
                    animation = tween(pulseDuration, delayMillis = delay),
                    repeatMode = RepeatMode.Restart
                ),
                label = "scale_$index"
            )

            // 透明度动画:从1到0
            val alpha by infiniteTransition.animateFloat(
                initialValue = 1f,
                targetValue = 0f,
                animationSpec = infiniteRepeatable(
                    animation = tween(pulseDuration, delayMillis = delay),
                    repeatMode = RepeatMode.Restart
                ),
                label = "alpha_$index"
            )

            drawCircle(
                color = SeaGreen.copy(alpha = alpha),
                radius = maxRadiusPx * scale,
                center = center
            )
        }
    }
}

关键实现细节

  1. 多脉冲层错开启动:通过给每个脉冲层设置不同的delayMillis,让脉冲依次向外扩散,形成连续流动的效果
  2. 缩放+透明度联动动画:每个脉冲从0半径开始放大到最大半径,同时透明度从1降到0,模拟扩散淡出的视觉效果
  3. 简化Canvas绘制:将所有绘制逻辑整合到一个Canvas中,减少组件层级,提升性能
  4. 可配置参数:通过pulseCount、pulseDuration、maxPulseRadius可以灵活调整脉冲的数量、速度和大小

内容的提问来源于stack exchange,提问作者Compose Learner

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 11:17:53