当前位置: 首页 > 科技观察

Android动画:可爱的蜡烛吹蜡烛动画

时间:2023-03-13 23:07:33 科技观察

一、介绍最近开始写一些文章,记录一下之前写的一些小项目或者定义View的积累。还需要多挖出一些尘土飞扬的东西,整理一下。的。这只是点燃和熄灭动画的一部分,并没有火焰为什么会点燃的动画。希望有兴趣的同学能够完成并分享。废话不多说,让我们来看看这两支可爱的小蜡烛吧。小蜡烛屏住呼吸点燃了火焰,被旁边的小伙伴吹灭了0^0,看起来还是很可爱的。看图大家应该能想到怎么实现吧,自定义View!顺便说一句,但是如何做好这个过程呢,下面就跟着步骤一起来看看吧。代码有点多,请耐心观看,有兴趣的同学可以从我的GITHUBclone下来看代码。2.流程实现了蜡烛的绘制和动画。基于面向对象的思想,显然这里有两根蜡烛!鉴于这种情况,我们定义了一个具有蜡烛基本属性的蜡烛类。publicabstractclassICandle{//蜡烛左下角坐标protectedintmCurX;protectedintmCurY;//蜡烛宽高protectedintmCandleWidth;protectedintmCandleHeight;//蜡烛左眼坐标protectedPointmEyeLPoint;//蜡烛右眼坐标protectedPointmEyeRPoint;//蜡烛眼半径protectedintmEyeRadius;//眼距Eprotect;//主体颜色protectedintmCandleColor;//是否停止动画protectedbooleanmIsAnimStoping=false;//烛芯坐标protectedPointmCandlewickPoint;publicvoidinitAnim(){}publicvoidstopAnim(){}publicvoiddrawSelf(Canvascanvas){}}对应蜡烛和代码,那么一目了然。可能需要说明的应该是下面的方法publicvoidinitAnim(),stopAnim()来初始化动画开始和结束所需要的数据,小蜡烛会实现这个方法,drawSelf(Canvascanvas)将canvas传入和蜡烛本身画出你自己。现在是时候让我们看看小蜡烛的内部结构了,hiahiahiahia!不对,是伴随着蜡烛的生死相伴的火焰!我们先看看火焰,小蜡烛稍后会自己燃烧。+10086sFlame,先来看看我们富勒姆的真面目,貌似也没什么不对。首先,内区是Flame,外区是Flame先生自己燃烧的人道之光和散落的灰烬(手动擦眼泪)。让我们看一下Flame的实现。我们一步步来分析。privatestaticfloatCHANGE_FACTOR=20;privatePaintmPaint;privatePathmPath;//左下点坐标privateintmCurX;privateintmCurY;//火焰宽度privateintmWidth;//火焰高度privateintmHeight;//记录初始高度privateintmPreHeight;//记录初始宽度privateintmPreWidth;//火焰顶部贝塞尔曲线曲线控制点变化参数privateintmTopXFactor;privateintmTopYFactor;//用于实现火焰的抖动privateRandommRandom;//光环半径privateintmHaloRadius;//正在燃烧privatebooleanmIsFiring;//是否启动停止动画privatebooleanmIsStopAnim=false;privatebooleanmFlagStop=false;privateLinearGradientmLinearGradient;privateRadialGradientmRadialGradient;privateValueAnimatormFlameAnimator;privateValueAnimatormHaloAnimator;这些就是参数,主要是我们的动画实现过程,也就是我们的属性动画ValueAnimator。这里有两个渲染类。学习关于。LinearGradient绘制火焰,RadialGradient绘制漫射光。初始化过程就不写了,大家看看这段代码吧。主要是小火苗是怎么画出来的。显示代码quadTo(mCurX+mWidth/2,mCurY+mHeight/3,mCurX+mWidth,mCurY);mPath.quadTo(mCurX+mWidth/2+((1-mRandom.nextFloat())*CHANGE_FACTOR)+mTopXFactor,mCurY-2*mHeight+mTopYFactor,mCurX,mCurY);canvas.drawPath(mPath,mPaint);这是火焰火焰的绘制。可以看到这里用到了二次贝塞尔曲线的绘制。不了解贝塞尔曲线的同学也可以点击这个waveLoading动画(贝塞尔曲线)进行简单介绍,当时是在一个水波视图中使用的。这里的绘图是基于上图中的矩形。我们再看看这张图(当然是hiahia的加强版)。那为什么mRandom.nextFloat())*CHANGE_FACTOR要加到上面的x坐标上呢?你想,火焰不是左右摇摆吗,用一个随机的来控制左右摇摆。mFlameAnimator=ValueAnimator.ofFloat(0,4).setDuration(4000);mFlameAnimator.setRepeatCount(ValueAnimator.INFINITE);mFlameAnimator.addUpdateListener(newValueAnimator.AnimatorUpdateListener(){@OverridepublicvoidonAnimationUpdate(ValueAnimator.AnimationZero){animator=float(OneimatorAnimationZero){float);if(zeroToOne>=1.0f&&zeroToOne<=1.2f){//火焰点燃zeroToOne=1.0f-5*(zeroToOne-1.0f);//1-0mHeight=(int)(mPreHeight*(1-zeroToOne));mIsFiring=true;}elseif(zeroToOne>=3.5f){if(mFlagStop){mFlameAnimator.cancel();return;}//火焰吹灭zeroToOne=2*(zeroToOne-3.5f);//0-2mTopXFactor=(int)(-20*zeroToOne);mTopYFactor=(int)(160*zeroToOne);/mWidth=(int)(mPreWidth*(1-zeroToOne));mIsFiring=false;}}});4秒内,火焰进行了一系列的活动,随着灯芯从下方向上移动,不断变换着火焰的位置,分为两部分,火焰点燃,火焰熄灭,从代码中可以看出,火焰在启动时燃烧mHeight逐渐增加,然后有一个上升的过程。另一种是在火焰被吹灭时,因为火焰被吹灭时的高度必须保持之前的值,所以不需要改变,但是mTopXFactor和mTopYFactor这两个因子是用来控制位置的火焰。好吧,既然有了火焰,蜡炬化为灰烬,眼泪开始干涸,生命之光就该出现了。光圈的绘制和动画比较简单mPaint.setStyle(Paint.Style.STROKE);mPaint.setStrokeWidth(5);mPaint.setShader(mRadialGradient);canvas.drawCircle(mCurX+mWidth/2,mCurY-mHeight/2,mHaloRadius,mPaint);canvas.drawCircle(mCurX+mWidth/2,mCurY-mHeight/2,mHaloRadius+5,mPaint);canvas.drawCircle(mCurX+mWidth/2,mCurY-mHeight/2,mHaloRadius-5,mPaint);这里只改变了一个参数,mHaloRadius是光圈的半径。但是不要忘了其他的参数也在同时变化,只不过是放在mFlameAnimator里面而已。好了,关于这个Flame的介绍就结束了,接下来还有很长的路要走。写了这么多,还没完。这让我想起了某个古人说的,不是我。在你老死之前,你会累死。FireCandle这个名字有点陌生。之前介绍过ICandle,现在来看看它的实现类,蜡烛两兄弟的FireCandle。和往常一样,我不说初始化,先看看应该有的变量。privatePaintmPaint;//中心X坐标privateintmCenterX;//记录初始宽privateintmPreWidth;//记录初始高privateintmPreHeight;//蜡烛芯旋转角privateintmCandlewickDegrees=0;privateFlamemFlame;privatebooleanmIsFire=false;privatebooleanmIsStateOnStart=false;privatebooleanmIsStateOnEnd=false;privatebooleanmFlagStop=false;privateValueAnimatormCandlesAnimator;命名很规范,一看就知道是做什么用的。我们还是主要看绘图和属性动画的配合,不看绘图(光速打脸)。观看动画。mCandlesAnimator=ValueAnimator.ofFloat(0,4).setDuration(4000);mCandlesAnimator.addUpdateListener(newValueAnimator.AnimatorUpdateListener(){@OverridepublicvoidonAnimationUpdate(ValueAnimatoranimation){floatzeroToOne=(float)animation.getAnimatedValue();if(zeroToOne<=1.0f){//蜡烛芯负载拉mIsFire=true;mCandleWidth=mPreWidth+(int)(zeroToOne*40);mCandleHeight=mPreHeight-(int)(zeroToOne*30);mCandlewickDegrees=(int)(-60+(180+60)*zeroToOne);refreshEyePosition();}elseif(zeroToOne<=2.0f){zeroToOne=zeroToOne-1.0f;//蜡烛芯上摆if(zeroToOne<=0.2f){zeroToOne=1.0f-5*zeroToOne;mIsFire=false;mCandleWidth=mPreWidth+(int)(zeroToOne*40);mCandleHeight=mPreHeight-(int)(zeroToOne*30);mCandlewickDegrees=(int)(180*zeroToOne);}else{if(mFlameStateListener!=null&&!mIsStateOnStart){mFlameStateListener.flameStart();mIsStateOnStart=true;}mCandleWidth=mPreWidth;mCandleHeight=mPreHeight;mCandlewickDegrees=0;if(mFlagStop){mCandlesAnimator.cancel();}}refreshEyePosition();}elseif(zeroToOne>=3.5f){//烛芯被吹歪zeroToOne=2*(zeroToOne-3.5f);//0-1mCandlewickDegrees=(int)(-60*zeroToOne);if(mFlameStateListener!=null&&!mIsStateOnEnd){mFlameStateListener.flameEnd();mIsStateOnEnd=true;}}}});这个过程有点多,但是一点也不复杂。首先我们来看动画中的小蜡烛。一开始,他来了一个变胖变红脸变矮,所以mCandleWidth变大了,mCandleHeight变小了。背部的灯芯随深蹲大角度旋转。您还看到了灯芯是如何旋转的。更改坐标系,然后你可以使用canvas.rotate(mCandlewickDegrees,mCenterX,mCurY-mCandleHeight);这个方法。上摆过程也是一样,就不多说了。refreshEyePosition();这个方法是改变眼睛的位置,两处都用,所以略显独立。注意变量mIsFire,在没有火焰的时候做其他绘制,比如红眼等。好了,介绍到这里,小蜡烛的部分就结束了。SecCandle大蜡烛,帅气的蜡烛小镇建筑,实际出图和小蜡烛差不多,这里就不多说了。联合绘制View和控制器AnimControler的功能非常简单。绘制地板的部分就是将计算出的高度和宽度赋值给两根蜡烛,然后控制两根蜡烛开始动画。mFirCandle=newFirCandle(mRelativeX+mWidth/6,mRelativeY+mHeight);mFirCandle.initCandle(mFirCandleWidth,mFirCandleHeight);mFirCandle.initAnim();mSecCandle=newSecCandle(mRelativeX+mWidth/2,mRelativeX+mWidth/2,mRelativeCandleY+mHeight);mSec,mSecCandleHeight-80);mSecCandle.initAnim();*****是我们的ViewCandlesAnimView//16ms刷新CanvasmInvalidateAnimator=ValueAnimator.ofInt(0,1).setDuration(16);mInvalidateAnimator.setRepeatCount(ValueAnimator.INFINITE);mInvalidateAnimator.addListener(newAnimatorListenerAdapter(){@OverridepublicvoidonAnimationRepeat(Animatoranimation){invalidate();}});mInvalidateAnimator.start();这个属性动画的任务是快速刷新界面,就是蜡烛及时显示的动画。@OverrideprotectedvoidonMeasure(intwidthMeasureSpec,intheightMeasureSpec){super.onMeasure(widthMeasureSpec,heightMeasureSpec);intwidth=measureDimension(WIDTH_DEFAULT*mDensity,widthMeasureSpec);intheight=measureDimension(HEIGHT_DEFAULT*mDensity,heightMeasureSpec);setMeasuredDimension(宽度,高度);}publicintmeasureDimension(默认intmeasureDimension),intmeasureSpec){intresult;intspecMode=MeasureSpec.getMode(measureSpec);intspecSize=MeasureSpec.getSize(measureSpec);if(specMode==MeasureSpec.EXACTLY){result=specSize;}else{result=defaultSize;if(specMode==MeasureSpec.AT_MOST){result=Math.min(result,specSize);}}returnresult;}@OverrideprotectedvoidonDraw(Canvascanvas){if(!mIsInit){initConfig();mIsInit=true;}mAnimControler.drawMyView(canvas);}你可以看到***在视图中调用了我们的控制器并传递了cavas。***提示:你有没有注意到每个动画的时长是一样的。3.***好了,到此为止,一个简单的视图自定义说了这么多。第一次写简书,希望大家多多支持。希望大家提出建议和意见。希望斧头是对的。