Android TV使用贝赛尔曲线制作炫酷的开场动画

目录

前言

效果展示

动画一

动画一效果

贝赛尔曲线

代码示例

动画二

动画二效果

时差动画

面向对象绘制

总结


前言

很多App启动的时候会用到炫酷的开场动画。Android TV端也一样,每一个不同的模块,产品经理都可能设计了不同的开场动画。对于这些复杂的开场动画,最重要的是学会拆分,只要拆分得当,就会变成一个个普通的动画组合而成。今天给大家介绍一个曾经在项目中使用过的炫酷的开场动画。

效果展示

上面的图片中展示了一个曾在项目中使用的开场动画,由于项目还还在线,所以仅仅截图了其中一部分的动画。对于这样一个动画,我们可以选择放一张GIF图片,但是对于每秒60帧的安卓来说,GIF的效果真的很难让人满意,而且GIF貌似不能做透明背景,这样一来要做组合动画就难了。所以我们不得不考虑用原生技术来解决这样一个动画效果。

这个动画可以拆分成三个动画,左边红色气球一直左右摇摆,像是在风中摇曳。右边一团紫色气球上升,像是有人放飞了一簇气球。紫色气球飞完之后有一个横幅逐渐展示。 最后横幅的展示仅仅是个属性动画,今天不多做介绍,今天主要介绍前两个动画。

动画一

动画一效果

首先我们把第一个动画拆分出来,单独展示的话,它是这样的:

这个动画看似还比较简单,我第一印象也是这样,不就是一个气球左右移动吗?使用属性动画或者帧动画都可以完成吧。实际操作中发现,下面的绳子是主要难点,绳子一端固定,一端随气球移动,而且绳子是曲线不是直线,这就排除了用属性动画移动气球的解决方案。帧动画理论上是可以的,只要分出足够多帧就行,但是想要这么如丝般润滑,帧数一定不少,帧数越多图片就越多,app体积就越大。笔者采用的方法是一张图片搞定,上面的气球是一张图片,左右移动,下面的绳子直接画出来。这里就需要用到贝塞尔曲线。

贝赛尔曲线

贝塞尔曲线于1962年,由法国工程师皮埃尔·贝塞尔(Pierre Bézier)所广泛发表,他运用贝塞尔曲线来为汽车的主体进行设计。贝塞尔曲线最初由 Paul de Casteljau 于1959年运用de Casteljau 算法开发,以稳定数值的方法求出贝塞尔曲线。

需用到Path方法:

mPath.moveTo     移动到操作起始点坐标,不会进行绘制,只用于移动移动画笔

mPath.lineTo        从一个点连线到另一个点,用于进行直线绘制

mPath.quadTo(x1, y1, x2, y2)       生成二次贝塞尔曲线,(x1,y1) 为控制点,(x2,y2)为结束点

mPath.cubicTo(x1, y1, x2, y2, x3, y3)       生成三次贝塞尔曲线, (x1,y1) 为控制点,(x2,y2)为控制点,(x3,y3) 为结束点;即与二阶区别多一个控制点

更多关于贝塞尔曲线的知识可以参考这篇文章:Android自定义View:Path之贝塞尔曲线

代码示例

只要把思路理清楚了,代码就比较简单了,下面代码是画气球和曲线的代码:

canvas.drawBitmap(mBitmap, mLeft, 0, mPaint);

mEnd.x = mLeft + DisplayUtil.dip2px(getContext(),13);
mEnd.y = mBitmap.getHeight();

mControl.x = mEnd.x;
mControl.y = mStart.y - DisplayUtil.dip2px(getContext(),5);

mPath.reset();
mPath.moveTo(mStart.x, mStart.y);
mPath.quadTo(mControl.x, mControl.y, mEnd.x, mEnd.y);

canvas.drawPath(mPath, mPaint);

动画二

动画二效果

我们把第二个动画的效果也拆分出来:

其实这个动画相对简单,途中总共有14个气球,我们只要搞清楚每个气球的轨迹,分别在各自的轨迹中绘制出来即可。

时差动画

这里面每个气球不是同时启动的,有的启动早有的启动晚,形成了一个时差效果。所以我们在设计这个动画的时候需要考虑每个气球的启动时间,每个气球可以延迟不同的时间启动。

面向对象绘制

要同时绘制14个气球,每个气球起点、终点、启动时间等都不同。考虑到这些因素,我们必须以面向对象的思路来绘制。如果使用android做过一些小游戏的话,应该不难理解这个思路。比如做一个坦克大战或者雷电游戏,每颗子弹的飞行轨迹也不一样,我们也是把子弹设计成一个类,传入起点、终点等属性,让子弹自己去完成绘制的。

下面是我抽象出来的气球类:

    private class Balloon {
        Bitmap bitmap;

        float endX;

        float endY;

        int delay;

        void onDraw(Canvas canvas, int currTime, int width, int height) {
            if (currTime < delay) {
                return;
            }
            float startX = (width - bitmap.getWidth()) / 2;
            float startY = height - bitmap.getHeight();
            int duration = DURATION;
            currTime = currTime - delay;
            if (currTime > DURATION) {
                currTime = DURATION;
            }
            float speedX = (endX - startX) / duration;
            float speedY = (endY - startY) / duration;

            float currX = startX + speedX * currTime;
            float currY = startY + speedY * currTime;
            canvas.drawBitmap(bitmap, currX, currY, null);
        }
    }

使用的时候初始化不同的气球对象:

        mBalloons = new ArrayList<>();
        Balloon balloon1 = new Balloon();
        balloon1.bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.balloon_1);
        balloon1.delay = 0;
        balloon1.endX = 0;
        balloon1.endY = DisplayUtil.dip2px(getContext(), 45);
        mBalloons.add(balloon1);

        Balloon balloon2 = new Balloon();
        balloon2.bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.balloon_2);
        balloon2.delay = 50;
        balloon2.endX = DisplayUtil.dip2px(getContext(),195);
        balloon2.endY = DisplayUtil.dip2px(getContext(),67);
        mBalloons.add(balloon2);

最终绘制的时候,在view的onDraw方法中调用气球的onDraw方法:

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        for (Balloon balloon : mBalloons) {
            balloon.onDraw(canvas, mCurrTime, getWidth(), getHeight());
        }
    }

总结

工作中面对这样的复杂动画,不要被吓住。再复杂的动画我们拆分成一个个的小动画之后会发现自己其实是可以实现的。

完整代码:GitHub地址,由于像这样的动画往往不具有通用性,所以没办法做成一个库来发布,感兴趣的小伙伴版可以直接看源码,大家相互交流学习。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值