指点成金-最美分享吧

登录

实战演练!二阶贝塞尔仿微信扔炸弹动画

佚名 举报

篇首语:本文由小编为大家整理,主要介绍了实战演练!二阶贝塞尔仿微信扔炸弹动画相关的知识,希望对你有一定的参考价值。

前言

新出来的微信炸屎动画很多人都玩过了,所以先仿照一个微信扔炸弹的动画,在后续有时间会做一个完整的,效果如下:

具体实现

其中最麻烦的就是绘制抛物线了,爆炸的效果只是播放了一个动画,另外微信貌似都是通过代码绘制的,可能不是动画,奈何没有人家那技术,只能找一张动画来凑合。

二阶贝塞尔曲线

抛物线在这里是通过二阶贝塞尔曲线来完成,所以先来了解下什么是二阶贝塞尔曲线,从下图中可以发现,二阶贝塞尔曲线有三个关键点,我们可以称作起点坐标、终点坐标,还有控制点。

起点和终点坐标好理解,控制点可以理解成开始下降的转折点,而古老的数学大神早就提供好了公式,我们只需要向这个公式提供这几个参数即可得到x、y,当然还有个参数是时间,有了时间控制,我们可以在指定秒内把他平滑的绘制完成。

公式如下:

x = (1 - t)^2 * 0 + 2 t (1 - t) * 1 + t^2 * 1 = 2 t (1 - t) + t^2y= (1 - t)^2 * 1 + 2 t (1 - t) * 1 + t^2 * 0 = (1 - t)^2 + 2 t (1 - t) 

自定义二阶贝塞尔曲线计算器

提到动画,首先可能会想到ObjectAnimator类,没错,抛物线也是通过ObjectAnimator来完成的,只不过我们需要自定义一个TypeEvaluator,用来提供二阶贝塞尔曲线的x和y。

TypeEvaluator只有一个方法,定义如下:

public abstract T evaluate (float fraction,                 T startValue,                 T endValue)复制代码

fraction表示开始值和结束值之间的比例,startValue、endValue分别是开始值和结束值,这个比例也可以当作是时间,可能官方一点叫比例,他会自动计算,值的范围是0-1,比如取值0.5的时候就是动画完成了一半,1的时候动画完成。

所以套入二阶贝塞尔曲线公式得到如下代码:

class PointFTypeEvaluator(var control: PointF) : TypeEvaluator     override fun evaluate(fraction: Float, startValue: PointF, endValue: PointF): PointF         return getPointF(startValue, endValue, control, fraction)        private fun getPointF(start: PointF, end: PointF, control: PointF, t: Float): PointF         val pointF = PointF()        pointF.x = (1 - t) * (1 - t) * start.x + 2 * t * (1 - t) * control.x + t * t * end.x        pointF.y = (1 - t) * (1 - t) * start.y + 2 * t * (1 - t) * control.y + t * t * end.y        return pointF    复制代码

播放动画

然后使用ObjectAnimator进行播放。

 val animator = ObjectAnimator.ofObject(activityMainBinding.boom, "mPointF",         PointFTypeEvaluator(controlP), startP, endP)复制代码

注意的是这个View需要有point方法,参数是PointF,方法内主要完成x和y的设置。

 public void setPoint(PointF pointF)      setX(pointF.x);     setY(pointF.y); 复制代码

当然微信炸弹落地的位置是随机的,我们也加个随机。

class MainActivity : AppCompatActivity()     lateinit var binding: ActivityMainBinding;    private fun getRandom(max: Int, min: Int): Int         val random = java.util.Random()        return random.nextInt(max - min + 1) + min        private fun getRandomPointF():PointF        val outMetrics = DisplayMetrics()        val offset = 100        windowManager.defaultDisplay.getMetrics(outMetrics)        val width = outMetrics.widthPixels        val height = outMetrics.heightPixels        return PointF(getRandom(width / 2 + offset, width / 2 - offset).toFloat(), getRandom(height / 2 + offset, height / 2 - offset).toFloat())        override fun onCreate(savedInstanceState: Bundle?)         super.onCreate(savedInstanceState)        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);        binding.button.setOnClickListener             binding!!.boom.visibility = View.VISIBLE            val startP = PointF()            val endP = PointF()            val controlP = PointF()            val randomPointF = getRandomPointF()            startP.x = 916f            startP.y = 1353f            endP.x = randomPointF.x            endP.y = randomPointF.y            controlP.x = randomPointF.x + getRandom(200, 50)            controlP.y = randomPointF.y - getRandom(200, 50)            val animator = ObjectAnimator.ofObject(binding.boom, "point",                PointFTypeEvaluator(controlP), startP, endP)            animator.start()            复制代码
android="http://schemas.android.com/apk/res/android"    xmlns:app="http://schemas.android.com/apk/res-auto"    xmlns:tools="http://schemas.android.com/tools">                                        复制代码

效果如下:

爆炸效果

爆炸效果是使用的动画,用的lottie框架,这里提供爆炸文件的下载地址。

https://lottiefiles.com/download/public/9990-explosion复制代码

有了结束的坐标点,只需要吧LottieAnimationView移动到对应位置进行播放即可,播放后隐藏,完整代码如下:

package com.example.kotlindemoimport android.animation.Animatorimport android.animation.ObjectAnimatorimport android.content.Contextimport android.content.Intentimport android.graphics.BitmapFactoryimport android.graphics.PointFimport android.media.projection.MediaProjectionManagerimport android.os.Buildimport android.os.Bundleimport android.util.DisplayMetricsimport android.view.Menuimport android.view.MenuItemimport android.view.Viewimport android.widget.Toastimport androidx.appcompat.app.AppCompatActivityimport androidx.databinding.DataBindingUtilimport com.example.kotlindemo.databinding.ActivityMainBindingimport com.example.kotlindemo.widget.PointFTypeEvaluatorimport meow.bottomnavigation.MeowBottomNavigationimport kotlin.random.Randomclass MainActivity : AppCompatActivity()     lateinit var binding: ActivityMainBinding;    private fun getRandom(max: Int, min: Int): Int         val random = java.util.Random()        return random.nextInt(max - min + 1) + min        private fun getRandomPointF():PointF        val outMetrics = DisplayMetrics()        val offset = 100        windowManager.defaultDisplay.getMetrics(outMetrics)        val width = outMetrics.widthPixels        val height = outMetrics.heightPixels        return PointF(getRandom(width / 2 + offset, width / 2 - offset).toFloat(), getRandom(height / 2 + offset, height / 2 - offset).toFloat())        override fun onCreate(savedInstanceState: Bundle?)         super.onCreate(savedInstanceState)        binding = DataBindingUtil.setContentView(this, R.layout.activity_main);        binding!!.button.setOnClickListener             binding!!.boom.visibility = View.VISIBLE            val startP = PointF()            val endP = PointF()            val controlP = PointF()            val randomPointF = getRandomPointF()            startP.x = 916f            startP.y = 1353f            endP.x = randomPointF.x            endP.y = randomPointF.y            controlP.x = randomPointF.x + getRandom(200, 50)            controlP.y = randomPointF.y - getRandom(200, 50)            val animator = ObjectAnimator.ofObject(binding.boom, "point",                PointFTypeEvaluator(controlP), startP, endP)            animator.duration = 600            animator.addListener(object : Animator.AnimatorListener                 override fun onAnimationStart(animation: Animator)                 override fun onAnimationEnd(animation: Animator)                     val measuredHeight = binding.lottie.measuredHeight                    val measuredWidth = binding.lottie.measuredWidth                    binding.lottie.x = randomPointF.x - measuredWidth / 2                    binding.lottie.y = randomPointF.y - measuredHeight / 2                    binding.lottie.visibility = View.VISIBLE                    binding.boom.visibility = View.GONE                    binding.lottie.playAnimation()                    binding.lottie.addAnimatorListener(object : Animator.AnimatorListener                         override fun onAnimationStart(animation: Animator)                         override fun onAnimationEnd(animation: Animator)                             binding.lottie.visibility = View.GONE                                                override fun onAnimationCancel(animation: Animator)                         override fun onAnimationRepeat(animation: Animator)                     )                                override fun onAnimationCancel(animation: Animator)                 override fun onAnimationRepeat(animation: Animator)             )            animator.start()            复制代码

本文在开源项目:https://github.com/Android-Alvin/Android-LearningNotes 中已收录,里面包含不同方向的自学编程路线、面试题集合/面经、及系列技术文章等,资源持续更新中…

以上是关于实战演练!二阶贝塞尔仿微信扔炸弹动画的主要内容,如果未能解决你的问题,请参考以下文章