雷电雨效果:Kotlin+Compose+协程+Flow 实现天气UI

天气效果Kotlin+Compose+协程+Flow+Channel实现,以后天气效果不是梦

一、前言

我们在开发天气App时候,常见的有效果是根据天气展示实时UI效果,下雪的时候要展示下雪的效果,雷电雨时候展示雷电雨效果,这样才能让天气展示的更加真实,贴近人民生活。

本文将会展示是如何通过 Kotlin + Compose + 协程 + Flow + Channel 实现天气UI 中的雷电雨效果,整个设计采用分层设计分离:

  1. 背景层
  2. 下雨层
  3. 闪电层。

二、背景层

背景层没有什么特别的,使用 ComposeCanvasdrawRect 方法通过颜色绘制出一块背景,它是垂直渐变效果(Brush.verticalGradient)::

less 复制代码
Canvas(Modifier.fillMaxSize()) {
    drawRect(
        brush = Brush.verticalGradient(
            colors = listOf(
                Color(0xFF1A2980),
                Color(0xFF26D0CE)
            )
        )
    )
}

三、降雨层

  1. 先设计雨滴的属性,雨滴显示的位置:X坐标,Y坐标,雨滴下降速度:speed,雨滴长度:length,雨滴透明度:alpha,雨滴下降到最大位置 maxY后肉眼看不到,类似消失,雨滴降落时受到横向风力导致左右摇摆幅 度windEffect
kotlin 复制代码
data class RainDrop(
    val x: Float,//X坐标
    val y: Float,//Y坐标
    val speed: Float, //雨滴下降速度
    val length: Float, //雨滴长度
    val alpha: Float, //雨滴透明度
    val maxY: Float, //雨滴下降到最大位置
    val windEffect: Float //横向风力导致左右摇摆幅
)
  1. 开始创建雨滴列表:intensity:代表初始雨滴密度,竖直越大,代表雨越大,通过flow 实现 每隔 60fps,实时更新雨滴列表: 3. 更新雨滴列表中的雨滴(updateRainDrops()方法):包含更新雨滴下降速度,受到风力影响的左右变化幅度,雨滴下降到最大位置 maxY后肉眼看不到后,随机创建天空新掉下来的雨滴。 4. 创建新的雨滴:createRainDrop()方法:随机生成雨滴相关属性,产生新的雨滴 相关代码如下:
scss 复制代码
class RainSystem(private val intensity: Float) {
    val drops = mutableStateListOf<RainDrop>()
 
    init {
        repeat((800 * intensity).toInt()) {
            drops.add(createRainDrop())
        }
    }

    fun startAnimation(scope: CoroutineScope) {
        scope.launch {
            flow {
                while (true) {
                    emit(Unit)
                    delay(16) // 60fps
                }
            }.collect {
                 updateRainDrops()
            }
        }
    }

    private fun updateRainDrops() {
        drops.replaceAll { drop ->
            if (drop.y > drop.maxY) createRainDrop()
            else drop.copy(
                y = drop.y + drop.speed,
                x = drop.x + drop.windEffect
            )
        }
    }

    private fun createRainDrop() = RainDrop(
        x = Random.nextFloat() * 2000 - 500,
        y = -Random.nextFloat() * 200,
        speed = 12f + Random.nextFloat() * 20 * intensity,
        length = 10f + Random.nextFloat() * 25,
        alpha = 0.3f + Random.nextFloat() * 0.7f,
        maxY = 1500f + Random.nextFloat() * 500,
        windEffect = Random.nextFloat() * 3 - 1.5f
    )
}
  1. 通过Compose Canvas方法绘制雨滴:
ini 复制代码
@Composable
fun RainLayer(
    drops: List<RainDrop>,
) {
    Canvas(Modifier.fillMaxSize()) {
        drops.forEach { drop ->
            drawLine(
                brush = Brush.verticalGradient(
                    colors = listOf(
                        Color.White.copy(alpha = drop.alpha),
                        Color.White.copy(alpha = 0f)
                    )
                ),
                start = Offset(drop.x, drop.y),
                end = Offset(drop.x, drop.y + drop.length),
                strokeWidth = 1.5f.dp.toPx()
            )
        }
    }
}

四、闪电层

  1. 闪电层数据:闪电可以看成是一条一条不规则的线,绘制出来的。所以闪电最小单元数据模型只有线条的起始位置,换成坐标就4个,开始坐标 x1,y1 结束坐标 x2,y2
kotlin 复制代码
data class LightningSegment(
    val x1: Float, val y1: Float,
    val x2: Float, val y2: Float
)
  1. 开始创建闪电数据线条集合,thunderFrequency 代表闪电密度,数值越大,闪电频率越高,isActive的作用就是随机激活展示闪电标记,只有它为true时才生效,flashAlpha闪电光透明度设置参数,然后通过flow 实现 每隔 随机时间,实时更新闪电绘制的路径: 3. 随机产生闪电绘制线条路径:generateLightningPath() 4. 闪电结束,重置闪电光透明度,所有相关代码如下:
kotlin 复制代码
class LightningSystem(private val frequency: Float) {
    var isActive by mutableStateOf(false)
    var segments by mutableStateOf(emptyList<LightningSegment>())
    var flashAlpha by mutableStateOf(0f)

    fun startRandomStrikes(scope: CoroutineScope) {
        scope.launch {
            flow {
                while (true) {
                    delay((1000..3000).random().toLong())
                    if (Random.nextFloat() < frequency) {
                        emit(Unit)
                    }
                }
            }.collect {
                triggerLightning()
            }
        }
    }

    private suspend fun triggerLightning() {
        isActive = true
        generateLightningPath()
        animateFlash()
        delay(300)
        isActive = false
    }

    private fun generateLightningPath() {
        segments = buildList {
            val startX = 300f + Random.nextFloat() * 600
            var currentY = 0f
            repeat(8) {
                add(
                    LightningSegment(
                        startX + it * 50f, currentY,
                        startX + (it + 1) * 50f + Random.nextFloat() * 100 - 50,
                        currentY + 100f + Random.nextFloat() * 50
                    )
                )
                currentY += 100f + Random.nextFloat() * 50
            }
        }
    }

    private suspend fun animateFlash() {
        flashAlpha = 0.9f
        repeat(5) {
            flashAlpha -= 0.15f
            delay(30)
        }
        flashAlpha = 0f
    }
}
  1. 绘制闪电路径:通过Compose 的 CanvasdrawRect 绘制闪电光,drawLine方法绘制闪电路径
scss 复制代码
@Composable
fun ThunderLayer(
    isActive: Boolean,
    segments: List<LightningSegment>,
    flashAlpha: Float
) {
    if (!isActive) return

    Canvas(Modifier.fillMaxSize()) {
        // 闪电闪光
        drawRect(
            color = Color.White.copy(alpha = flashAlpha * 0.6f)
        )

        // 闪电路径
        segments.forEach { seg ->
            drawLine(
                color = Color(0xFFE3F2FD),
                start = Offset(seg.x1, seg.y1),
                end = Offset(seg.x2, seg.y2),
                strokeWidth = 4f.dp.toPx(),
                cap = StrokeCap.Round
            )
        }
    }
}

五、整体雷电雨天气效果Compose

  1. 通过WeatherConfig来配置雨滴密度,闪电频率等
  2. 通过LaunchedEffect开启雨滴更新动画信息,闪电变化信息
scss 复制代码
@Composable
fun WeatherScene() {
    val scope = rememberCoroutineScope()
    val config = remember { WeatherConfig() }

    // 天气系统初始化
    val rainSystem = remember { RainSystem(config.rainIntensity) }
    val lightningSystem = remember { LightningSystem(config.thunderFrequency) }
    // 启动天气动画
    LaunchedEffect(Unit) {
        rainSystem.startAnimation(scope)
        lightningSystem.startRandomStrikes(scope)
    }

    BoxWithConstraints(Modifier.fillMaxSize()) {
        // 背景层
        SkyBackground()

        // 降雨层
        RainLayer(
            drops = rainSystem.drops
        )

        // 闪电层
        ThunderLayer(
            isActive = lightningSystem.isActive, segments = lightningSystem.segments, flashAlpha = lightningSystem.flashAlpha
        )
    }
}

六、总结

本文简单介绍了雷电雨效果:Kotlin+Compose+协程+Flow 实现的步骤:

主要通过三层介绍:

  1. 背景层设计:绘制渐变背景
  2. 降雨层设计:雨滴产生,变化,控制雨滴密度决定大雨还是小雨
  3. 闪电层设计:闪电管,闪电路径

感谢阅读:

欢迎用你发财的小手 关注,点赞、收藏

这里你会学到不一样的东西

相关推荐
爱睡觉的王宇昊1 小时前
单体架构详细解析:从概念到实践--购物网站搭建
java·spring boot·架构·团队开发·个人开发·敏捷流程
wy3136228211 小时前
android——开发中的常见Bug汇总与解决方案(闪退)
android·bug
小小测试开发2 小时前
实战派SQL性能优化:从语法层面攻克项目中的性能瓶颈
android·sql·性能优化
QuantumLeap丶3 小时前
《Flutter全栈开发实战指南:从零到高级》- 26 -持续集成与部署
android·flutter·ios
许泽宇的技术分享4 小时前
解密Anthropic的MCP Inspector:从协议调试到AI应用开发的全栈架构之旅
人工智能·架构·typescript·mcp·ai开发工具
StarShip4 小时前
从Activity.setContentView()开始
android
千里马学框架4 小时前
重学SurfaceFlinger之Layer显示区域bounds计算剖析
android·智能手机·sf·安卓framework开发·layer·surfaceflinger·车载开发
Jason_zhao_MR5 小时前
米尔RK3506核心板SDK重磅升级,解锁三核A7实时控制新架构
linux·嵌入式硬件·物联网·架构·嵌入式·嵌入式实时数据库
シ風箏5 小时前
Flink【基础知识 01】简介+核心架构+分层API+集群架构+应用场景+特点优势(一篇即可大概了解Flink)
大数据·架构·flink·bigdata
Psycho_MrZhang5 小时前
Airflow简介和架构
架构·wpf