你看这个圆脸😁,又大又可爱~ (Compose低配版)


前言

阅读本文需要一定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
                )

整个笑脸就画出来了,效果如下

二、跟随手势移动

在实现功能之前我们需要介绍transformableanimateFloatAsStatetranslate

transformable

transformablemodifier用于平移、缩放和旋转的多点触控手势的修饰符,此修饰符本身不会转换元素,只会检测手势。

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实现了这个眼珠跟随手势移动的笑脸😁

相关推荐
一笑的小酒馆2 小时前
Android12Launcher3实现应用裁剪
android
李姆斯3 小时前
为啥Agent在coding表现这么好,但是在别的领域就是差的不少?
前端·agent·ai编程
鱼与宇6 小时前
前端Web(html+css+js+vue3)
前端
FinelyYang7 小时前
UniApp + LiveKit:Android 端为什么要用原生做屏幕共享,以及我们怎么落地的
android·uni-app
蓝速科技7 小时前
蓝速科技信创落地:平衡安全合规与项目成本的实战方案
android·运维·人工智能·科技·安全·电脑·鸿蒙
雪芽蓝域zzs8 小时前
(六)打包优化 + Nginx 部署完整配置 + 项目收尾
前端·vue.js
研☆香8 小时前
聊一聊前端的常见字 关键字
开发语言·前端·javascript
东风破_8 小时前
JWT 1:从一个登录请求开始,理解 React 项目里的 API 层与 Mock
前端·后端
东风破_8 小时前
JWT 3:为什么 Token 要放进 Authorization?Axios 拦截器到底解决了什么?
前端·后端
东风破_8 小时前
JWT 5:路由守卫是什么?把整个 JWT 登录鉴权流程串起来
前端·后端