仿抖音-视频及直播点赞效果

先上GIF
效果图

需求梳理

抖音APP中,视频的点赞和直播中点赞效果是不同的,先找寻两者的共同点提取接口:

类型图片动画初始旋转角度Y轴偏移量
视频点赞单张红心组合动画随机旋转角度-50
直播点赞多张图片随机组合动画-20

Y轴偏移量是多次调试的,看各位同学需求,都可以调整。

interface ILikeFollowData {

    /**
     * 获取图标列表
     */
    fun getIconList(): List<Any>

    /**
     * 获取图标大小
     */
    fun getIconSize(): Int

    /**
     * 获取旋转角度范围
     */
    fun getRotationRange(): IntRange

    /**
     * 获取Y轴偏移量
     */
    fun getYOffset(): Int

    /**
     * 获取动画集合
     */
    fun getAnimatorSet(view: View): AnimatorSet
}

实现

视频点赞数据类

class VideoLikeFollowData : ILikeFollowData {

    private val iconList = arrayListOf<Any>(R.drawable.img_like_follow)

    override fun getIconList() = iconList

    override fun getIconSize() = 240

    override fun getRotationRange() = -30..30

    override fun getYOffset() = -50

    override fun getAnimatorSet(view: View): AnimatorSet {
        val set = AnimatorSet()

        //放大
        set.playSequentially(
            AnimatorSet().apply {
                playTogether(
                    ObjectAnimator.ofFloat(view, "scaleX", 1f, 1.4f).apply {
                        duration = 50
                    },
                    ObjectAnimator.ofFloat(view, "scaleY", 1f, 1.4f).apply {
                        duration = 50
                    },
                )
            },
            //缩小
            AnimatorSet().apply {
                playTogether(
                    ObjectAnimator.ofFloat(view, "scaleX", 1.4f, 1f).apply {
                        duration = 200
                    },
                    ObjectAnimator.ofFloat(view, "scaleY", 1.4f, 1f).apply {
                        duration = 200
                    },
                )
            },
            ValueAnimator.ofInt(0, 100).apply {
                duration = 200
            },
            //缩放/透明并位移
            AnimatorSet().apply {
                playTogether(
                    ObjectAnimator.ofFloat(view, "scaleX", 1f, 1.8f).apply {
                        duration = 500
                    },
                    ObjectAnimator.ofFloat(view, "scaleY", 1f, 1.8f).apply {
                        duration = 500
                    },
                    ObjectAnimator.ofFloat(view, "alpha", 1f, 0f).apply {
                        duration = 500
                    },
                    ObjectAnimator.ofFloat(
                        view,
                        "translationY",
                        view.y,
                        view.y - 300f
                    ).apply {
                        duration = 500
                    },
                )
            })

        return set
    }
}

直播点赞数据类

class LiveLikeFollowData : ILikeFollowData {

    private val iconList = arrayListOf<Any>(
        R.drawable.img_like_follow_1,
        R.drawable.img_like_follow_2,
        R.drawable.img_like_follow_3,
        R.drawable.img_like_follow_4,
        R.drawable.img_like_follow_5,
        R.drawable.img_like_follow_6,
        R.drawable.img_like_follow_7,
    )

    override fun getIconList() = iconList

    override fun getIconSize() = 100

    override fun getRotationRange() = 0..0

    override fun getYOffset() = -20

    override fun getAnimatorSet(view: View): AnimatorSet {
        val set = AnimatorSet()

        //放大
        set.playSequentially(
            AnimatorSet().apply {
                // 同时播放
                playTogether(
                    ObjectAnimator.ofFloat(view, "scaleX", 1f, 1.1f).apply {
                        duration = 50
                    },
                    ObjectAnimator.ofFloat(view, "scaleY", 1f, 1.1f).apply {
                        duration = 50
                    },
                    ObjectAnimator.ofFloat(view, "rotation", -20f, -6f).apply {
                        duration = 50
                    }
                )
            },
            //缩小
            AnimatorSet().apply {
                playTogether(
                    ObjectAnimator.ofFloat(view, "scaleX", 1.1f, 1f).apply {
                        duration = 200
                    },
                    ObjectAnimator.ofFloat(view, "scaleY", 1.1f, 1f).apply {
                        duration = 200
                    },
                    ObjectAnimator.ofFloat(view, "rotation", -6f, 4f, 0f).apply {
                        duration = 200
                    }
                )
            },
            ValueAnimator.ofInt(0, 100).apply {
                duration = 200
            },
            //缩放/透明并位移
            AnimatorSet().apply {
                playTogether(
                    ObjectAnimator.ofFloat(view, "scaleX", 1f, 1.8f).apply {
                        duration = 500
                    },
                    ObjectAnimator.ofFloat(view, "scaleY", 1f, 1.8f).apply {
                        duration = 500
                    },
                    ObjectAnimator.ofFloat(view, "alpha", 1f, 0f).apply {
                        duration = 500
                    }
                )
            })

        return set
    }
}

上述代码中的图片,各位同学可以让UI自行设计切图,如果需要,可以在末尾代码链接处下载获取。

功能model

object LikeFollowModel {

    //短视频展示数据
    val videoLikeFollowData: ILikeFollowData by lazy {
        VideoLikeFollowData()
    }

    //直播展示数据
    val liveLikeFollowData: ILikeFollowData by lazy {
        LiveLikeFollowData()
    }
    
    /**
     * 展示点赞效果
     * @param x: 点击的x坐标
     * @param y: 点击的y坐标
     * @param viewGroup: 父布局
     * @param data: 展示数据
     */
    fun show(x: Int, y: Int, viewGroup: ViewGroup, data: ILikeFollowData = videoLikeFollowData) {
        //添加爱心
        val likeView = AppCompatImageView(viewGroup.context)
        //加载图片,封装的Glide方法,同学可自行处理
        loadImage(likeView, data.getIconList().random())

        val size = data.getIconSize()
        val layoutParams = ViewGroup.LayoutParams(size, size)
        likeView.layoutParams = layoutParams
        //设置爱心位置
        likeView.x = (x - (size / 2)).toFloat()
        likeView.y = (y - size + data.getYOffset()).toFloat()
        //设置随机旋转角度
        likeView.rotation = (data.getRotationRange().random()).toFloat()
        viewGroup.addView(likeView)

         //获取动画集合
        val animatorSet = data.getAnimatorSet(likeView)
        //添加动画结束监听
        animatorSet.addListener(onEnd = {
            tryCatch({
                //清理动画
                likeView.clearAnimation()
                //隐藏图片
                likeView.visibility = View.GONE
                //移除图片,延迟500毫秒移除,防止动画还没结束就移除
                HandlerUtils.postRunnable({
                    viewGroup.removeView(likeView)
                }, 500)
            })
        })
        animatorSet.start()
    }
}

以上四个类就是所有的功能代码。

实现思路还是蛮简单的:
在这里插入图片描述

获取点击事件坐标

避免同学获取点击坐标有疑问,这里再补充上相关代码:

    /**
     * 设置点击监听
     * 返回坐标
     */
    @SuppressLint("ClickableViewAccessibility")
    private fun setViewOnClickListener(view: View, clickCallBack: (x: Int, y: Int) -> Unit) {
        var x = 0f
        var y = 0f
        view.setOnTouchListener { _, event ->
            when (event.action) {
                MotionEvent.ACTION_DOWN -> {
                    x = event.x
                    y = event.y
                }
                MotionEvent.ACTION_UP -> {
                    if (x == event.x && y == event.y) {
                        clickCallBack(event.x.toInt(), event.y.toInt())
                    }
                }
            }
            true
        }
    }

代码仓库

仓库地址

  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
要实现仿抖音视频滑动效果,你可以使用uni-app框架中的swiper组件结合相关的动画效果来实现。下面是一种可能的实现方式: 1. 在你的uni-app项目中,使用swiper组件创建一个滑动容器,设置为横向滑动。 ```html <swiper class="swiper" :autoplay="false" :indicator-dots="false" :circular="true"> <swiper-item v-for="(item, index) in videoList" :key="index"> <video :src="item.url" autoplay muted loop></video> </swiper-item> </swiper> ``` 2. 使用css样式来设置容器的布局和样式。 ```css .swiper { width: 100%; height: 100%; overflow: hidden; } .swiper-item { width: 100%; height: 100%; } video { width: 100%; height: 100%; object-fit: cover; } ``` 3. 使用JavaScript或者Vue的生命周期钩子函数来监听滑动事件,并根据滑动的距离和方向来实现动画效果。 ```javascript export default { data() { return { videoList: [...], // 视频列表数据 startX: 0, // 触摸起始点的X坐标 startY: 0, // 触摸起始点的Y坐标 moveX: 0, // 触摸移动中的X坐标 moveY: 0, // 触摸移动中的Y坐标 direction: '', // 滑动方向 currentIndex: 0 // 当前显示的视频索引 }; }, methods: { onTouchStart(e) { this.startX = e.changedTouches[0].pageX; this.startY = e.changedTouches[0].pageY; }, onTouchMove(e) { this.moveX = e.changedTouches[0].pageX; this.moveY = e.changedTouches[0].pageY; const offsetX = this.moveX - this.startX; const offsetY = this.moveY - this.startY; if (Math.abs(offsetY) < Math.abs(offsetX)) { // 水平滑动 if (offsetX > 0) { this.direction = 'right'; } else { this.direction = 'left'; } } else { // 垂直滑动 if (offsetY > 0) { this.direction = 'down'; } else { this.direction = 'up'; } } }, onTouchEnd() { if (this.direction === 'left') { // 向左滑动,切换到下一个视频 this.currentIndex++; if (this.currentIndex >= this.videoList.length) { this.currentIndex = 0; } } else if (this.direction === 'right') { // 向右滑动,切换到上一个视频 this.currentIndex--; if (this.currentIndex < 0) { this.currentIndex = this.videoList.length - 1; } } // 根据currentIndex更新swiper组件的activeIndex属性,实现视图切换 this.$refs.swiper.swiperRef.setActiveItem(this.currentIndex); } } }; ``` 上述代码中,通过监听触摸事件,根据滑动方向切换到对应的视频,并将视图更新到当前的视频。 这是一种简单的实现方式,你可以根据自己的需求进行扩展和优化。希望对你有帮助!

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值