虎虎生威年,用Compose Canvas画只猛虎让大家 “虎躯一震” 吧

Posted 乐翁龙

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了虎虎生威年,用Compose Canvas画只猛虎让大家 “虎躯一震” 吧相关的知识,希望对你有一定的参考价值。

开篇废话

无奈放假前又出了两个需求,忙活到昨天终于是上线了。 打开掘金发现大家都在创意闹新春,有放烟花的,有写福、写春联的,有年兽作战小游戏的,还有画虎的。该说不说,‘你们掘金花活儿挺多啊’!我也来参与了,真香~

写这篇文章呢是看到了前端同学使用Echarts画的老虎( 《辞旧迎新 2022 我用Echarts画了个大老虎,祝大家新春快乐,虎虎生威啊。》)。这老虎确实可以,我就同样直接拿来了,感谢iconfont作者Eve!

实现目标

这篇文章我们就利用Compose的Canvas来一块一块的绘制出一只猛虎,如上动图,如假包换。

关于Compose Canvas有不太熟悉的同学可以参考我之前写的博客 《Jetpack Compose - Canvas》,里面详细介绍了Canvas的各种draw()方法,我们这里主要就是使用drawPath()方法来实现。

实现流程

首先我们需要点击素材的 【SVG下载】,下载后导入android Studio:

在资源文件夹上右键,选择 Vector Asset 来导入刚刚下载的svg文件。

OK,大功告成 ~~~~~~ 了一部分!

来看下生成的XML文件,主要在于其中pathData的值,我们就需要利用这些值来一步步的进行绘制。那么这些值要怎么看呢?

1、认识pathData

关于SVG path的详情介绍请看Paths – SVG 1.1 (Second Edition)

我呢这里先根据Android写了一个了简单的pathData,渲染图如右侧所示:

接下来一起看下这段神奇的指令:

M0,0 l100,0 L100,100 h100 v-100 h100 v200 H0 z
  • M0,0 ,表示path.moveTo(),后面的0,0就是坐标位置,所以意思就是就是将画笔移到了A(0,0)的位置
  • l100,0 ,表示path.relativeLineTo(),也就是说相对于刚才的A点,横向移动100,水平移动0到B点,那么B的坐标点为(100,0)
  • L100,100 ,表示path.lineTo(),也就是绝对的移动了,直接将画笔移动到(100,100)的位置,所以C的坐标点为(100,100)
  • h100 ,horizontal,表示path.relativeLineTo(),是一种简化版的写法,意思就是相对于C点纵向不变,横向移动100到D点,所以D的坐标为(200,100)。如果用l表示,那么等同于“l100,0”
  • v-100,vertical,表示path.relativeLineTo(),是一种简化版的写法,意思就是相对于D点横向不变,纵向向上移动100到E点,所以E的坐标为(200,0)

剩下的h、v移动则不说明了,直接看H:

  • H0,同样是横向移动,只是后面的0表示要移动到 x=0 的位置,需要与上文h的相对移动距离区分开。那么从G点直接横向移动到x=0的位置,所以H点坐标(0,100)
  • z ,表示path.close()

经过上面描述,可能大家大致就能明白pathData所表示的含义了。并且一个Path可以由M开头z结尾来完成。

其实后面还有更复杂的C(三次贝塞尔曲线)、Q(二次贝塞尔曲线)、A(圆弧)等,但是,这里我们就不过多介绍了。

以上内容可以足够让我们画出来上文提到的猛虎了。

2、解析pathData

了解到pathData的构成及含义后,我们就可以进行解析,转成Canvas需要的path,然后就可以进行绘制了。

大致描述下我的临时解法吧,将上述规范的pathData字符串一个字符一个字符的进行解析,如果字符是字母等,则表示移动moveTo()、lineTo()等的命令,后面跟着的则是坐标,连续的坐标是可以用空格隔开的,处理的时候需要注意。

其中处理H命令时就有点特殊了,因为我们只知道要移动到x=0的位置,可是如果当前的位置是相对移动来的,那么我们就不知道当前位置的y坐标,所以我们还需要手动计算出当前的y坐标,那么就需要 PathMeasure 类来帮我们处理(感谢【路很长OoO】大佬的指导)。


//由于在compose下PathMeasure并没有获取位置的方法,
//所以我们使用android.graphics.PathMeasure(),
//然后path也需要转换成android.graphics.Path

pathMeasure.setPath(path.asAndroidPath(), false)

val position = FloatArray(2)
val tan = FloatArray(2)

pathMeasure.getPosTan(pathMeasure.length, position, tan)

//至此position就存储了当前的位置坐标

获取到移动的命令以及坐标后,整体的处理就变得很简单了,如下所示:

fun processMethod() 
    when (nextMethod) 

        cmdM -> 
            path.moveTo(lastX, lastY)
        

        cmdm -> 
            path.relativeMoveTo(lastX, lastY)
        

        cmdL -> 
            path.lineTo(lastX, lastY)
        

        cmdl -> 
            path.relativeLineTo(lastX, lastY)
        

        cmdH -> 
            //注意此时y坐标应该是当前位置的y坐标
            pathMeasure.setPath(path.asAndroidPath(), false)

            val position = FloatArray(2)
            val tan = FloatArray(2)

            pathMeasure.getPosTan(pathMeasure.length, position, tan)
            path.lineTo(lastX, position[1])
        

        cmdh -> 
            path.relativeLineTo(lastX, 0f)
        

        cmdV -> 
            //注意此时x坐标应该是当前位置的x坐标
            pathMeasure.setPath(path.asAndroidPath(), false)

            val position = FloatArray(2)
            val tan = FloatArray(2)

            pathMeasure.getPosTan(pathMeasure.length, position, tan)
            path.lineTo(position[0], lastX)
        

        cmdv -> 
            path.relativeLineTo(0f, lastX)
        

        cmdz -> 
            path.close()
        
    


所以根据上述老虎的SVG图片,从转成的Android pathData我们可以获取到多条path,然后我们按照顺序将其进行绘制,最终就可以得到上述动图了。

说起来很简单,可是在解析pathData以及在绘制老虎的顺序上真真是废了老半天的劲了,一条一条的进行展示、隐藏,看下排序过后的部分数据吧:

最后再看下动图,哇,成果还是可以的吧。画虎点睛,王!!!

最后的最后,祝大家新年快乐,虎虎生威啊!!!

以上是关于虎虎生威年,用Compose Canvas画只猛虎让大家 “虎躯一震” 吧的主要内容,如果未能解决你的问题,请参考以下文章

兔年了,一起用Compose来画兔子吧

兔年了,一起用Compose来画兔子吧

用 Compose 画个小老虎恭贺新春

2021年年度总结 虎虎生威 继往开来

2021年年度总结 虎虎生威 继往开来

2021年年度总结 虎虎生威 继往开来