自定义view实现超萌动感小炸弹

声明:本文章独家授权微信公众号码个蛋原创推文

Hello,小伙伴们,我回来了。这些日子有的小伙伴问我怎么没有更新了。这个其实是有原因,首先,最近有点忙。其次没有看到什么觉得好玩的动画!最后,就是我更新过了!!ThreadLocal源码完全解析,只是你们源码不感冒,然后你们忽略了!!!!忽略了!!!还有,我其实有更新一个薄荷卷尺,只是觉得有点简单,而且还像也有什么好讲的,所以只是上传到github,没有文章。
客套话已经完了,现在开始我们的超萌动感小炸弹之旅
首先,我还是先感谢一下作者,设计出这么棒的动画!!设计出处点我
效果如下,Amazing:

preview.gif

再来看android的实现效果。

android实现

下面我们和自定义view实现超萌动感天气小太阳一样,开始解析动画!(没看过天气小太阳的朋友可以先去看天气小太阳,有些天气小太阳讲过的套路将不再讲,同时需要掌握pathcamera、贝塞尔曲线等,不然部分代码可能会引起不适)。

我们先把静态view绘制出来,然后再实现动画,Let's go。

静态效果
1.地板
image.png

可以看到地板其实就是一条直线。然后中间两个缺口。这要个么实现呢?看到小太阳的小伙伴可能都会说,这很简单。只要画一线直线然后覆盖两个白的区间就可以了。的确这可以实现,但是仔细观察可以发现下方的缺口是两个半圆加矩形实现的,这样的话就有点麻烦,而且不方便缺口位置的移动。那有什么简单的方法呢?有,那就是使用Path进行绘画一条直线,然后通过设置圆笔头,再设置DashPathEffect(实现虚线,一段画,一段不画的效果,可以自由控制各段长度)来实现间隔(本view的缺口都是使用此特性实现的,不熟悉的小伙伴可以去看一下),代码如下:

        float[] groundEffectFloat=new float[]  {bombLineWidth/4,bombLineWidth/2+bombLineWidth,bombLineWidth*2,bombLineWidth/3*2+bombLineWidth,getMeasuredWidth(),0};//设置画与不画所占长度
        groundDashPathEffect=new DashPathEffect(groundEffectFloat,0);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(bombLineColor);
        mPaint.setPathEffect(groundDashPathEffect);//设置虚线效果
        mPath.reset();
        mPath.moveTo(bombLineWidth/2,getMeasuredHeight()-bombLineWidth/2);
        mPath.lineTo(getMeasuredWidth()-bombLineWidth/2,getMeasuredHeight()-  
        bombLineWidth/2);
        canvas.drawPath(mPath,mPaint);
2.身体的边框

image.png

仔细一看!聪明的你一定会说太简单了,这不就是一个圆然后再用DashPathEffect实现缺口不就可以了!!嗯,对,就是这样的。直接放代码:

        mPaint.setPathEffect(bodyDashPathEffect);
        mPaint.setColor(bombLineColor);
        mPaint.setStyle(Paint.Style.STROKE);
        mPath.reset();
        mPath.addCircle(bombCenterX,bombCenterY,bodyRadius,   
        Path.Direction.CW);
        canvas.drawPath(mPath,mPaint);
        canvas.restore();

简单!简单的不能再简单了,下面看身体

3.身体
image.png

可以发现身体其实也就是一个圆,然后加上左上角的高光。那么高光是怎么实现的呢?
三个点的高光,很简单的,用Path画弧,然后使用DashPathEffect效果,完美。
那么另一个高光呢?看图。

image.png

可以看到就是条圆弧和一个路径合成的,然后裁剪保持圆内。路径的形成就是取弧度的两个点,然后用贝塞尔曲线进行绘制,控制点位于弧度中分线中(下图红点)

image.png
image.png

代码如下:(部分代码,左上角高光的,其它的请查看源码)

       //左上角的光边
        mPaint.setPathEffect(null);
        mRectF.set(bombCenterX-bodyRadius+bombLineWidth/2,bombCenterY-bodyRadius+bombLineWidth/2
        ,bombCenterX+bodyRadius-bombLineWidth/2,getMeasuredHeight()-bombLineWidth-bombLineWidth/2);
        canvas.drawArc(mRectF,160,100,false,mPaint);
        //拼接光边
        mPath.reset();
        mPath.addCircle(bombCenterX,bombCenterY,bodyRadius-bombLineWidth/2, Path.Direction.CCW);
        canvas.save();
        canvas.clipPath(mPath);//裁剪圆内

        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(lightColor);
        canvas.drawPath(mBodyLightPath,mPaint);
        canvas.restore();
4.脸
image.png

大家可以看到,好你有点复杂的,其实还好。这里是因为使用了Z轴旋转,看起来有点复杂,那我们移到中间。

image.png

好像简单了,眼睛和酒窝简单,4个圆!!嘴巴,这个。。。这个好像有点恶心啊。其实不然,看图。

image.png
image.png

其实就是一个圆然后再加上一个路径图就可以实现,红点表示的是控制点。空心点表示节点。细心的朋友可能发现,不对啊。舌头下面不全是红的,和嘴巴是分开的。这里只需要把嘴巴按比例缩小,然后和嘴巴做个Xfermode就可以了。部分代码:

        //画舌头 圆和嘴巴的缩放相交,mpath是嘴巴的路径
        int save=canvas.saveLayer(0,0,getMeasuredWidth(),getMeasuredHeight(),null,Canvas.ALL_SAVE_FLAG);
        canvas.drawPath(mPath,mPaint);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        mPaint.setColor(Color.parseColor("#f34671"));
        canvas.drawCircle(bombCenterX,mouthY+(mouthMaxY-mouthY)/8+bodyRadius/(5-1.4f*mouthOffsetPercent),bodyRadius/5,mPaint);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
        canvas.scale(0.8f,0.8f,bombCenterX,(mouthMaxY+mouthY)/2);
        canvas.drawPath(mPath,mPaint);
        canvas.restoreToCount(save);
        mPaint.setXfermode(null);
5.脸上的阴影(不知道叫什么,暂时称为阴影遮罩)
image.png

一看,个别好事的小伙伴说,你不会又让我用贝塞尔曲线画吧!这个不好找啊!!冷静冷静,这个实现如下:

image.png

如此简单,两个圆取红圆未相交的部分。

//两个圆相交产生阴影
        int save=canvas.saveLayer(0,0,getMeasuredWidth(),getMeasuredHeight(),null,Canvas.ALL_SAVE_FLAG);
        mPaint.setStyle(Paint.Style.FILL);
        mPaint.setColor(bombShadowColor);
        canvas.drawCircle(bombCenterX,bombCenterY,bodyRadius-bombLineWidth/2,mPaint);
        mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
        canvas.translate(-bodyRadius/5,-bodyRadius/5);
        mPaint.setColor(bombColor);
        canvas.drawCircle(bombCenterX,bombCenterY,bodyRadius-bombLineWidth/2,mPaint);
        canvas.restoreToCount(save);
        mPaint.setXfermode(null);
6.头
image.png

小伙伴又要说了。这个不好画,不好画!!冷静冷静。这个其实更简单。只要把头放在身体的后面一层就可以了。看图:

image.png

代码:

太简单,我不想贴了,假装我是代码
7.引线
image.png

这个引线,其实也就是一线曲线,贝塞尔曲线继续上场(不解释,不懂的请面壁去)。

image.png
8.爆炸效果
image.png

简单的不太再简单了,4个圆,半径从大到小画,中间然后挖空。so easy!!

        int save = canvas.saveLayer(0,0,getMeasuredWidth(),getMeasuredHeight(),null,Canvas.ALL_SAVE_FLAG);
        float distance = maxBlastCircleRadius/12;
        //画圆
        mPaint.setColor(lightColor);
        canvas.drawCircle(bombCenterX,circleY, currentBlastCircleRadius,mPaint);
        mPaint.setColor(bombColor);
        canvas.drawCircle(bombCenterX,circleY, currentBlastCircleRadius -distance,mPaint);
        mPaint.setColor(bombLineColor);
        canvas.drawCircle(bombCenterX,circleY, currentBlastCircleRadius -distance*2,mPaint);
        mPaint.setColor(lightColor);
        canvas.drawCircle(bombCenterX,circleY, currentBlastCircleRadius -distance*3,mPaint);
        //掏空
        if (blastCircleRadiusPercent >0.65) {
            mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
            canvas.drawCircle(bombCenterX, circleY, currentBlastCircleRadius - maxBlastCircleRadius * 0.65f, mPaint);
            mPaint.setXfermode(null);
        }
        canvas.restoreToCount(save);

到这里,我们已经完成了一半,那就是小炸弹的显示,现在到了动画的时间了!再次出场


android实现
9.脸左右移动动画

可以看到左右移动,在移动的时间然后我们只需要在画脸的时间加一个偏移,然后在移动的过程中,会发现脸会绕炸弹身体的中心旋转。所以代码如下

        canvas.save();
        mCamera.save();
        mCamera.rotate(bombTBRotate,0,-bombLRRotate/3);
        mMatrix.reset();
        mCamera.getMatrix(mMatrix);
        mCamera.restore();
        mMatrix.preTranslate(-bombCenterX,-(bombCenterY));
        mMatrix.postTranslate(bombCenterX,bombCenterY);
        mMatrix.postTranslate(faceLROffset,faceTBOffset);
        canvas.setMatrix(mMatrix);

使用camera,进行z轴的旋转,然后再进行translate左右移动,然后使用valueanimator动画对变偏移进行设置,搞定!在移动过程中,可以发现眼睛有眯下的效果。这个很简单,可以把眼睛用椭圆来实现,保持宽度不变,改变高度就可以了。

image.png
10.身体头部引线左右旋转

这个就更简单了,只需要在画之前用camera旋转变换获取martix,然后对canvas进行变换。

11.脸部上下移动

首先和脸部左右移动一样,使用matrix.translate进行上下移动。眼睛的变换也一样。后面的眼睛放大效果,就是在变成圆的眼睛的时候,放大圆的半径。
嘴巴的变换就相对比较复杂!看图,高能预警,我也不知道我讲不讲得清楚!!!!

image.png

这是刚才画嘴巴的图!!!嘴巴动画有两个部分!!(以下语句可能会引起不适)

  • 第一部分嘴角往两边移动,嘴巴变扁。这里我们需要把ab两点用属性动画往两边移动(两边的拐角点同样移动),c点往上方移动,然后回到原始位置。
  • 第二部分是ab两点往中间靠拢,直到ab重合,同时ab两点往上移,de的控制点同时拉长,直到形成一个椭圆。

不理解!!不理解再好好想象一下,空间想象能力。要是还想不懂,那就算了。毕竟平时用得不多。

12.炸弹引线,点燃效果

炸弹引线效果同样分两个部分

  • 一个是引线变短,可以根据PathMeasure,获取Path的比例Path(比如70%Path),这样我们就可以通过ValueAnimator用一个01的比例来绘制引线变短的效果
      //mHeadLinePath是引线的完整Path
       mPathMeasure.setPath(mHeadLinePath,false);
        mPath.reset();
      mPathMeasure.getSegment(0,mPathMeasure.getLength()*headLinePercent,mPath,true);//根据比例获取对应比例的引线
        canvas.drawPath(mPath,mPaint);
  • 第二部分是点燃的效果。其实就是一个金色的实心圆,然后一个红色的圆边框,中间白色,三个圆按不同的速率和极限做放大缩小动画 (这里原设计还加入了变色的功能,金色圆会变色,可以用ArgbEvaluator实现)。
image.png
13.爆炸动画

和引线动画类型,4个圆做放大缩小动画,只是到一定的大小后,然后圆小漏空,并且漏空逐渐放大。

14.结语

好了,我们的超萌动感小炸弹到这里就结束了。希望小伙伴们能有所收获,掌握更多自定义view的套路,更多分析方法,我们下次见。

源码点我

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,999评论 4 368
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 68,102评论 1 302
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 110,709评论 0 250
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,439评论 0 217
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,846评论 3 294
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,881评论 1 224
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 32,062评论 2 317
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,783评论 0 205
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,517评论 1 248
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,762评论 2 253
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,241评论 1 264
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,568评论 3 260
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,236评论 3 241
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,145评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,941评论 0 201
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,965评论 2 283
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,802评论 2 275

推荐阅读更多精彩内容