情人节用Compose给女神写个爱心动画APP
- 前言
- 涉及知识点
- 实现思路
- 实现过程
- 绘制爱心
- 创建动画效果
- Preview预览效果
- 完整源码
- 彩蛋
前言
前一阵子看电视里的学霸用代码写了个炫酷的爱心,网上有很多js和python的源码,复制粘贴就能拥有,但是Android的好像还没有人写过。今天正好是情人节,咱们来用Compose写一个简单的爱心动画,告诉女神们,咱们程序猿也有自己的浪漫(/手动狗头)
废话不多说,直接看效果:
(源码在最后)
涉及知识点
本篇文章涉及到技术不多,也都不深,适合各方面技术入门,以下列出关键的一些:
- Jetpack Compose
- Compose动画
- Canvas自由绘制
- 三次贝塞尔曲线
实现思路
-
爱心是左右对称的,所以我们只要能实现半边,另外半边就很容易了
-
半边爱心的曲线不算太复杂,但也不简单,使用两段三阶贝塞尔曲线相连才可以达到效果,取样点可以自己草图上画一下,草图可以不用很精确,后续可以根据效果再调整参数,如下
-
绘制好一边之后,另一边就很简单,数据可以直接拿过来用,注意x轴符号取反就ok了
-
用Compose的InfiniteTransition实现大小 和 透明度 的无限循环动画
实现过程
绘制爱心
讲解都在代码注释里了,直接看代码吧
@Composable
fun HeartBeat(
modifier: Modifier = Modifier.fillMaxSize(),
color: Color = Color.Red
) {
Canvas(modifier = modifier) {
//取canvas当前画布宽高的较小值-30,防止超出边界
val minSize = min(size.height, size.width) - 30f
val path = Path()
//右半边爱心,先移动到中间心窝位置
path.moveTo(center.x, center.y - minSize / 3)
//相对位置的三阶贝塞尔曲线,从当前点连接下三个取样点
path.relativeCubicTo(
minSize / 4, -minSize / 4,
minSize / 2, 0f,
minSize / 2, minSize / 5
)
//同理,三阶贝塞尔曲线
path.relativeCubicTo(
0f, minSize / 3,
-minSize * 3 / 8, minSize * 3 / 8,
-minSize / 2, minSize * 3 / 4
)
//左半边爱心,同理,回到心窝位置开始,x轴参数取反即可
path.moveTo(center.x, center.y - minSize / 3)
path.relativeCubicTo(
-minSize / 4, -minSize / 4,
-minSize / 2, 0f,
-minSize / 2, minSize / 5
)
path.relativeCubicTo(
0f, minSize / 3,
minSize * 3 / 8, minSize * 3 / 8,
minSize / 2, minSize * 3 / 4
)
drawPath(path, color)
}
}
创建动画效果
想要实现心跳的感觉,一个是大小的变化,还有一个就是透明度,由于是无线循环动画,所以使用Compose的InfiniteTransition来实现,不太了解的同学可以后续自行补习一下Compose动画
在HeartBeat方法内,Canvas代码块之上添加如下代码:
@Composable
fun HeartBeat(
modifier: Modifier = Modifier.fillMaxSize(),
color: Color = Color.Red,
duration: Int = 600
) {
//创建InfiniteTransition
val transition = rememberInfiniteTransition()
//使用animateFloat创建透明度动画的State<Float>
val alpha by transition.animateFloat(
initialValue = 0.3f,
targetValue = 1f,
animationSpec = infiniteRepeatable(
tween(duration),
repeatMode = RepeatMode.Reverse
)
)
//同理,创建跳动大小动画的State<Float>
val beatSize by transition.animateFloat(
initialValue = 150f,
targetValue = 50f,
animationSpec = infiniteRepeatable(
tween(duration),
repeatMode = RepeatMode.Reverse
)
)
Canvas(modifier = modifier) {
//...
}
}
再改一下原来的Canvas,使用这两个动画参数
//...
Canvas(modifier = modifier) {
val minSize = min(size.height, size.width) - beatSize
//...
drawPath(path, color, alpha)
}
Preview预览效果
这一步早在开发过程中就应该添加了,用Compose的话说:边看边开发,让你更加自信
@Preview
@Composable
fun HeartBeatPre() {
HeartBeat()
}
至此就已经实现了这么一个简单的心跳动画,附上源码:
完整源码
/*
* Copyright (c) 2023.
* @username: LiePy
* @file: HeartBeat.kt
* @project: ComposeAnimationKit
* @model: ComposeAnimationKit.ComposeAnimationKit.main
* @date: 2023/2/13 下午9:44
*/
package com.lie.composeanimationkit.animation
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.graphics.Color
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.tooling.preview.Preview
import kotlin.math.min
/**
* @desc 爱心跳动
* @author LiePy
* @date 2023/2/13
*/
@Composable
fun HeartBeat(
modifier: Modifier = Modifier.fillMaxSize(),
color: Color = Color.Red,
duration: Int = 600
) {
val transition = rememberInfiniteTransition()
val alpha by transition.animateFloat(
initialValue = 0.3f,
targetValue = 1f,
animationSpec = infiniteRepeatable(
tween(duration),
repeatMode = RepeatMode.Reverse
)
)
val beatSize by transition.animateFloat(
initialValue = 150f,
targetValue = 50f,
animationSpec = infiniteRepeatable(
tween(duration),
repeatMode = RepeatMode.Reverse
)
)
Canvas(modifier = modifier) {
//最小边作为正方形
val minSize = min(size.height, size.width) - beatSize
val path = Path()
//右半边爱心
path.moveTo(center.x, center.y - minSize / 3)
path.relativeCubicTo(
minSize / 4, -minSize / 4,
minSize / 2, 0f,
minSize / 2, minSize / 5
)
path.relativeCubicTo(
0f, minSize / 3,
-minSize * 3 / 8, minSize * 3 / 8,
-minSize / 2, minSize * 3 / 4
)
//左半边爱心
path.moveTo(center.x, center.y - minSize / 3)
path.relativeCubicTo(
-minSize / 4, -minSize / 4,
-minSize / 2, 0f,
-minSize / 2, minSize / 5
)
path.relativeCubicTo(
0f, minSize / 3,
minSize * 3 / 8, minSize * 3 / 8,
minSize / 2, minSize * 3 / 4
)
drawPath(path, color, alpha)
}
}
@Preview
@Composable
fun HeartBeatPre() {
HeartBeat()
}
彩蛋
本动画已收录至我的 git 开源库项目,持续更新中。。。
GitHub: ComposeAnimationKit
Gitee: ComposeAnimationKit
导入使用ComposeAnimationKit,更多好玩的动画等你发现,
implementation 'io.github.LiePy:ComposeAnimationKit:1.1.2'