前言
阅读本文需要一定compose基础,如果没有请移步Jetpack Compose入门详解(实时更新)
在网上看到有人用css写出了下面这种效果;原文链接
我看到了觉得很好玩,于是用Compose撸了一个随手势移动眼珠的版本。
一、Canvas画图
这种笑脸常用的控件肯定实现不了,我们只能用Canvas自己画了
笑脸
我们先画脸
下例当中的size和center都是onDraw 的DrawScope提供的属性,drawCircle则是DrawScope提供的画圆的方法
kotlin
Canvas(modifier = modifier
.size(300.dp),
onDraw = {
// 脸
drawCircle(
color = Color(0xfffecd00),
radius = size.width / 2,
center = center
)
})
属性解释
- color:填充颜色
- radius: 半径
- center: 圆心坐标
这里我们半径取屏幕宽度一半,圆心取屏幕中心,画出来的脸效果如下
微笑
微笑是一个弧形,我们使用drawArc来画微笑
kotlin
// 微笑
val smilePadding = size.width / 4
drawArc(
color = Color(0xffb57700),
startAngle = 0f,
sweepAngle = 180f,
useCenter = true,
topLeft = Offset(smilePadding, size.height / 2 - smilePadding / 2),
size = Size(size.width / 2, size.height / 4)
)
属性解释
- color:填充颜色
- startAngle: 弧形开始的角度,默认以3点钟方向为0度
- sweepAngle:弧形结束的角度,默认以3点钟方向为0度
- useCenter :指示圆弧是否要闭合边界中心的标志(上例加不加都无所谓)
- topLeft :相对于当前平移从0的本地原点偏移,0开始
- size:要绘制的圆弧的尺寸
效果如下
眼睛和眼珠子
眼睛也是drawCircle方法,只是位置不同,这边就不再多做解释
kotlin
// 左眼
drawCircle(
color = Color.White,
center = Offset(smilePadding, size.height / 2 - smilePadding / 2),
radius = smilePadding / 2
)
//左眼珠子
drawCircle(
color = Color.Black,
center = Offset(smilePadding, size.height / 2 - smilePadding / 2),
radius = smilePadding / 4
)
// 右眼
drawCircle(
color = Color.White,
center = Offset(smilePadding * 3, size.height / 2 - smilePadding / 2),
radius = smilePadding / 2
)
//右眼珠子
drawCircle(
color = Color.Black,
center = Offset(smilePadding * 3, size.height / 2 - smilePadding / 2),
radius = smilePadding / 4
)
整个笑脸就画出来了,效果如下
二、跟随手势移动
在实现功能之前我们需要介绍transformable ,animateFloatAsState 和translate
transformable
transformable 是modifier用于平移、缩放和旋转的多点触控手势的修饰符,此修饰符本身不会转换元素,只会检测手势。
animateFloatAsState
animateFloatAsState 是通过Float状态变化来控制动画 的状态
知道了这两个玩意过后我们就可以先通过transformable 监听手势滑动然后通过translate 方法和animateFloatAsState方法组成一个平移动画来实现眼珠跟随手势移动
完整的代码:
kotlin
@Composable
fun SmileyFaceCanvas(
modifier: Modifier
) {
var x by remember {
mutableStateOf(0f)
}
var y by remember {
mutableStateOf(0f)
}
val state = rememberTransformableState { _, offsetChange, _ ->
x = offsetChange.x
if (offsetChange.x >50f){
x = 50f
}
if (offsetChange.x < -50f){
x=-50f
}
y = offsetChange.y
if (offsetChange.y >50f){
y= 50f
}
if (offsetChange.y < -50f){
y=-50f
}
}
val animTranslateX by animateFloatAsState(
targetValue = x,
animationSpec = TweenSpec(1000)
)
val animTranslateY by animateFloatAsState(
targetValue = y,
animationSpec = TweenSpec(1000)
)
Canvas(
modifier = modifier
.size(300.dp)
.transformable(state = state)
) {
// 脸
drawCircle(
Color(0xfffecd00),
radius = size.width / 2,
center = center
)
// 微笑
val smilePadding = size.width / 4
drawArc(
color = Color(0xffb57700),
startAngle = 0f,
sweepAngle = 180f,
useCenter = true,
topLeft = Offset(smilePadding, size.height / 2 - smilePadding / 2),
size = Size(size.width / 2, size.height / 4)
)
// 左眼
drawCircle(
color = Color.White,
center = Offset(smilePadding, size.height / 2 - smilePadding / 2),
radius = smilePadding / 2
)
translate(left = animTranslateX, top = animTranslateY) {
//左眼珠子
drawCircle(
color = Color.Black,
center = Offset(smilePadding, size.height / 2 - smilePadding / 2),
radius = smilePadding / 4
)
}
// 右眼
drawCircle(
color = Color.White,
center = Offset(smilePadding * 3, size.height / 2 - smilePadding / 2),
radius = smilePadding / 2
)
translate(left = animTranslateX, top = animTranslateY) {
//右眼珠子
drawCircle(
color = Color.Black,
center = Offset(smilePadding * 3, size.height / 2 - smilePadding / 2),
radius = smilePadding / 4
)
}
}
}
为了不让眼珠子从眼眶里蹦出来,我们将位移的范围限制在了50以内,运行效果如下
总结
通过Canvas中的一些方法配合简单的动画API实现了这个眼珠跟随手势移动的笑脸😁