雷电雨效果: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. 闪电层设计:闪电管,闪电路径

感谢阅读:

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

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

相关推荐
DashVector24 分钟前
如何通过Java SDK检索Doc
后端·算法·架构
用户20187928316736 分钟前
Binder 同应用内(本地)通信是否存在 1MB 大小限制?
android
一条上岸小咸鱼1 小时前
Kotlin 基本数据类型(四):String
android·前端·kotlin
Onion_992 小时前
学习下Github上的Android CICD吧
android·github
吴尊和2 小时前
将indextts封装到docker容器中
架构
我星期八休息2 小时前
Agent开发进阶路线:从基础响应到自主决策的架构演进
架构
来来走走2 小时前
Flutter Form组件的基本使用
android·flutter
顾林海3 小时前
Android MMKV 深度解析:原理、实践与源码剖析
android·面试·源码阅读
雨白3 小时前
TCP/IP 核心概念详解:从网络分层到连接管理
android