仿微信聊天炸“屎”效果!!诶,就是玩!

Long Long Ago,微信8.0更新了一个“炸屎”的新功能,还蛮有新意。

这两天和朋友聊天触发了这个功能,当时心想,诶!这文章素材不就来了吗?!趁着周末时间,赶紧来实现一下。

我们先来看看整体实现的效果:

boom.gif

本篇文章无技术含量,纯属娱乐,纯属娱乐

思路

经过不断试验,反复查看了微信炸💩的效果,基本上可以将其拆解成几个小部分。

  • 选择炸弹表情,发送后炸弹会以抛物线的形式向对方发送,同时💣会进行翻转;
  • 在💣碰到💩表情的时候,出现爆炸动画;
  • 在动画爆炸的同时,出现大概7个💩表情,随机朝向,做放大效果,最后下移并渐渐消失。

效果已经拆解了,技术方面具体实现思路如下:

  • IM聊天界面,主要以Recyclerview分为发送方和接收方两种type;
  • 以贝塞尔曲线实现炸弹抛物线的效果,这里用二阶贝塞尔曲线即可;
  • 炸弹抛过去的同时,使用动画进行旋转;
  • Lottie实现爆炸效果;
  • 缩放、平移、渐变动画实现7个💩的炸出效果。

具体实现

IM聊天界面

Im聊天界面很简单,一个Recyclerview,分为左右两个type,根据type值,分别加载左侧和右侧的布局。

class ImAdapter  : BaseDelegateMultiAdapter<ImMsg?, BaseViewHolder>(){
    companion object {
        private const val TAG = "ImMsgAdapter"
        private const val TYPE_LEFT = 0
        private const val TYPE_RIGHT = 1
    }

    init {
        setMultiTypeDelegate(object : BaseMultiTypeDelegate<ImMsg?>() {
            override fun getItemType(data: List<ImMsg?>, position: Int): Int {

                if (data[position]?.type == 0) {
                    return TYPE_LEFT
                }
                return TYPE_RIGHT
            }
        })
        Objects.requireNonNull(getMultiTypeDelegate())!!
            .addItemType(TYPE_LEFT, R.layout.item_im_left)
            .addItemType(TYPE_RIGHT, R.layout.item_im_right)
    }

    override fun convert(holder: BaseViewHolder, item: ImMsg?) {

        if (holder.itemViewType == TYPE_LEFT) {
            holder.setText(R.id.tv_chat_left_msg,item?.msg)
        } else {
            holder.setText(R.id.tv_chat_right_msg,item?.msg)
        }

    }
}

这里创建一个实体类ImMsg,用于模拟聊天数据。

data class ImMsg(val type:Int,val msg:String)


//初始化聊天信息,模拟数据
val msgList = mutableListOf<ImMsg>()
msgList.add(ImMsg(0, "Hello!"))
msgList.add(ImMsg(0, "我们来测试一下"))
msgList.add(ImMsg(0, "微信爆炸效果"))
msgList.add(ImMsg(1, "欧了!"))
msgList.add(ImMsg(0, "\uD83D\uDE14"))
msgList.add(ImMsg(0, "\uD83D\uDCA9"))
msgList.add(ImMsg(0, "\uD83D\uDCA9"))
msgList.add(ImMsg(0, "\uD83D\uDCA9"))

val adapter = ImAdapter()
imRv.adapter = adapter
adapter.addData(msgList)

当然,聊天界面还需要加上输入框,以及表情栏,这里就不一一展示。

发送炸弹💣

聊天界面搭建完成,接下来就开始进入正题,从扔炸弹开始。

上面已经分析了,扔炸弹的过程其实是一个类似抛物线的过程,从右侧聊天发送出去开始到碰到左侧💩结束。

掘金-付十一.png

看到扔炸弹的过程,第一反应就是用贝塞尔曲线来解决炸弹路线的问题,炸弹的行进路线比较简单,这里用二阶贝塞尔曲线就能实现。

我们都知道,要实现二阶贝塞尔曲线,至少得知道两个点的数据,一个是控制点,一个是终点的坐标。

从上图就可以基本知道,控制点在开始点和终点中间靠上的位置,这里以屏幕宽和高来做计算,模拟出控制点。

val resources: Resources = this.resources
val dm: DisplayMetrics = resources.displayMetrics
val scWidth = dm.widthPixels
val scHeight = dm.heightPixels
val controlX : Int = scWidth/2 - 400//控制点x
val controlY : Int = scHeight/2 - 400//控制点y

终点同样的道理,以起点为参考值,计算出坐标

val lastX = ivBomb.x-200
val lastY = ivBomb.y-800

有个控制点和终点的坐标,那就开始绘制二阶贝塞尔曲线,实现贝塞尔曲线,Android中已经有现成的API,Path中有个quadTo方法,只要传入控制点和终点的坐标,即生成贝塞尔曲线路径。

path.quadTo(
    -controlX,
    -controlY,
    -lastX,
    -lastY
)

有了path,接下来就是将炸弹按照贝塞尔曲线动起来,这里就需要用到属性动画结合PathMeasure,让炸弹💣按照路径移动。

pathMeasure = PathMeasure(path, false)
valueAnimator = ValueAnimator.ofFloat(0f, pathMeasure.length)
valueAnimator.duration = MILLS + 100
valueAnimator.interpolator = LinearInterpolator()
valueAnimator.addUpdateListener { animation ->
    val animatedValue = animation.animatedValue as Float
    pathMeasure.getPosTan(animatedValue, mCurrentPosition, null)
    ivBomb.translationX = mCurrentPosition[0]
    ivBomb.translationY = mCurrentPosition[1]
    ivBomb.rotation = animatedValue
}

微信的炸弹在抛物线的同时,是有个不断翻转的效果。

那我们实现就只需要监听属性动画的更新listener,在监听移动的过程中,设置rotation,使炸弹不断进行翻转。

我们来看效果:

爆炸效果

接下来该说的就是炸弹的爆炸效果,这里给出的方案是使用Lottie库来加载。微信的爆炸效果还是蛮逼真,找了好久,都没找到相应的素材,这里属实无法模拟。

于是就从https://lottiefiles.com/ 资源库中找到了一个类似爆炸效果的json文件,结合Lottie实现爆炸的效果。

Lottie的使用也很简单,添加依赖

implementation 'com.airbnb.android:lottie:3.4.0'

在layout中添加LottieAnimationView

<com.airbnb.lottie.LottieAnimationView
    android:id="@+id/lottie_layer_view"
    android:layout_width="200dp"
    android:layout_height="200dp"
    app:layout_constraintBottom_toTopOf="@id/cl_ed_send"
    android:layout_marginBottom="140dp"/>

在炸弹执行抛物线结束时,开始播放

mLottieAnimation?.playAnimation()

来看下炸弹结合爆炸的整体效果

💩炸开效果

当炸弹爆炸后,有一个💩被炸开的效果,如下图。

从微信效果上看,存在7个💩的表情,并且每个表情随机倾斜角度,从爆炸动画那里为起点,由小到大弹出,形成由远及近的效果。

实现的方案是自定义View绘制出7个相同的表情图,利用Math.random随机旋转角度,且7个表情在可控范围内随机摆放。

//爆炸刚开始时随机分别7个图,且旋转角度随机
for (i in 0..6) {
    dst?.let {
        it.left = i * 50 + 150
        it.top = mHeight + (Math.random() * 400).toInt() - mHeight / 4
        it.right = it.left + 100
        it.bottom = it.top + 100
    }
    
    val matrix = Matrix()
    matrix.postScale(i.toFloat() + 4, i.toFloat() + 4)
    matrix.postTranslate((-bitmap.width / 2).toFloat(), (-bitmap.height / 2).toFloat())
    matrix.postRotate((Math.random() * 70).toFloat())
    matrix.postTranslate(dst!!.top.toFloat(), dst!!.left.toFloat())
    val bitmap1 =
        Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
    canvas.drawBitmap(bitmap1, null, dst!!, paint)
    matrix.reset()
}

至于由远及近的效果,就需要对自定义的粑粑View进行缩放动画。

val scaleXAni = ObjectAnimator.ofFloat(baView, "scaleX", 0f, 1.8f)
val scaleYAni = ObjectAnimator.ofFloat(baView, "scaleY", 0f, 1.5f)

另外,在💩炸开的最后,💩会向下移动并且同时渐变透明,所以在缩放动画后开始平移动画
translation和渐变动画alpha。

val tranY1Ani = ObjectAnimator.ofFloat(baView, "translationY", 0f, -300f)
val tranYAni = ObjectAnimator.ofFloat(baView, "translationY", -300f, 300f)
val alphaAni = ObjectAnimator.ofFloat(baView, "alpha", 1f, 0f)

最后使用AnimatorSet将动画集合,开始播放。

val animatorSet = AnimatorSet()
animatorSet.play(scaleXAni).with(scaleYAni).with(tranY1Ani).before(tranYAni)
    .before(alphaAni)

看看效果。

408x425_boom2.gif

基本上每个拆解的部分都已经实现,现在开始将IM界面、炸弹抛物线、爆炸效果以及最后的炸开效果合在一起。

最后的最后,实现的效果如下:

boom.gif

在炸弹爆炸的瞬间,用户头像和表情都会有被震到抖动的效果,这里就没实现了,有兴趣的小伙伴可以试试。

好了,本篇文章就到这了。觉得还凑合的,可以给个三连哟!

推荐阅读:

玩会儿Compose,原神主题列表
Compose版来啦!仿自如裸眼3D效果

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

推荐阅读更多精彩内容